Skip to content

Commit 5259777

Browse files
authored
EOA Production Readiness (#14)
* wip * refactor eoa store * add event notification integration points * wip * typo * Add transaction context creation from Redis pipeline - Introduced `transaction_context_from_pipeline` method to allow atomic job queuing within an existing Redis transaction. - This enhancement improves the flexibility of transaction handling in the queue implementation. * Add thirdweb-core dependency and enhance transaction processing - Added `thirdweb-core` as a dependency in `Cargo.toml` and updated `Cargo.lock`. - Refactored transaction handling in the EOA executor to improve error handling and processing of borrowed transactions. - Introduced new methods for moving pending transactions to borrowed state using incremented and recycled nonces. - Enhanced submission result processing to include detailed reporting and webhook event queuing for transaction outcomes. These changes aim to improve the robustness and flexibility of transaction management within the EOA executor. * Refactor signer and transaction handling for improved efficiency - Updated the `SmartAccountSigner` to pass credentials by reference instead of cloning, enhancing performance. - Modified transaction structures to include default serialization options for gas limits and transaction type data. - Enhanced webhook options handling in various job data structures, ensuring consistent default behavior. - Refactored the EOA executor to streamline transaction confirmation and submission processes, improving overall transaction management. These changes aim to optimize resource usage and improve the clarity of transaction handling across the codebase. * remove dbg, fix underflow * fix event
1 parent 00832c3 commit 5259777

File tree

40 files changed

+5073
-4203
lines changed

40 files changed

+5073
-4203
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

aa-core/src/signer.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@ impl<C: Chain + Clone> SmartAccountSigner<C> {
161161
},
162162
message,
163163
format,
164-
self.credentials.clone(),
164+
&self.credentials,
165165
)
166166
.await
167167
}
@@ -189,7 +189,7 @@ impl<C: Chain + Clone> SmartAccountSigner<C> {
189189
from: self.options.signer_address,
190190
},
191191
typed_data,
192-
self.credentials.clone(),
192+
&self.credentials,
193193
)
194194
.await;
195195
}
@@ -212,7 +212,7 @@ impl<C: Chain + Clone> SmartAccountSigner<C> {
212212
from: self.options.signer_address,
213213
},
214214
typed_data,
215-
self.credentials.clone(),
215+
&self.credentials,
216216
)
217217
.await
218218
}
@@ -242,7 +242,7 @@ impl<C: Chain + Clone> SmartAccountSigner<C> {
242242
from: self.options.signer_address,
243243
},
244244
&typed_data,
245-
self.credentials.clone(),
245+
&self.credentials,
246246
)
247247
.await
248248
}

core/src/execution_options/mod.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,8 @@ pub struct WebhookOptions {
8989
pub struct SendTransactionRequest {
9090
pub execution_options: ExecutionOptions,
9191
pub params: Vec<InnerTransaction>,
92-
pub webhook_options: Option<Vec<WebhookOptions>>,
92+
#[serde(default)]
93+
pub webhook_options: Vec<WebhookOptions>,
9394
}
9495

9596
/// # QueuedTransaction

core/src/signer.rs

Lines changed: 16 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -169,23 +169,23 @@ pub trait AccountSigner {
169169
options: Self::SigningOptions,
170170
message: &str,
171171
format: MessageFormat,
172-
credentials: SigningCredential,
172+
credentials: &SigningCredential,
173173
) -> impl std::future::Future<Output = Result<String, EngineError>> + Send;
174174

175175
/// Sign typed data
176176
fn sign_typed_data(
177177
&self,
178178
options: Self::SigningOptions,
179179
typed_data: &TypedData,
180-
credentials: SigningCredential,
180+
credentials: &SigningCredential,
181181
) -> impl std::future::Future<Output = Result<String, EngineError>> + Send;
182182

183183
/// Sign a transaction
184184
fn sign_transaction(
185185
&self,
186186
options: Self::SigningOptions,
187-
transaction: TypedTransaction,
188-
credentials: SigningCredential,
187+
transaction: &TypedTransaction,
188+
credentials: &SigningCredential,
189189
) -> impl std::future::Future<Output = Result<String, EngineError>> + Send;
190190

191191
/// Sign EIP-7702 authorization
@@ -195,7 +195,7 @@ pub trait AccountSigner {
195195
chain_id: u64,
196196
address: Address,
197197
nonce: alloy::primitives::U256,
198-
credentials: SigningCredential,
198+
credentials: &SigningCredential,
199199
) -> impl std::future::Future<Output = Result<SignedAuthorization, EngineError>> + Send;
200200
}
201201

@@ -224,7 +224,7 @@ impl AccountSigner for EoaSigner {
224224
options: EoaSigningOptions,
225225
message: &str,
226226
format: MessageFormat,
227-
credentials: SigningCredential,
227+
credentials: &SigningCredential,
228228
) -> Result<String, EngineError> {
229229
match credentials {
230230
SigningCredential::Vault(auth_method) => {
@@ -260,7 +260,7 @@ impl AccountSigner for EoaSigner {
260260
.sign_message(
261261
auth_token,
262262
thirdweb_auth,
263-
message.to_string(),
263+
message,
264264
options.from,
265265
options.chain_id,
266266
Some(iaw_format),
@@ -280,7 +280,7 @@ impl AccountSigner for EoaSigner {
280280
&self,
281281
options: EoaSigningOptions,
282282
typed_data: &TypedData,
283-
credentials: SigningCredential,
283+
credentials: &SigningCredential,
284284
) -> Result<String, EngineError> {
285285
match &credentials {
286286
SigningCredential::Vault(auth_method) => {
@@ -301,12 +301,7 @@ impl AccountSigner for EoaSigner {
301301
} => {
302302
let iaw_result = self
303303
.iaw_client
304-
.sign_typed_data(
305-
auth_token.clone(),
306-
thirdweb_auth.clone(),
307-
typed_data.clone(),
308-
options.from,
309-
)
304+
.sign_typed_data(auth_token, thirdweb_auth, typed_data, options.from)
310305
.await
311306
.map_err(|e| {
312307
tracing::error!("Error signing typed data with EOA (IAW): {:?}", e);
@@ -321,14 +316,14 @@ impl AccountSigner for EoaSigner {
321316
async fn sign_transaction(
322317
&self,
323318
options: EoaSigningOptions,
324-
transaction: TypedTransaction,
325-
credentials: SigningCredential,
319+
transaction: &TypedTransaction,
320+
credentials: &SigningCredential,
326321
) -> Result<String, EngineError> {
327322
match credentials {
328323
SigningCredential::Vault(auth_method) => {
329324
let vault_result = self
330325
.vault_client
331-
.sign_transaction(auth_method.clone(), transaction, options.from)
326+
.sign_transaction(auth_method.clone(), transaction.clone(), options.from)
332327
.await
333328
.map_err(|e| {
334329
tracing::error!("Error signing transaction with EOA (Vault): {:?}", e);
@@ -343,7 +338,7 @@ impl AccountSigner for EoaSigner {
343338
} => {
344339
let iaw_result = self
345340
.iaw_client
346-
.sign_transaction(auth_token.clone(), thirdweb_auth.clone(), transaction)
341+
.sign_transaction(auth_token, thirdweb_auth, &transaction)
347342
.await
348343
.map_err(|e| {
349344
tracing::error!("Error signing transaction with EOA (IAW): {:?}", e);
@@ -361,7 +356,7 @@ impl AccountSigner for EoaSigner {
361356
chain_id: u64,
362357
address: Address,
363358
nonce: U256,
364-
credentials: SigningCredential,
359+
credentials: &SigningCredential,
365360
) -> Result<SignedAuthorization, EngineError> {
366361
// Create the Authorization struct that both clients expect
367362
let authorization = Authorization {
@@ -373,7 +368,7 @@ impl AccountSigner for EoaSigner {
373368
SigningCredential::Vault(auth_method) => {
374369
let vault_result = self
375370
.vault_client
376-
.sign_authorization(auth_method, options.from, authorization)
371+
.sign_authorization(auth_method.clone(), options.from, authorization)
377372
.await
378373
.map_err(|e| {
379374
tracing::error!("Error signing authorization with EOA (Vault): {:?}", e);
@@ -389,7 +384,7 @@ impl AccountSigner for EoaSigner {
389384
} => {
390385
let iaw_result = self
391386
.iaw_client
392-
.sign_authorization(auth_token, thirdweb_auth, options.from, authorization)
387+
.sign_authorization(auth_token, thirdweb_auth, options.from, &authorization)
393388
.await
394389
.map_err(|e| {
395390
tracing::error!("Error signing authorization with EOA (IAW): {:?}", e);

core/src/transaction.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ pub struct InnerTransaction {
2323
/// Gas limit for the transaction
2424
/// If not provided, engine will estimate the gas limit
2525
#[schema(value_type = Option<u64>)]
26-
#[serde(default, rename = "gasLimit")]
26+
#[serde(default, rename = "gasLimit", skip_serializing_if = "Option::is_none")]
2727
pub gas_limit: Option<u64>,
2828

2929
/// Transaction type-specific data for different EIP standards
@@ -33,7 +33,7 @@ pub struct InnerTransaction {
3333
/// Depending on the execution mode chosen, these might be ignored:
3434
///
3535
/// - For ERC4337 execution, all gas fee related fields are ignored. Sending signed authorizations is also not supported.
36-
#[serde(flatten)]
36+
#[serde(flatten, default, skip_serializing_if = "Option::is_none")]
3737
pub transaction_type_data: Option<TransactionTypeData>,
3838
}
3939

@@ -58,14 +58,17 @@ pub struct Transaction7702Data {
5858
/// List of signed authorizations for contract delegation
5959
/// Each authorization allows the EOA to temporarily delegate to a smart contract
6060
#[schema(value_type = Option<Vec<SignedAuthorizationSchema>>)]
61+
#[serde(default, skip_serializing_if = "Option::is_none")]
6162
pub authorization_list: Option<Vec<SignedAuthorization>>,
6263

6364
/// Maximum fee per gas willing to pay (in wei)
6465
/// This is the total fee cap including base fee and priority fee
66+
#[serde(default, skip_serializing_if = "Option::is_none")]
6567
pub max_fee_per_gas: Option<u128>,
6668

6769
/// Maximum priority fee per gas willing to pay (in wei)
6870
/// This is the tip paid to validators for transaction inclusion
71+
#[serde(default, skip_serializing_if = "Option::is_none")]
6972
pub max_priority_fee_per_gas: Option<u128>,
7073
}
7174

@@ -77,10 +80,12 @@ pub struct Transaction7702Data {
7780
pub struct Transaction1559Data {
7881
/// Maximum fee per gas willing to pay (in wei)
7982
/// This is the total fee cap including base fee and priority fee
83+
#[serde(default, skip_serializing_if = "Option::is_none")]
8084
pub max_fee_per_gas: Option<u128>,
8185

8286
/// Maximum priority fee per gas willing to pay (in wei)
8387
/// This is the tip paid to validators for transaction inclusion
88+
#[serde(default, skip_serializing_if = "Option::is_none")]
8489
pub max_priority_fee_per_gas: Option<u128>,
8590
}
8691

@@ -92,5 +97,6 @@ pub struct Transaction1559Data {
9297
pub struct TransactionLegacyData {
9398
/// Gas price willing to pay (in wei)
9499
/// This is the total price per unit of gas for legacy transactions
100+
#[serde(default, skip_serializing_if = "Option::is_none")]
95101
pub gas_price: Option<u128>,
96102
}

executors/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ edition = "2024"
66
[dependencies]
77
hex = "0.4.3"
88
alloy = { version = "1.0.8", features = ["serde"] }
9+
thirdweb-core = { version = "0.1.0", path = "../thirdweb-core" }
910
hmac = "0.12.1"
1011
reqwest = "0.12.15"
1112
serde = "1.0.219"

executors/src/eip7702_executor/confirm.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,12 @@ pub struct Eip7702ConfirmationJobData {
3232
pub bundler_transaction_id: String,
3333
pub eoa_address: Address,
3434
pub rpc_credentials: RpcCredentials,
35-
pub webhook_options: Option<Vec<WebhookOptions>>,
35+
#[serde(default)]
36+
pub webhook_options: Vec<WebhookOptions>,
3637
}
3738

3839
impl HasWebhookOptions for Eip7702ConfirmationJobData {
39-
fn webhook_options(&self) -> Option<Vec<WebhookOptions>> {
40+
fn webhook_options(&self) -> Vec<WebhookOptions> {
4041
self.webhook_options.clone()
4142
}
4243
}

executors/src/eip7702_executor/send.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -46,13 +46,14 @@ pub struct Eip7702SendJobData {
4646
pub transactions: Vec<InnerTransaction>,
4747
pub eoa_address: Address,
4848
pub signing_credential: SigningCredential,
49-
pub webhook_options: Option<Vec<WebhookOptions>>,
49+
#[serde(default)]
50+
pub webhook_options: Vec<WebhookOptions>,
5051
pub rpc_credentials: RpcCredentials,
5152
pub nonce: Option<U256>,
5253
}
5354

5455
impl HasWebhookOptions for Eip7702SendJobData {
55-
fn webhook_options(&self) -> Option<Vec<WebhookOptions>> {
56+
fn webhook_options(&self) -> Vec<WebhookOptions> {
5657
self.webhook_options.clone()
5758
}
5859
}
@@ -243,7 +244,7 @@ where
243244
.sign_typed_data(
244245
signing_options.clone(),
245246
&typed_data,
246-
job_data.signing_credential.clone(),
247+
&job_data.signing_credential,
247248
)
248249
.await
249250
.map_err(|e| Eip7702SendError::SigningError {
@@ -272,7 +273,7 @@ where
272273
job_data.chain_id,
273274
MINIMAL_ACCOUNT_IMPLEMENTATION_ADDRESS,
274275
nonce,
275-
job_data.signing_credential.clone(),
276+
&job_data.signing_credential,
276277
)
277278
.await
278279
.map_err(|e| Eip7702SendError::SigningError {

0 commit comments

Comments
 (0)