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: network abstraction and transaction builder #190

Merged
merged 65 commits into from
Mar 11, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
65 commits
Select commit Hold shift + click to select a range
d030974
refactor: remove `TempProvider`
onbjerg Mar 7, 2024
668d3d0
chore: bump alloy to 0.6.4
onbjerg Mar 7, 2024
a698dee
refactor: add `TypedTransaction` and tx traits
onbjerg Mar 7, 2024
dfbc394
feat: tx builder
onbjerg Mar 7, 2024
6e1b1ee
feat: network signer trait
onbjerg Mar 7, 2024
8c5c484
feat: `Ethereum` network
onbjerg Mar 7, 2024
66cf994
refactor: adjust alloy-contract to new provider
onbjerg Mar 7, 2024
bc5f084
refactor: adjust signers to new signer traits
onbjerg Mar 7, 2024
6e4e7d2
chore: remove unused imports
onbjerg Mar 7, 2024
b809324
feat: EIP-1559 gas methods in builder
onbjerg Mar 7, 2024
98b7c67
feat: EIP-4844 gas methods in builder
onbjerg Mar 7, 2024
13e9a3b
feat: impl ethereum builder
onbjerg Mar 7, 2024
f6b8e00
chore: rm unused dep
onbjerg Mar 7, 2024
16b328e
temp: remove `Hash` derive
onbjerg Mar 7, 2024
b3db0fe
fix: cfg attr for wasm
onbjerg Mar 7, 2024
126a4b7
feat: tx signer trait impls for `Wallet`
onbjerg Mar 7, 2024
92cf293
test: use ethereum network in provider test
onbjerg Mar 7, 2024
5f195ba
docs: adjust provider docs
onbjerg Mar 7, 2024
9d3f6d3
temp: temporarily disable contract tests
onbjerg Mar 7, 2024
b419d25
chore: clippy
onbjerg Mar 7, 2024
105fc61
feat: finish tx signer trait impls
onbjerg Mar 7, 2024
c89e5a4
fix: set tx chain id
onbjerg Mar 7, 2024
f3541d2
test: port old provider tests
onbjerg Mar 7, 2024
07d5aea
test: don't drop `AnvilInstance` too early
onbjerg Mar 7, 2024
d88eba1
test: fix k256 gated tests
onbjerg Mar 7, 2024
5dcd259
chore: fmt
onbjerg Mar 7, 2024
5d0a548
chore: clippy
onbjerg Mar 7, 2024
ac07332
chore: docs lint
onbjerg Mar 7, 2024
264daa1
ci: do not check wasm support for `Network` crate
onbjerg Mar 7, 2024
136cf84
docs: adjust all docs
onbjerg Mar 7, 2024
043cef3
chore: rm unneeded `'static`
onbjerg Mar 7, 2024
34b76ee
test: re-enable contract tests
onbjerg Mar 7, 2024
2404e88
chore: use patched sol-macro
onbjerg Mar 7, 2024
288959f
chore: update sol-macro patch
onbjerg Mar 7, 2024
201df2b
refactor: move anvil methods to extension trait
onbjerg Mar 7, 2024
14d8329
feat: populate gas helpers
onbjerg Mar 7, 2024
5372ae0
fix: remove anvil cfg
onbjerg Mar 7, 2024
abf6e4b
chore: clippy
onbjerg Mar 7, 2024
04158ea
feat: impl `TxSigner` for aws, gcp
onbjerg Mar 8, 2024
4715b3e
refactor: move `Receipt` trait
onbjerg Mar 8, 2024
b04e966
refactor: move tx builder impl to own module
onbjerg Mar 8, 2024
89c8c01
feat: `CallBuilder::map`
onbjerg Mar 8, 2024
d5585ad
test: fix double import
onbjerg Mar 8, 2024
290beab
chore: last nit
onbjerg Mar 8, 2024
d897d2e
feat: signer layer
onbjerg Mar 8, 2024
e228a61
feat: `CallBuilder::send`
onbjerg Mar 8, 2024
ff555ab
chore: nits
DaniPopes Mar 8, 2024
dbf7f7f
chore: ZST cannot be enforced by a trait
DaniPopes Mar 8, 2024
dc18b0d
feat: better private key instantiation discoverability
DaniPopes Mar 8, 2024
8afa99c
chore: rm outdated todo
onbjerg Mar 8, 2024
6f9b180
feat: `EthereumSigner::new`
onbjerg Mar 8, 2024
a7be381
chore: code nit
onbjerg Mar 8, 2024
17eb93a
docs: note about network zst
onbjerg Mar 8, 2024
bf86056
chore: nit
onbjerg Mar 8, 2024
5210141
refactor: rename `sign` to `sign_transaction`
onbjerg Mar 8, 2024
69bb2e1
feat: `ProviderBuilder::signer`
onbjerg Mar 8, 2024
9324861
docs: more network signer docs
onbjerg Mar 11, 2024
2425bc9
refactor: remove unneeded `'static`
onbjerg Mar 11, 2024
95f22db
refactor: `set_chain_id_checked` sort of
onbjerg Mar 11, 2024
5a41fbb
chore: use LocalWallet
DaniPopes Mar 11, 2024
6e03867
fix: docs
DaniPopes Mar 11, 2024
ceb865a
chore: dedup signing code
DaniPopes Mar 11, 2024
ad9c520
chore: more dedup
DaniPopes Mar 11, 2024
81c98a5
ci: run doctests
DaniPopes Mar 11, 2024
d2a349e
test: fix doctest
onbjerg Mar 11, 2024
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
Prev Previous commit
Next Next commit
feat: impl ethereum builder
  • Loading branch information
onbjerg committed Mar 11, 2024
commit 13e9a3b4eb60ad3ac1766e5e417e087a70e31106
144 changes: 139 additions & 5 deletions crates/network/src/ethereum/mod.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
use crate::{BuilderResult, Network, NetworkSigner, TransactionBuilder};
use crate::{BuilderResult, Network, NetworkSigner, TransactionBuilder, TransactionBuilderError};
use alloy_consensus::{TxEip1559, TxEip2930, TxEip4844, TxEip4844Variant, TxLegacy};
use alloy_primitives::{Address, TxKind, U256, U64};
use alloy_rpc_types::request::TransactionRequest;
use async_trait::async_trait;

mod receipt;
mod signer;
use alloy_primitives::{Address, TxKind, U256, U64};
pub use signer::EthereumSigner;

/// Types for a mainnet-like Ethereum network.
Expand All @@ -27,6 +30,7 @@ impl Network for Ethereum {
type HeaderResponse = alloy_rpc_types::Header;
}

#[async_trait]
impl TransactionBuilder<Ethereum> for alloy_rpc_types::TransactionRequest {
fn chain_id(&self) -> Option<alloy_primitives::ChainId> {
self.chain_id
Expand Down Expand Up @@ -120,13 +124,143 @@ impl TransactionBuilder<Ethereum> for alloy_rpc_types::TransactionRequest {
}

fn build_unsigned(self) -> BuilderResult<<Ethereum as Network>::UnsignedTx> {
todo!()
match (
self.gas_price.as_ref(),
self.max_fee_per_gas.as_ref(),
self.access_list.as_ref(),
self.max_fee_per_blob_gas.as_ref(),
self.blob_versioned_hashes.as_ref(),
self.sidecar.as_ref(),
) {
// Legacy transaction
(Some(_), None, None, None, None, None) => build_legacy(self).map(Into::into),
// EIP-2930
// If only accesslist is set, and there are no EIP-1559 fees
(_, None, Some(_), None, None, None) => build_2930(self).map(Into::into),
// EIP-1559
// If EIP-4844 fields are missing
(None, _, _, None, None, None) => build_1559(self).map(Into::into),
// EIP-4844
// All blob fields required
(None, _, _, Some(_), Some(_), Some(_)) => {
build_4844(self).map(TxEip4844Variant::from).map(Into::into)
}
_ => build_legacy(self).map(Into::into),
}
}

fn build<S: NetworkSigner<Ethereum>>(
async fn build<S: NetworkSigner<Ethereum>>(
self,
signer: &S,
) -> BuilderResult<<Ethereum as Network>::TxEnvelope> {
todo!()
// todo: BuilderResult + SignerResult
Ok(signer.sign(self.build_unsigned()?).await.unwrap())
}
}

/// Build a legacy transaction.
fn build_legacy(request: TransactionRequest) -> Result<TxLegacy, TransactionBuilderError> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ideally I'd have these on TransactionRequest, but that would cause a circular dep (alloy-network depends on rpc-types -> rpc-types would need to depend on alloy-network for TransactionBuilderError)

Ok(TxLegacy {
chain_id: request.chain_id,
nonce: request.nonce.ok_or_else(|| TransactionBuilderError::MissingKey("nonce"))?.to(),
gas_price: request
.gas_price
.ok_or_else(|| TransactionBuilderError::MissingKey("gas_price"))?
.to(),
gas_limit: request
.gas
.ok_or_else(|| TransactionBuilderError::MissingKey("gas_limit"))?
.to(),
to: request.to.into(),
value: request.value.unwrap_or_default(),
input: request.input.into_input().unwrap_or_default(),
})
}

/// Build an EIP-1559 transaction.
fn build_1559(request: TransactionRequest) -> Result<TxEip1559, TransactionBuilderError> {
Ok(TxEip1559 {
chain_id: request.chain_id.unwrap_or(1),
nonce: request.nonce.ok_or_else(|| TransactionBuilderError::MissingKey("nonce"))?.to(),
max_priority_fee_per_gas: request
.max_priority_fee_per_gas
.ok_or_else(|| TransactionBuilderError::MissingKey("max_priority_fee_per_gas"))?
.to(),
max_fee_per_gas: request
.max_fee_per_gas
.ok_or_else(|| TransactionBuilderError::MissingKey("max_fee_per_gas"))?
.to(),
gas_limit: request
.gas
.ok_or_else(|| TransactionBuilderError::MissingKey("gas_limit"))?
.to(),
to: request.to.into(),
value: request.value.unwrap_or_default(),
input: request.input.into_input().unwrap_or_default(),
access_list: convert_access_list(request.access_list.unwrap_or_default()),
})
}

/// Build an EIP-2930 transaction.
fn build_2930(request: TransactionRequest) -> Result<TxEip2930, TransactionBuilderError> {
Ok(TxEip2930 {
chain_id: request.chain_id.unwrap_or(1),
nonce: request.nonce.ok_or_else(|| TransactionBuilderError::MissingKey("nonce"))?.to(),
gas_price: request
.gas_price
.ok_or_else(|| TransactionBuilderError::MissingKey("gas_price"))?
.to(),
gas_limit: request
.gas
.ok_or_else(|| TransactionBuilderError::MissingKey("gas_limit"))?
.to(),
to: request.to.into(),
value: request.value.unwrap_or_default(),
input: request.input.into_input().unwrap_or_default(),
access_list: convert_access_list(request.access_list.unwrap_or_default()),
})
}

/// Build an EIP-4844 transaction.
fn build_4844(request: TransactionRequest) -> Result<TxEip4844, TransactionBuilderError> {
Ok(TxEip4844 {
chain_id: request.chain_id.unwrap_or(1),
nonce: request.nonce.ok_or_else(|| TransactionBuilderError::MissingKey("nonce"))?.to(),
gas_limit: request
.gas
.ok_or_else(|| TransactionBuilderError::MissingKey("gas_limit"))?
.to(),
max_fee_per_gas: request
.max_fee_per_gas
.ok_or_else(|| TransactionBuilderError::MissingKey("max_fee_per_gas"))?
.to(),
max_priority_fee_per_gas: request
.max_priority_fee_per_gas
.ok_or_else(|| TransactionBuilderError::MissingKey("max_priority_fee_per_gas"))?
.to(),
to: request.to.into(),
value: request.value.unwrap_or_default(),
access_list: convert_access_list(request.access_list.unwrap_or_default()),
blob_versioned_hashes: request
.blob_versioned_hashes
.ok_or_else(|| TransactionBuilderError::MissingKey("blob_versioned_hashes"))?,
max_fee_per_blob_gas: request
.max_fee_per_blob_gas
.ok_or_else(|| TransactionBuilderError::MissingKey("max_fee_per_blob_gas"))?
.to(),
input: request.input.into_input().unwrap_or_default(),
})
}

// todo: these types are almost 1:1, minus rlp decoding and ser/de, should dedupe
fn convert_access_list(list: alloy_rpc_types::AccessList) -> alloy_eips::eip2930::AccessList {
alloy_eips::eip2930::AccessList(
list.0
.into_iter()
.map(|item| alloy_eips::eip2930::AccessListItem {
address: item.address,
storage_keys: item.storage_keys,
})
.collect(),
)
}
4 changes: 3 additions & 1 deletion crates/network/src/transaction/builder.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use super::signer::NetworkSigner;
use crate::Network;
use alloy_primitives::{Address, Bytes, ChainId, TxKind, U256, U64};
use async_trait::async_trait;

/// Error type for transaction builders.
#[derive(Debug, thiserror::Error)]
Expand Down Expand Up @@ -42,6 +43,7 @@ pub type BuilderResult<T, E = TransactionBuilderError> = std::result::Result<T,
///
/// Transaction builders should be able to construct all available transaction types on a given
/// network.
#[async_trait]
pub trait TransactionBuilder<N: Network>: Default + Sized + Send + Sync + 'static {
/// Get the chain ID for the transaction.
fn chain_id(&self) -> Option<ChainId>;
Expand Down Expand Up @@ -192,5 +194,5 @@ pub trait TransactionBuilder<N: Network>: Default + Sized + Send + Sync + 'stati
fn build_unsigned(self) -> BuilderResult<N::UnsignedTx>;

/// Build a signed transaction.
fn build<S: NetworkSigner<N>>(self, signer: &S) -> BuilderResult<N::TxEnvelope>;
async fn build<S: NetworkSigner<N>>(self, signer: &S) -> BuilderResult<N::TxEnvelope>;
}
2 changes: 1 addition & 1 deletion crates/network/src/transaction/signer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use async_trait::async_trait;
// todo: move
/// A signer capable of signing any transaction for the given network.
#[async_trait]
pub trait NetworkSigner<N: Network> {
pub trait NetworkSigner<N: Network>: Sync {
/// Asynchronously sign an unsigned transaction.
async fn sign(&self, tx: N::UnsignedTx) -> alloy_signer::Result<N::TxEnvelope>;
}
Expand Down
16 changes: 13 additions & 3 deletions crates/rpc-types/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,22 @@ proptest-derive = { version = "0.4", optional = true }
jsonrpsee-types = { version = "0.20", optional = true }

[features]
arbitrary = ["dep:arbitrary", "dep:proptest-derive", "dep:proptest", "alloy-primitives/arbitrary"]
arbitrary = [
"dep:arbitrary",
"dep:proptest-derive",
"dep:proptest",
"alloy-primitives/arbitrary",
]
jsonrpsee-types = ["dep:jsonrpsee-types"]
ssz = ["dep:ethereum_ssz" ,"dep:ethereum_ssz_derive", "alloy-primitives/ssz"]
ssz = ["dep:ethereum_ssz", "dep:ethereum_ssz_derive", "alloy-primitives/ssz"]

[dev-dependencies]
alloy-primitives = { workspace = true, features = ["rand", "rlp", "serde", "arbitrary"] }
alloy-primitives = { workspace = true, features = [
"rand",
"rlp",
"serde",
"arbitrary",
] }

arbitrary = { workspace = true, features = ["derive"] }
proptest.workspace = true
Expand Down