diff --git a/Cargo.lock b/Cargo.lock
index 9d31619f..7e98c6b2 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -2116,9 +2116,9 @@ dependencies = [
[[package]]
name = "rlp"
-version = "0.5.0"
+version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e54369147e3e7796c9b885c7304db87ca3d09a0a98f72843d532868675bbfba8"
+checksum = "999508abb0ae792aabed2460c45b89106d97fe4adac593bdaef433c2605847b5"
dependencies = [
"bytes",
"rustc-hex",
diff --git a/ethers-core/src/types/mod.rs b/ethers-core/src/types/mod.rs
index 597b7cb6..2cf9d5fc 100644
--- a/ethers-core/src/types/mod.rs
+++ b/ethers-core/src/types/mod.rs
@@ -7,9 +7,13 @@ pub use ethabi::ethereum_types::H256 as TxHash;
pub use ethabi::ethereum_types::{Address, Bloom, H160, H256, U128, U256, U64};
-mod transaction;
-pub use transaction::request::TransactionRequest;
-pub use transaction::response::{Transaction, TransactionReceipt};
+pub mod transaction;
+pub use transaction::{
+ eip1559::Eip1559TransactionRequest,
+ eip2930::Eip2930TransactionRequest,
+ request::TransactionRequest,
+ response::{Transaction, TransactionReceipt},
+};
mod address_or_bytes;
pub use address_or_bytes::AddressOrBytes;
diff --git a/ethers-core/src/types/transaction/eip1559.rs b/ethers-core/src/types/transaction/eip1559.rs
new file mode 100644
index 00000000..29d39da2
--- /dev/null
+++ b/ethers-core/src/types/transaction/eip1559.rs
@@ -0,0 +1,172 @@
+use super::{eip2930::AccessList, rlp_opt};
+use crate::{
+ types::{Address, Bytes, NameOrAddress, Signature, H256, U256, U64},
+ utils::keccak256,
+};
+use rlp::RlpStream;
+
+/// EIP-1559 transactions have 9 fields
+const NUM_TX_FIELDS: usize = 9;
+
+use serde::{Deserialize, Serialize};
+/// Parameters for sending a transaction
+#[derive(Clone, Default, Serialize, Deserialize, PartialEq, Eq, Debug)]
+pub struct Eip1559TransactionRequest {
+ /// Sender address or ENS name
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub from: Option
,
+
+ /// Recipient address (None for contract creation)
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub to: Option,
+
+ /// Supplied gas (None for sensible default)
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub gas: Option,
+
+ /// Transfered value (None for no transfer)
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub value: Option,
+
+ /// The compiled code of a contract OR the first 4 bytes of the hash of the
+ /// invoked method signature and encoded parameters. For details see Ethereum Contract ABI
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub data: Option,
+
+ /// Transaction nonce (None for next available nonce)
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub nonce: Option,
+
+ #[serde(
+ rename = "accessList",
+ default,
+ skip_serializing_if = "Option::is_none"
+ )]
+ pub access_list: Option,
+
+ #[serde(
+ rename = "maxPriorityFeePerGas",
+ default,
+ skip_serializing_if = "Option::is_none"
+ )]
+ /// Represents the maximum tx fee that will go to the miner as part of the user's
+ /// fee payment. It serves 3 purposes:
+ /// 1. Compensates miners for the uncle/ommer risk + fixed costs of including transaction in a block;
+ /// 2. Allows users with high opportunity costs to pay a premium to miners;
+ /// 3. In times where demand exceeds the available block space (i.e. 100% full, 30mm gas),
+ /// this component allows first price auctions (i.e. the pre-1559 fee model) to happen on the priority fee.
+ ///
+ /// More context [here](https://hackmd.io/@q8X_WM2nTfu6nuvAzqXiTQ/1559-wallets)
+ pub max_priority_fee_per_gas: Option,
+
+ #[serde(
+ rename = "maxFeePerGas",
+ default,
+ skip_serializing_if = "Option::is_none"
+ )]
+ /// Represents the maximum amount that a user is willing to pay for their tx (inclusive of baseFeePerGas and maxPriorityFeePerGas).
+ /// The difference between maxFeePerGas and baseFeePerGas + maxPriorityFeePerGas is “refunded” to the user.
+ pub max_fee_per_gas: Option,
+}
+
+impl Eip1559TransactionRequest {
+ /// Creates an empty transaction request with all fields left empty
+ pub fn new() -> Self {
+ Self::default()
+ }
+
+ // Builder pattern helpers
+
+ /// Sets the `from` field in the transaction to the provided value
+ pub fn from>(mut self, from: T) -> Self {
+ self.from = Some(from.into());
+ self
+ }
+
+ /// Sets the `to` field in the transaction to the provided value
+ pub fn to>(mut self, to: T) -> Self {
+ self.to = Some(to.into());
+ self
+ }
+
+ /// Sets the `gas` field in the transaction to the provided value
+ pub fn gas>(mut self, gas: T) -> Self {
+ self.gas = Some(gas.into());
+ self
+ }
+
+ /// Sets the `max_priority_fee_per_gas` field in the transaction to the provided value
+ pub fn max_priority_fee_per_gas>(mut self, max_priority_fee_per_gas: T) -> Self {
+ self.max_priority_fee_per_gas = Some(max_priority_fee_per_gas.into());
+ self
+ }
+
+ /// Sets the `max_fee_per_gas` field in the transaction to the provided value
+ pub fn max_fee_per_gas>(mut self, max_fee_per_gas: T) -> Self {
+ self.max_fee_per_gas = Some(max_fee_per_gas.into());
+ self
+ }
+
+ /// Sets the `value` field in the transaction to the provided value
+ pub fn value>(mut self, value: T) -> Self {
+ self.value = Some(value.into());
+ self
+ }
+
+ /// Sets the `data` field in the transaction to the provided value
+ pub fn data>(mut self, data: T) -> Self {
+ self.data = Some(data.into());
+ self
+ }
+
+ /// Sets the `access_list` field in the transaction to the provided value
+ pub fn access_list>(mut self, access_list: T) -> Self {
+ self.access_list = Some(access_list.into());
+ self
+ }
+
+ /// Sets the `nonce` field in the transaction to the provided value
+ pub fn nonce>(mut self, nonce: T) -> Self {
+ self.nonce = Some(nonce.into());
+ self
+ }
+
+ /// Hashes the transaction's data with the provided chain id
+ pub fn sighash>(&self, chain_id: T) -> H256 {
+ keccak256(self.rlp(chain_id).as_ref()).into()
+ }
+
+ /// Gets the unsigned transaction's RLP encoding
+ pub fn rlp>(&self, chain_id: T) -> Bytes {
+ let mut rlp = RlpStream::new();
+ rlp.begin_list(NUM_TX_FIELDS);
+ self.rlp_base(chain_id, &mut rlp);
+ rlp.out().freeze().into()
+ }
+
+ /// Produces the RLP encoding of the transaction with the provided signature
+ pub fn rlp_signed>(&self, chain_id: T, signature: &Signature) -> Bytes {
+ let mut rlp = RlpStream::new();
+ rlp.begin_unbounded_list();
+ self.rlp_base(chain_id, &mut rlp);
+
+ // append the signature
+ rlp.append(&signature.v);
+ rlp.append(&signature.r);
+ rlp.append(&signature.s);
+ rlp.finalize_unbounded_list();
+ rlp.out().freeze().into()
+ }
+
+ pub(crate) fn rlp_base>(&self, chain_id: T, rlp: &mut RlpStream) {
+ rlp.append(&chain_id.into());
+ rlp_opt(rlp, &self.nonce);
+ rlp_opt(rlp, &self.max_priority_fee_per_gas);
+ rlp_opt(rlp, &self.max_fee_per_gas);
+ rlp_opt(rlp, &self.gas);
+ rlp_opt(rlp, &self.to.as_ref());
+ rlp_opt(rlp, &self.value);
+ rlp_opt(rlp, &self.data.as_ref().map(|d| d.as_ref()));
+ rlp_opt(rlp, &self.access_list);
+ }
+}
diff --git a/ethers-core/src/types/transaction/eip2718.rs b/ethers-core/src/types/transaction/eip2718.rs
new file mode 100644
index 00000000..fd23e178
--- /dev/null
+++ b/ethers-core/src/types/transaction/eip2718.rs
@@ -0,0 +1,237 @@
+use super::{eip1559::Eip1559TransactionRequest, eip2930::Eip2930TransactionRequest};
+use crate::{
+ types::{Address, Bytes, NameOrAddress, Signature, TransactionRequest, H256, U256, U64},
+ utils::keccak256,
+};
+use serde::{Deserialize, Serialize};
+
+#[derive(Serialize, Deserialize, Clone, PartialEq, Debug)]
+#[serde(tag = "type")]
+pub enum TypedTransaction {
+ // 0x00
+ #[serde(rename = "0x00")]
+ Legacy(TransactionRequest),
+ // 0x01
+ #[serde(rename = "0x01")]
+ Eip2930(Eip2930TransactionRequest),
+ // 0x02
+ #[serde(rename = "0x02")]
+ Eip1559(Eip1559TransactionRequest),
+}
+
+impl TypedTransaction {
+ pub fn from(&self) -> Option<&Address> {
+ use TypedTransaction::*;
+ match self {
+ Legacy(inner) => inner.from.as_ref(),
+ Eip2930(inner) => inner.tx.from.as_ref(),
+ Eip1559(inner) => inner.from.as_ref(),
+ }
+ }
+
+ pub fn set_from(&mut self, from: Address) {
+ use TypedTransaction::*;
+ match self {
+ Legacy(inner) => inner.from = Some(from),
+ Eip2930(inner) => inner.tx.from = Some(from),
+ Eip1559(inner) => inner.from = Some(from),
+ };
+ }
+
+ pub fn to(&self) -> Option<&NameOrAddress> {
+ use TypedTransaction::*;
+ match self {
+ Legacy(inner) => inner.to.as_ref(),
+ Eip2930(inner) => inner.tx.to.as_ref(),
+ Eip1559(inner) => inner.to.as_ref(),
+ }
+ }
+
+ pub fn set_to>(&mut self, to: T) {
+ let to = to.into();
+ use TypedTransaction::*;
+ match self {
+ Legacy(inner) => inner.to = Some(to),
+ Eip2930(inner) => inner.tx.to = Some(to),
+ Eip1559(inner) => inner.to = Some(to),
+ };
+ }
+
+ pub fn nonce(&self) -> Option<&U256> {
+ use TypedTransaction::*;
+ match self {
+ Legacy(inner) => inner.nonce.as_ref(),
+ Eip2930(inner) => inner.tx.nonce.as_ref(),
+ Eip1559(inner) => inner.nonce.as_ref(),
+ }
+ }
+
+ pub fn set_nonce>(&mut self, nonce: T) {
+ let nonce = nonce.into();
+ use TypedTransaction::*;
+ match self {
+ Legacy(inner) => inner.nonce = Some(nonce),
+ Eip2930(inner) => inner.tx.nonce = Some(nonce),
+ Eip1559(inner) => inner.nonce = Some(nonce),
+ };
+ }
+
+ pub fn value(&self) -> Option<&U256> {
+ use TypedTransaction::*;
+ match self {
+ Legacy(inner) => inner.value.as_ref(),
+ Eip2930(inner) => inner.tx.value.as_ref(),
+ Eip1559(inner) => inner.value.as_ref(),
+ }
+ }
+
+ pub fn set_value>(&mut self, value: T) {
+ let value = value.into();
+ use TypedTransaction::*;
+ match self {
+ Legacy(inner) => inner.value = Some(value),
+ Eip2930(inner) => inner.tx.value = Some(value),
+ Eip1559(inner) => inner.value = Some(value),
+ };
+ }
+
+ pub fn gas(&self) -> Option<&U256> {
+ use TypedTransaction::*;
+ match self {
+ Legacy(inner) => inner.gas.as_ref(),
+ Eip2930(inner) => inner.tx.gas.as_ref(),
+ Eip1559(inner) => inner.gas.as_ref(),
+ }
+ }
+
+ pub fn set_gas>(&mut self, gas: T) {
+ let gas = gas.into();
+ use TypedTransaction::*;
+ match self {
+ Legacy(inner) => inner.gas = Some(gas),
+ Eip2930(inner) => inner.tx.gas = Some(gas),
+ Eip1559(inner) => inner.gas = Some(gas),
+ };
+ }
+
+ pub fn set_gas_price>(&mut self, gas_price: T) {
+ let gas_price = gas_price.into();
+ use TypedTransaction::*;
+ match self {
+ Legacy(inner) => inner.gas_price = Some(gas_price),
+ Eip2930(inner) => inner.tx.gas_price = Some(gas_price),
+ Eip1559(inner) => {
+ inner.max_fee_per_gas = Some(gas_price);
+ inner.max_priority_fee_per_gas = Some(gas_price);
+ }
+ };
+ }
+
+ pub fn data(&self) -> Option<&Bytes> {
+ use TypedTransaction::*;
+ match self {
+ Legacy(inner) => inner.data.as_ref(),
+ Eip2930(inner) => inner.tx.data.as_ref(),
+ Eip1559(inner) => inner.data.as_ref(),
+ }
+ }
+
+ pub fn set_data(&mut self, data: Bytes) {
+ use TypedTransaction::*;
+ match self {
+ Legacy(inner) => inner.data = Some(data),
+ Eip2930(inner) => inner.tx.data = Some(data),
+ Eip1559(inner) => inner.data = Some(data),
+ };
+ }
+
+ pub fn rlp_signed>(&self, chain_id: T, signature: &Signature) -> Bytes {
+ use TypedTransaction::*;
+ let mut encoded = vec![];
+ match self {
+ Legacy(inner) => {
+ encoded.extend_from_slice(&[0x0]);
+ encoded.extend_from_slice(inner.rlp_signed(signature).as_ref());
+ }
+ Eip2930(inner) => {
+ encoded.extend_from_slice(&[0x1]);
+ encoded.extend_from_slice(inner.rlp_signed(chain_id, signature).as_ref());
+ }
+ Eip1559(inner) => {
+ encoded.extend_from_slice(&[0x2]);
+ encoded.extend_from_slice(inner.rlp_signed(chain_id, signature).as_ref());
+ }
+ };
+
+ rlp::encode(&encoded).freeze().into()
+ }
+
+ pub fn rlp>(&self, chain_id: T) -> Bytes {
+ let chain_id = chain_id.into();
+ let mut encoded = vec![];
+ use TypedTransaction::*;
+ match self {
+ Legacy(inner) => {
+ encoded.extend_from_slice(&[0x0]);
+ encoded.extend_from_slice(inner.rlp(chain_id).as_ref());
+ }
+ Eip2930(inner) => {
+ encoded.extend_from_slice(&[0x1]);
+ encoded.extend_from_slice(inner.rlp(chain_id).as_ref());
+ }
+ Eip1559(inner) => {
+ encoded.extend_from_slice(&[0x2]);
+ encoded.extend_from_slice(inner.rlp(chain_id).as_ref());
+ }
+ };
+
+ encoded.into()
+ }
+
+ /// Hashes the transaction's data with the provided chain id
+ /// Does not double-RLP encode
+ pub fn sighash>(&self, chain_id: T) -> H256 {
+ let encoded = self.rlp(chain_id);
+ keccak256(encoded).into()
+ }
+}
+
+impl From for TypedTransaction {
+ fn from(src: TransactionRequest) -> TypedTransaction {
+ TypedTransaction::Legacy(src)
+ }
+}
+
+impl From for TypedTransaction {
+ fn from(src: Eip2930TransactionRequest) -> TypedTransaction {
+ TypedTransaction::Eip2930(src)
+ }
+}
+
+impl From for TypedTransaction {
+ fn from(src: Eip1559TransactionRequest) -> TypedTransaction {
+ TypedTransaction::Eip1559(src)
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use crate::types::{Address, U256};
+
+ #[test]
+ fn serde_legacy_tx() {
+ let tx = TransactionRequest::new()
+ .to(Address::zero())
+ .value(U256::from(100));
+ let tx: TypedTransaction = tx.into();
+ let serialized = serde_json::to_string(&tx).unwrap();
+
+ // deserializes to either the envelope type or the inner type
+ let de: TypedTransaction = serde_json::from_str(&serialized).unwrap();
+ assert_eq!(tx, de);
+
+ let de: TransactionRequest = serde_json::from_str(&serialized).unwrap();
+ assert_eq!(tx, TypedTransaction::Legacy(de));
+ }
+}
diff --git a/ethers-core/src/types/transaction/eip2930.rs b/ethers-core/src/types/transaction/eip2930.rs
index ae0c7419..b8577d69 100644
--- a/ethers-core/src/types/transaction/eip2930.rs
+++ b/ethers-core/src/types/transaction/eip2930.rs
@@ -1,10 +1,16 @@
-use crate::types::{Address, H256};
+use super::request::TransactionRequest;
+use crate::types::{Address, Bytes, Signature, H256, U64};
-use rlp_derive::RlpEncodable;
+use rlp::RlpStream;
+use rlp_derive::{RlpEncodable, RlpEncodableWrapper};
use serde::{Deserialize, Serialize};
+const NUM_EIP2930_FIELDS: usize = 8;
+
/// Access list
-#[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize, RlpEncodable)]
+// NB: Need to use `RlpEncodableWrapper` else we get an extra [] in the output
+// https://github.com/gakonst/ethers-rs/pull/353#discussion_r680683869
+#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize, RlpEncodableWrapper)]
pub struct AccessList(pub Vec);
impl From> for AccessList {
@@ -13,8 +19,19 @@ impl From> for AccessList {
}
}
+impl TransactionRequest {
+ /// Sets the `access_list` field in the transaction (converts the [`TransactionRequest`] to
+ /// an [`Eip2930TransactionRequest`])
+ pub fn with_access_list>(
+ self,
+ access_list: T,
+ ) -> Eip2930TransactionRequest {
+ Eip2930TransactionRequest::new(self, access_list.into())
+ }
+}
+
/// Access list item
-#[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize, RlpEncodable)]
+#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize, RlpEncodable)]
#[serde(rename_all = "camelCase")]
pub struct AccessListItem {
/// Accessed address
@@ -22,3 +39,103 @@ pub struct AccessListItem {
/// Accessed storage keys
pub storage_keys: Vec,
}
+
+/// An EIP-2930 transaction is a legacy transaction including an [`AccessList`].
+#[derive(Clone, Serialize, Deserialize, PartialEq, Debug)]
+pub struct Eip2930TransactionRequest {
+ #[serde(flatten)]
+ pub tx: TransactionRequest,
+ pub access_list: AccessList,
+}
+
+impl Eip2930TransactionRequest {
+ pub fn new(tx: TransactionRequest, access_list: AccessList) -> Self {
+ Self { tx, access_list }
+ }
+
+ pub fn rlp>(&self, chain_id: T) -> Bytes {
+ let mut rlp = RlpStream::new();
+ rlp.begin_list(NUM_EIP2930_FIELDS);
+ rlp.append(&chain_id.into());
+ self.tx.rlp_base(&mut rlp);
+ // append the access list in addition to the base rlp encoding
+ rlp.append(&self.access_list);
+
+ rlp.out().freeze().into()
+ }
+
+ /// Produces the RLP encoding of the transaction with the provided signature
+ pub fn rlp_signed>(&self, chain_id: T, signature: &Signature) -> Bytes {
+ let mut rlp = RlpStream::new();
+ rlp.begin_list(NUM_EIP2930_FIELDS + 3);
+
+ rlp.append(&chain_id.into());
+ self.tx.rlp_base(&mut rlp);
+ // append the access list in addition to the base rlp encoding
+ rlp.append(&self.access_list);
+
+ // append the signature
+ rlp.append(&signature.v);
+ rlp.append(&signature.r);
+ rlp.append(&signature.s);
+ rlp.out().freeze().into()
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use crate::types::{transaction::eip2718::TypedTransaction, U256};
+
+ #[test]
+ // https://github.com/ethereum/go-ethereum/blob/c503f98f6d5e80e079c1d8a3601d188af2a899da/core/types/transaction_test.go#L59-L67
+ fn rlp() {
+ let tx: TypedTransaction = TransactionRequest::new()
+ .nonce(3)
+ .gas_price(1)
+ .gas(25000)
+ .to("b94f5374fce5edbc8e2a8697c15331677e6ebf0b"
+ .parse::()
+ .unwrap())
+ .value(10)
+ .data(vec![0x55, 0x44])
+ .with_access_list(vec![])
+ .into();
+
+ let hash = tx.sighash(1);
+ let sig: Signature = "c9519f4f2b30335884581971573fadf60c6204f59a911df35ee8a540456b266032f1e8e2c5dd761f9e4f88f41c8310aeaba26a8bfcdacfedfa12ec3862d3752101".parse().unwrap();
+ let enc = tx.rlp_signed(1, &sig);
+
+ assert_eq!(
+ hash,
+ "49b486f0ec0a60dfbbca2d30cb07c9e8ffb2a2ff41f29a1ab6737475f6ff69f3"
+ .parse()
+ .unwrap()
+ );
+
+ let expected = "b86601f8630103018261a894b94f5374fce5edbc8e2a8697c15331677e6ebf0b0a825544c001a0c9519f4f2b30335884581971573fadf60c6204f59a911df35ee8a540456b2660a032f1e8e2c5dd761f9e4f88f41c8310aeaba26a8bfcdacfedfa12ec3862d37521";
+ assert_eq!(hex::encode(enc.to_vec()), expected);
+ }
+
+ #[test]
+ fn serde_eip2930_tx() {
+ let access_list = vec![AccessListItem {
+ address: Address::zero(),
+ storage_keys: vec![H256::zero()],
+ }];
+ let tx = TransactionRequest::new()
+ .to(Address::zero())
+ .value(U256::from(100))
+ .with_access_list(access_list);
+ let tx: TypedTransaction = tx.into();
+ let serialized = serde_json::to_string(&tx).unwrap();
+ dbg!(&serialized);
+
+ // deserializes to either the envelope type or the inner type
+ let de: TypedTransaction = serde_json::from_str(&serialized).unwrap();
+ assert_eq!(tx, de);
+
+ let de: Eip2930TransactionRequest = serde_json::from_str(&serialized).unwrap();
+ assert_eq!(tx, TypedTransaction::Eip2930(de));
+ }
+}
diff --git a/ethers-core/src/types/transaction/mod.rs b/ethers-core/src/types/transaction/mod.rs
index 22f5e8c8..5e95d089 100644
--- a/ethers-core/src/types/transaction/mod.rs
+++ b/ethers-core/src/types/transaction/mod.rs
@@ -1,6 +1,8 @@
pub mod request;
pub mod response;
+pub mod eip1559;
+pub mod eip2718;
pub mod eip2930;
pub(crate) const BASE_NUM_TX_FIELDS: usize = 9;
@@ -12,8 +14,8 @@ pub(crate) const NUM_TX_FIELDS: usize = BASE_NUM_TX_FIELDS;
#[cfg(feature = "celo")]
pub(crate) const NUM_TX_FIELDS: usize = BASE_NUM_TX_FIELDS + 3;
-pub(super) fn rlp_opt(rlp: &mut rlp::RlpStream, opt: Option) {
- if let Some(ref inner) = opt {
+pub(super) fn rlp_opt(rlp: &mut rlp::RlpStream, opt: &Option) {
+ if let Some(inner) = opt {
rlp.append(inner);
} else {
rlp.append(&"");
diff --git a/ethers-core/src/types/transaction/request.rs b/ethers-core/src/types/transaction/request.rs
index 7dd1e1e2..e71f5fc9 100644
--- a/ethers-core/src/types/transaction/request.rs
+++ b/ethers-core/src/types/transaction/request.rs
@@ -153,16 +153,16 @@ impl TransactionRequest {
}
pub(crate) fn rlp_base(&self, rlp: &mut RlpStream) {
- rlp_opt(rlp, self.nonce);
- rlp_opt(rlp, self.gas_price);
- rlp_opt(rlp, self.gas);
+ rlp_opt(rlp, &self.nonce);
+ rlp_opt(rlp, &self.gas_price);
+ rlp_opt(rlp, &self.gas);
#[cfg(feature = "celo")]
self.inject_celo_metadata(rlp);
- rlp_opt(rlp, self.to.as_ref());
- rlp_opt(rlp, self.value);
- rlp_opt(rlp, self.data.as_ref().map(|d| d.as_ref()));
+ rlp_opt(rlp, &self.to.as_ref());
+ rlp_opt(rlp, &self.value);
+ rlp_opt(rlp, &self.data.as_ref().map(|d| d.as_ref()));
}
}
@@ -171,9 +171,9 @@ impl TransactionRequest {
impl TransactionRequest {
// modifies the RLP stream with the Celo-specific information
fn inject_celo_metadata(&self, rlp: &mut RlpStream) {
- rlp_opt(rlp, self.fee_currency);
- rlp_opt(rlp, self.gateway_fee_recipient);
- rlp_opt(rlp, self.gateway_fee);
+ rlp_opt(rlp, &self.fee_currency);
+ rlp_opt(rlp, &self.gateway_fee_recipient);
+ rlp_opt(rlp, &self.gateway_fee);
}
/// Sets the `fee_currency` field in the transaction to the provided value
diff --git a/ethers-core/src/types/transaction/response.rs b/ethers-core/src/types/transaction/response.rs
index 85222d48..9c427acd 100644
--- a/ethers-core/src/types/transaction/response.rs
+++ b/ethers-core/src/types/transaction/response.rs
@@ -87,7 +87,8 @@ pub struct Transaction {
pub gateway_fee: Option,
// EIP2718
- /// Transaction type, Some(1) for AccessList transaction, None for Legacy
+ /// Transaction type, Some(2) for EIP-1559 transaction,
+ /// Some(1) for AccessList transaction, None for Legacy
#[serde(rename = "type", default, skip_serializing_if = "Option::is_none")]
pub transaction_type: Option,
@@ -130,9 +131,9 @@ impl Transaction {
// of this code duplication?
#[cfg(feature = "celo")]
fn inject_celo_metadata(&self, rlp: &mut RlpStream) {
- rlp_opt(rlp, self.fee_currency);
- rlp_opt(rlp, self.gateway_fee_recipient);
- rlp_opt(rlp, self.gateway_fee);
+ rlp_opt(rlp, &self.fee_currency);
+ rlp_opt(rlp, &self.gateway_fee_recipient);
+ rlp_opt(rlp, &self.gateway_fee);
}
pub fn hash(&self) -> H256 {
@@ -149,7 +150,7 @@ impl Transaction {
#[cfg(feature = "celo")]
self.inject_celo_metadata(&mut rlp);
- rlp_opt(&mut rlp, self.to);
+ rlp_opt(&mut rlp, &self.to);
rlp.append(&self.value);
rlp.append(&self.input.as_ref());
rlp.append(&self.v);
diff --git a/ethers-middleware/tests/gas_oracle.rs b/ethers-middleware/tests/gas_oracle.rs
index 8366abfd..ae79f461 100644
--- a/ethers-middleware/tests/gas_oracle.rs
+++ b/ethers-middleware/tests/gas_oracle.rs
@@ -15,7 +15,7 @@ async fn using_gas_oracle() {
let provider = Provider::::try_from(ganache.endpoint()).unwrap();
// assign a gas oracle to use
- let gas_oracle = Etherchain::new().category(GasCategory::Fastest);
+ let gas_oracle = GasNow::new().category(GasCategory::Fastest);
let expected_gas_price = gas_oracle.fetch().await.unwrap();
let provider = GasOracleMiddleware::new(provider, gas_oracle);
@@ -32,6 +32,7 @@ async fn using_gas_oracle() {
}
#[tokio::test]
+#[ignore]
async fn eth_gas_station() {
// initialize and fetch gas estimates from EthGasStation
let eth_gas_station_oracle = EthGasStation::new(None);
@@ -58,6 +59,7 @@ async fn etherscan() {
}
#[tokio::test]
+#[ignore]
async fn etherchain() {
// initialize and fetch gas estimates from Etherchain
let etherchain_oracle = Etherchain::new().category(GasCategory::Fast);