Skip to content
This repository has been archived by the owner on Oct 19, 2024. It is now read-only.

feat: added Optimism deposited transaction support #2390

Merged
merged 22 commits into from
May 26, 2023
Merged
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
21 changes: 21 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,24 @@ You can get one [here](https://polygonscan.io/apis).
There is abigen support for Avalanche and the Fuji test network. It is recommended that you set the `SNOWTRACE_API_KEY` environment variable.
You can get one [here](https://snowtrace.io/apis).

### Optimism support

Optimism is supported via the `optimism` feature flag:

```toml
[dependencies]
ethers = { version = "2.0", features = ["optimism"] }
```

Optimism has a new transaction type: [Deposited Transactions](https://github.com/ethereum-optimism/optimism/blob/develop/specs/deposits.md#the-deposited-transaction-type)
with type ID `0x7E`, which requires 3 new fields:

- `sourceHash`: The hash which uniquely identifies the origin of the deposit
- `mint`: The ETH value to mint on L2.
- `isSystemTx`: True if the tx does not interact with the L2 block gas pool

**Note:** the `optimism` and `celo` features are mutually exclusive.

### Celo Support

[Celo](https://celo.org) support is turned on via the feature-flag `celo`:
Expand All @@ -76,6 +94,8 @@ Celo's transactions differ from Ethereum transactions by including 3 new fields:
The feature flag enables these additional fields in the transaction request builders and
in the transactions which are fetched over JSON-RPC.

**Note:** the `optimism` and `celo` features are mutually exclusive.

## Features

- [x] Ethereum JSON-RPC Client
Expand All @@ -87,6 +107,7 @@ in the transactions which are fetched over JSON-RPC.
- [x] Celo support
- [x] Polygon support
- [x] Avalanche support
- [x] Optimism support
- [x] Websockets / `eth_subscribe`
- [x] Hardware Wallet Support
- [x] Parity APIs (`tracing`, `parity_blockWithReceipts`)
Expand Down
1 change: 1 addition & 0 deletions ethers-core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ rand.workspace = true
celo = ["legacy"] # celo support extends the transaction format with extra fields
legacy = []
macros = ["syn", "cargo_metadata", "once_cell"]
optimism = []

# Deprecated
eip712 = []
89 changes: 82 additions & 7 deletions ethers-core/src/types/transaction/response.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,22 @@ pub struct Transaction {
/// ECDSA signature s
pub s: U256,

///////////////// Optimism-specific transaction fields //////////////
/// The source-hash that uniquely identifies the origin of the deposit
#[cfg(feature = "optimism")]
#[serde(default, skip_serializing_if = "Option::is_none", rename = "sourceHash")]
pub source_hash: Option<H256>,

/// The ETH value to mint on L2
#[cfg(feature = "optimism")]
#[serde(default, skip_serializing_if = "Option::is_none")]
pub mint: Option<U256>,

/// True if the transaction does not interact with the L2 block gas pool
#[cfg(feature = "optimism")]
#[serde(default, skip_serializing_if = "Option::is_none", rename = "isSystemTx")]
pub is_system_tx: Option<bool>,

///////////////// Celo-specific transaction fields /////////////////
/// The currency fees are paid in (None for native currency)
#[cfg(feature = "celo")]
Expand Down Expand Up @@ -142,7 +158,7 @@ impl Transaction {

match self.transaction_type {
// EIP-2930 (0x01)
Some(x) if x == U64::from(1) => {
Some(x) if x == U64::from(0x1) => {
rlp_opt(&mut rlp, &self.chain_id);
rlp.append(&self.nonce);
rlp_opt(&mut rlp, &self.gas_price);
Expand All @@ -158,9 +174,11 @@ impl Transaction {
if let Some(chain_id) = self.chain_id {
rlp.append(&normalize_v(self.v.as_u64(), U64::from(chain_id.as_u64())));
}
rlp.append(&self.r);
rlp.append(&self.s);
}
// EIP-1559 (0x02)
Some(x) if x == U64::from(2) => {
Some(x) if x == U64::from(0x2) => {
rlp_opt(&mut rlp, &self.chain_id);
rlp.append(&self.nonce);
rlp_opt(&mut rlp, &self.max_priority_fee_per_gas);
Expand All @@ -173,6 +191,20 @@ impl Transaction {
if let Some(chain_id) = self.chain_id {
rlp.append(&normalize_v(self.v.as_u64(), U64::from(chain_id.as_u64())));
}
rlp.append(&self.r);
rlp.append(&self.s);
}
// Optimism Deposited Transaction
#[cfg(feature = "optimism")]
Some(x) if x == U64::from(0x7E) => {
rlp_opt(&mut rlp, &self.source_hash);
rlp.append(&self.from);
rlp_opt(&mut rlp, &self.to);
rlp_opt(&mut rlp, &self.mint);
rlp.append(&self.value);
rlp.append(&self.gas);
rlp_opt(&mut rlp, &self.is_system_tx);
rlp.append(&self.input.as_ref());
}
// Legacy (0x00)
_ => {
Expand All @@ -187,27 +219,32 @@ impl Transaction {
rlp.append(&self.value);
rlp.append(&self.input.as_ref());
rlp.append(&self.v);
rlp.append(&self.r);
rlp.append(&self.s);
}
}

rlp.append(&self.r);
rlp.append(&self.s);

rlp.finalize_unbounded_list();

let rlp_bytes: Bytes = rlp.out().freeze().into();
let mut encoded = vec![];
match self.transaction_type {
Some(x) if x == U64::from(1) => {
Some(x) if x == U64::from(0x1) => {
encoded.extend_from_slice(&[0x1]);
encoded.extend_from_slice(rlp_bytes.as_ref());
encoded.into()
}
Some(x) if x == U64::from(2) => {
Some(x) if x == U64::from(0x2) => {
encoded.extend_from_slice(&[0x2]);
encoded.extend_from_slice(rlp_bytes.as_ref());
encoded.into()
}
#[cfg(feature = "optimism")]
Some(x) if x == U64::from(0x7E) => {
encoded.extend_from_slice(&[0x7E]);
encoded.extend_from_slice(rlp_bytes.as_ref());
encoded.into()
}
_ => rlp_bytes,
}
}
Expand Down Expand Up @@ -1127,4 +1164,42 @@ mod tests {
};
Transaction::decode(&Rlp::new(&tx.rlp())).unwrap();
}

#[test]
#[cfg(feature = "optimism")]
fn test_rlp_encode_deposited_tx() {
let deposited_tx = Transaction {
hash: H256::from_str("0x7fd17d4a368fccdba4291ab121e48c96329b7dc3d027a373643fb23c20a19a3f").unwrap(),
nonce: U256::from(4391989),
block_hash: Some(H256::from_str("0xc2794a16acacd9f7670379ffd12b6968ff98e2a602f57d7d1f880220aa5a4973").unwrap()),
block_number: Some(8453214u64.into()),
transaction_index: Some(0u64.into()),
from: Address::from_str("0xdeaddeaddeaddeaddeaddeaddeaddeaddead0001").unwrap(),
to: Some(Address::from_str("0x4200000000000000000000000000000000000015").unwrap()),
value: U256::zero(),
gas_price: Some(U256::zero()),
gas: U256::from(1000000u64),
input: Bytes::from(
hex::decode("015d8eb90000000000000000000000000000000000000000000000000000000000878c1c00000000000000000000000000000000000000000000000000000000644662bc0000000000000000000000000000000000000000000000000000001ee24fba17b7e19cc10812911dfa8a438e0a81a9933f843aa5b528899b8d9e221b649ae0df00000000000000000000000000000000000000000000000000000000000000060000000000000000000000007431310e026b69bfc676c0013e12a1a11411eec9000000000000000000000000000000000000000000000000000000000000083400000000000000000000000000000000000000000000000000000000000f4240").unwrap()
),
v: U64::zero(),
r: U256::zero(),
s: U256::zero(),
source_hash: Some(H256::from_str("0xa8157ccf61bcdfbcb74a84ec1262e62644dd1e7e3614abcbd8db0c99a60049fc").unwrap()),
mint: Some(0.into()),
is_system_tx: None,
transaction_type: Some(U64::from(126)),
access_list: None,
max_priority_fee_per_gas: None,
max_fee_per_gas: None,
chain_id: None,
other: Default::default()
};

let rlp = deposited_tx.rlp();

let expected_rlp = Bytes::from(hex::decode("7ef90159a0a8157ccf61bcdfbcb74a84ec1262e62644dd1e7e3614abcbd8db0c99a60049fc94deaddeaddeaddeaddeaddeaddeaddeaddead00019442000000000000000000000000000000000000158080830f424080b90104015d8eb90000000000000000000000000000000000000000000000000000000000878c1c00000000000000000000000000000000000000000000000000000000644662bc0000000000000000000000000000000000000000000000000000001ee24fba17b7e19cc10812911dfa8a438e0a81a9933f843aa5b528899b8d9e221b649ae0df00000000000000000000000000000000000000000000000000000000000000060000000000000000000000007431310e026b69bfc676c0013e12a1a11411eec9000000000000000000000000000000000000000000000000000000000000083400000000000000000000000000000000000000000000000000000000000f4240").unwrap());

assert_eq!(rlp, expected_rlp);
}
}
2 changes: 2 additions & 0 deletions ethers/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ celo = [
"legacy",
]

optimism = ["ethers-core/optimism"]

rustls = [
"ethers-contract/rustls",
"ethers-etherscan/rustls",
Expand Down