mirror of
https://github.com/hl-archive-node/nanoreth.git
synced 2025-12-06 10:59:55 +00:00
feat: impl send_transaction (#1774)
Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
This commit is contained in:
@ -3,7 +3,9 @@
|
||||
//! transaction deserialized from the json input of an RPC call. Depending on what fields are set,
|
||||
//! it can be converted into the container type [`TypedTransactionRequest`].
|
||||
|
||||
use reth_primitives::{AccessList, Address, Bytes, U128, U256};
|
||||
use reth_primitives::{
|
||||
AccessList, Address, Bytes, Transaction, TxEip1559, TxEip2930, TxLegacy, U128, U256,
|
||||
};
|
||||
use reth_rlp::{BufMut, Decodable, DecodeError, Encodable, RlpDecodable, RlpEncodable};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
@ -20,6 +22,46 @@ pub enum TypedTransactionRequest {
|
||||
EIP1559(EIP1559TransactionRequest),
|
||||
}
|
||||
|
||||
impl TypedTransactionRequest {
|
||||
/// coverts a typed transaction request into a primitive transaction
|
||||
pub fn into_transaction(self) -> Transaction {
|
||||
match self {
|
||||
TypedTransactionRequest::Legacy(tx) => Transaction::Legacy(TxLegacy {
|
||||
chain_id: tx.chain_id,
|
||||
nonce: u64::from_be_bytes(tx.nonce.to_be_bytes()),
|
||||
gas_price: u128::from_be_bytes(tx.gas_price.to_be_bytes()),
|
||||
gas_limit: u64::from_be_bytes(tx.gas_limit.to_be_bytes()),
|
||||
to: tx.kind.into(),
|
||||
value: u128::from_be_bytes(tx.value.to_be_bytes()),
|
||||
input: tx.input,
|
||||
}),
|
||||
TypedTransactionRequest::EIP2930(tx) => Transaction::Eip2930(TxEip2930 {
|
||||
chain_id: tx.chain_id,
|
||||
nonce: u64::from_be_bytes(tx.nonce.to_be_bytes()),
|
||||
gas_price: u128::from_be_bytes(tx.gas_price.to_be_bytes()),
|
||||
gas_limit: u64::from_be_bytes(tx.gas_limit.to_be_bytes()),
|
||||
to: tx.kind.into(),
|
||||
value: u128::from_be_bytes(tx.value.to_be_bytes()),
|
||||
input: tx.input,
|
||||
access_list: tx.access_list,
|
||||
}),
|
||||
TypedTransactionRequest::EIP1559(tx) => Transaction::Eip1559(TxEip1559 {
|
||||
chain_id: tx.chain_id,
|
||||
nonce: u64::from_be_bytes(tx.nonce.to_be_bytes()),
|
||||
max_fee_per_gas: u128::from_be_bytes(tx.max_fee_per_gas.to_be_bytes()),
|
||||
gas_limit: u64::from_be_bytes(tx.gas_limit.to_be_bytes()),
|
||||
to: tx.kind.into(),
|
||||
value: u128::from_be_bytes(tx.value.to_be_bytes()),
|
||||
input: tx.input,
|
||||
access_list: tx.access_list,
|
||||
max_priority_fee_per_gas: u128::from_be_bytes(
|
||||
tx.max_priority_fee_per_gas.to_be_bytes(),
|
||||
),
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents a legacy transaction request
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct LegacyTransactionRequest {
|
||||
@ -112,3 +154,12 @@ impl Decodable for TransactionKind {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<TransactionKind> for reth_primitives::TransactionKind {
|
||||
fn from(kind: TransactionKind) -> Self {
|
||||
match kind {
|
||||
TransactionKind::Call(to) => reth_primitives::TransactionKind::Call(to),
|
||||
TransactionKind::Create => reth_primitives::TransactionKind::Create,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -28,6 +28,7 @@ reth-tasks = { path = "../../tasks" }
|
||||
revm = { version = "3.0.0", features = ["optional_block_gas_limit"] }
|
||||
ethers-core = { git = "https://github.com/gakonst/ethers-rs", features = ["eip712"] }
|
||||
|
||||
|
||||
# rpc
|
||||
jsonrpsee = { version = "0.16" }
|
||||
http = "0.2.8"
|
||||
|
||||
@ -7,13 +7,16 @@ use crate::{
|
||||
EthApi,
|
||||
};
|
||||
use async_trait::async_trait;
|
||||
|
||||
use crate::eth::error::SignError;
|
||||
use reth_primitives::{
|
||||
BlockId, BlockNumberOrTag, Bytes, FromRecoveredTransaction, IntoRecoveredTransaction,
|
||||
TransactionSignedEcRecovered, H256, U256,
|
||||
Address, BlockId, BlockNumberOrTag, Bytes, FromRecoveredTransaction, IntoRecoveredTransaction,
|
||||
TransactionSigned, TransactionSignedEcRecovered, H256, U256,
|
||||
};
|
||||
use reth_provider::{providers::ChainState, BlockProvider, EvmEnvProvider, StateProviderFactory};
|
||||
|
||||
use reth_rpc_types::{Index, Transaction, TransactionInfo, TransactionRequest};
|
||||
use reth_rpc_types::{
|
||||
Index, Transaction, TransactionInfo, TransactionRequest, TypedTransactionRequest,
|
||||
};
|
||||
use reth_transaction_pool::{TransactionOrigin, TransactionPool};
|
||||
use revm::primitives::{BlockEnv, CfgEnv};
|
||||
|
||||
@ -150,8 +153,53 @@ where
|
||||
Client: BlockProvider + StateProviderFactory + EvmEnvProvider + 'static,
|
||||
Network: 'static,
|
||||
{
|
||||
pub(crate) async fn send_transaction(&self, _request: TransactionRequest) -> EthResult<H256> {
|
||||
unimplemented!()
|
||||
/// Send a transaction to the pool
|
||||
///
|
||||
/// This will sign the transaction and submit it to the pool
|
||||
pub(crate) async fn send_transaction(&self, request: TransactionRequest) -> EthResult<H256> {
|
||||
let from = match request.from {
|
||||
Some(from) => from,
|
||||
None => return Err(SignError::NoAccount.into()),
|
||||
};
|
||||
let transaction = match request.into_typed_request() {
|
||||
Some(tx) => tx,
|
||||
None => {
|
||||
return Err(EthApiError::Unsupported(
|
||||
"both gasPrice and (maxFeePerGas or maxPriorityFeePerGas) specified",
|
||||
))
|
||||
}
|
||||
};
|
||||
|
||||
// TODO we need to update additional settings in the transaction: nonce, gaslimit, chainid,
|
||||
// gasprice
|
||||
|
||||
let signed_tx = self.sign_request(&from, transaction)?;
|
||||
|
||||
let recovered =
|
||||
signed_tx.into_ecrecovered().ok_or(EthApiError::InvalidTransactionSignature)?;
|
||||
|
||||
let pool_transaction = <Pool::Transaction>::from_recovered_transaction(recovered);
|
||||
|
||||
// submit the transaction to the pool with a `Local` origin
|
||||
let hash = self.pool().add_transaction(TransactionOrigin::Local, pool_transaction).await?;
|
||||
|
||||
Ok(hash)
|
||||
}
|
||||
|
||||
pub(crate) fn sign_request(
|
||||
&self,
|
||||
from: &Address,
|
||||
request: TypedTransactionRequest,
|
||||
) -> EthResult<TransactionSigned> {
|
||||
for signer in self.inner.signers.iter() {
|
||||
if signer.is_signer_for(from) {
|
||||
return match signer.sign_transaction(request, from) {
|
||||
Ok(tx) => Ok(tx),
|
||||
Err(e) => Err(e.into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(EthApiError::InvalidTransactionSignature)
|
||||
}
|
||||
|
||||
/// Get Transaction by [BlockId] and the index of the transaction within that Block.
|
||||
|
||||
@ -374,6 +374,9 @@ pub enum SignError {
|
||||
/// TypedData has invalid format.
|
||||
#[error("Given typed data is not valid")]
|
||||
TypedData,
|
||||
/// No chainid
|
||||
#[error("No chainid")]
|
||||
NoChainId,
|
||||
}
|
||||
|
||||
/// Converts the evm [ExecutionResult] into a result where `Ok` variant is the output bytes if it is
|
||||
|
||||
@ -7,6 +7,7 @@ use ethers_core::{
|
||||
};
|
||||
use reth_primitives::{sign_message, Address, Signature, TransactionSigned, H256};
|
||||
use reth_rpc_types::TypedTransactionRequest;
|
||||
|
||||
use secp256k1::SecretKey;
|
||||
use std::collections::HashMap;
|
||||
|
||||
@ -72,10 +73,15 @@ impl EthSigner for DevSigner {
|
||||
|
||||
fn sign_transaction(
|
||||
&self,
|
||||
_request: TypedTransactionRequest,
|
||||
_address: &Address,
|
||||
request: TypedTransactionRequest,
|
||||
address: &Address,
|
||||
) -> Result<TransactionSigned> {
|
||||
todo!()
|
||||
// convert to primitive transaction
|
||||
let transaction = request.into_transaction();
|
||||
let tx_signature_hash = transaction.signature_hash();
|
||||
let signature = self.sign_hash(tx_signature_hash, *address)?;
|
||||
|
||||
Ok(TransactionSigned::from_transaction_and_signature(transaction, signature))
|
||||
}
|
||||
|
||||
fn sign_typed_data(&self, address: Address, payload: &TypedData) -> Result<Signature> {
|
||||
|
||||
Reference in New Issue
Block a user