feat: network abstraction and transaction builder#190
Conversation
| /// The TypedTransaction enum represents all Ethereum transaction request types. | ||
| /// | ||
| /// Its variants correspond to specific allowed transactions: | ||
| /// 1. Legacy (pre-EIP2718) [`TxLegacy`] | ||
| /// 2. EIP2930 (state access lists) [`TxEip2930`] | ||
| /// 3. EIP1559 [`TxEip1559`] | ||
| /// 4. EIP4844 [`TxEip4844`] | ||
| #[derive(Debug, Clone, PartialEq, Eq, Hash)] | ||
| pub enum TypedTransaction { |
d0a8838 to
3300b0c
Compare
| // todo: block responses are ethereum only, so we need to include this in here too, or make `Block` | ||
| // generic over tx/header type |
There was a problem hiding this comment.
let's do as follow-up IMO when we're adding the 2nd network?
| // todo: move | ||
| /// A signer capable of signing any transaction for the given network. | ||
| #[async_trait] | ||
| pub trait NetworkSigner<N: Network> { | ||
| /// Asynchronously sign an unsigned transaction. | ||
| async fn sign(&self, tx: N::UnsignedTx) -> alloy_signer::Result<N::TxEnvelope>; | ||
| } | ||
|
|
||
| // todo: move | ||
| /// An async signer capable of signing any [SignableTransaction] for the given [Signature] type. | ||
| #[async_trait] | ||
| pub trait TxSigner<Signature> { | ||
| /// Asynchronously sign an unsigned transaction. | ||
| async fn sign_transaction( | ||
| &self, | ||
| tx: &mut dyn SignableTransaction<Signature>, | ||
| ) -> alloy_signer::Result<Signature>; |
There was a problem hiding this comment.
what's the difference between these 2?
There was a problem hiding this comment.
NetworkSigner is a signer capable of signing any transaction with any signature type for a given network, TxSigner is a signer capable of signing any transaction regardless of network for a given signature type.
The idea is to have fewer generics for signers
crates/providers/src/lib.rs
Outdated
| key: U256, | ||
| tag: Option<BlockId>, | ||
| ) -> TransportResult<StorageValue> { | ||
| self.client.prepare("eth_getStorageAt", (address, key, tag.unwrap_or_default())).await |
There was a problem hiding this comment.
generally find it weird we're calling this self.client.prepare when it's sending the request, shouldnt it be self.client.prepare(...).send().await or self.client.request(...)?
There was a problem hiding this comment.
I think it's called prepare because it doesn't do any serialization or really anything at all until awaited. The send method in your example is equiv to awaiting it
7ff8f67 to
0ac977d
Compare
e454e82 to
f23137f
Compare
| assert_eq!(recovered2, address); | ||
| } | ||
|
|
||
| #[tokio::test] |
There was a problem hiding this comment.
This test was moved to the network crate
| /// | ||
| /// Returns `None` if the transaction is not a contract creation (the `to` field is set), or if | ||
| /// the `from` or `nonce` fields are not set. | ||
| pub fn calculate_create_address(&self) -> Option<Address> { |
There was a problem hiding this comment.
this is now part of the Builder trait, so removed it here. can add it back if we want
There was a problem hiding this comment.
I added it for convenience, it's not strictly necessary but it's nice to have
There was a problem hiding this comment.
builder trait should cover us here
crates/network/src/ethereum/mod.rs
Outdated
| } | ||
|
|
||
| /// Build a legacy transaction. | ||
| fn build_legacy(request: TransactionRequest) -> Result<TxLegacy, TransactionBuilderError> { |
There was a problem hiding this comment.
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)
| retries -= 1; | ||
| continue; | ||
| } | ||
| Ok(None) if retries > 0 => { |
There was a problem hiding this comment.
the rpc can respond with the block not existing, which can be the case if we are querying the latest block but the node doesn't have it yet. in that case i think we retry imo
it would be nicer if we had the error, but that would cause cyclical dep
|
rebased ptal @gakonst @DaniPopes, after merge I will create all the follow up issues |
|
I realized we're not running doctests in CI because when I ran |
|
created all the follow up issues, will look into doctest |
|
@DaniPopes we are, it just showed up later. i ran |
|
Yes, because I added it in 81c98a5 😄 |
Motivation
We want a transaction builder that can build one of several strongly-typed consensus transactions for signing to implement
send_transaction. The intended flow is:This is made complicated due to our network abstraction:
Signaturefor transactions is not a good abstractionThis flow should also work server side, i.e. if I get a
TransactionRequestover the wire from a client, I should be able to resolve it to a consensus type transaction. I should also be able to take an already-signed RLP-encoded transaction and decode it into one of the valid transaction types.This PR intends to:
send_transactionin the provider and thealloy-contractcall builderSolution
TransactionBuildertrait and implements this forTransactionRequestEthereumnetwork with all types definedalloy-contractto work with the new providerTransactionintoTransactionandSignableTransaction, since not all transactions are signable (e.g. Optimism deposit tx's)Signaturetype inTransactioninto a parameter insteadTxSigner,TxSignerSyncandNetworkSignerTxSigneris essentially ripped out of theSignertrait and should be implemented for all signers, even if the signer does not care about transactions.Signature. Signer implementors need to implement this andSigner.NetworkSignerinstead is implemented by network crate authors. This trait is implemented for a wrapper type and essentially states that the signer can sign any transaction for the given network. (SeeEthereumSigner)ProviderBuilderabstraction and add aSignerLayerto add signing on top of an existing providerCompanion PRs:
TxKindcore#542Things left:
ProviderBuildera bitFollow ups
AnyNetworkthat is essentiallyEthereum+OtherFieldson all types [Feature] AddAnyNetwork#272Walletetc. out ofalloy-signersto makealloy-networkwasm compatible again, see feat: network abstraction and transaction builder #190 (comment) ([Feature] MoveWallet/LocalWalletetc to its own crate #262)RootProvider([Feature] Make it easier to instantiate aRootProvider#266)Breaking changes
Anything that uses
alloy-contract,alloy-provider, oralloy-consensuslikely broke with this PR as the API changed.PR Checklist