Skip to content

Commit 018d2bc

Browse files
userops
1 parent 70d35ce commit 018d2bc

File tree

15 files changed

+463
-84
lines changed

15 files changed

+463
-84
lines changed

Cargo.lock

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

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
[workspace]
2-
members = ["aa-core", "core", "executors", "server", "thirdweb-core", "twmq"]
2+
members = ["aa-core", "core", "executors", "server", "thirdweb-core", "twmq", "types-core"]
33
resolver = "2"

aa-core/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ edition = "2024"
77
alloy = { version = "1.0.8", features = ["serde"] }
88
tokio = "1.44.2"
99
engine-core = { path = "../core" }
10+
thirdweb-core = { path = "../thirdweb-core" }
1011
vault-types = { version = "0.1.0", git = "ssh://git@github.com/thirdweb-dev/vault.git", branch = "main" }
1112
vault-sdk = { version = "0.1.0", git = "ssh://git@github.com/thirdweb-dev/vault.git", branch = "main" }
1213
serde = "1.0.219"

aa-core/src/userop/builder.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,9 @@ use engine_core::{
1111
credentials::SigningCredential,
1212
error::{AlloyRpcErrorToEngineError, EngineError},
1313
execution_options::aa::{EntrypointAndFactoryDetails, EntrypointVersion},
14-
userop::{UserOpSigner, UserOpSignerParams, UserOpVersion},
14+
userop::{UserOpSigner, UserOpSignerParams},
1515
};
16+
use thirdweb_core::iaw::UserOpVersion;
1617

1718
pub struct UserOpBuilderConfig<'a, C: Chain> {
1819
pub account_address: Address,

core/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ tracing = "0.1.41"
1616
async-nats = "0.40.0"
1717
twmq = { version = "0.1.0", path = "../twmq" }
1818
thirdweb-core = { version = "0.1.0", path = "../thirdweb-core" }
19+
types-core = { path = "../types-core" }
1920
uuid = { version = "1.17.0", features = ["v4"] }
2021
utoipa = { version = "5.4.0", features = ["preserve_order"] }
2122
serde_with = "3.13.0"

core/src/rpc_clients/bundler.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use alloy::transports::{IntoBoxTransport, TransportResult};
66
use serde::{Deserialize, Serialize};
77
use std::collections::HashMap;
88

9-
use crate::userop::UserOpVersion;
9+
use types_core::UserOpVersion;
1010

1111
// Gas buffer added for managed account factories (matches TypeScript)
1212
pub const MANAGED_ACCOUNT_GAS_BUFFER: U256 = U256::from_limbs([21_000, 0, 0, 0]);

core/src/userop.rs

Lines changed: 62 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
use alloy::{
22
hex::FromHex,
33
primitives::{Address, Bytes, ChainId},
4-
rpc::types::{PackedUserOperation, UserOperation},
54
};
6-
use serde::{Deserialize, Serialize};
5+
use thirdweb_core::iaw::IAWClient;
6+
use types_core::UserOpVersion;
77
use vault_sdk::VaultClient;
88
use vault_types::{
99
enclave::encrypted::eoa::StructuredMessageInput,
@@ -12,16 +12,10 @@ use vault_types::{
1212

1313
use crate::{credentials::SigningCredential, error::EngineError};
1414

15-
#[derive(Debug, Clone, Serialize, Deserialize)]
16-
#[serde(untagged)]
17-
pub enum UserOpVersion {
18-
V0_6(UserOperation),
19-
V0_7(PackedUserOperation),
20-
}
21-
2215
#[derive(Clone)]
2316
pub struct UserOpSigner {
2417
pub vault_client: VaultClient,
18+
pub iaw_client: IAWClient,
2519
}
2620

2721
pub struct UserOpSignerParams {
@@ -32,49 +26,47 @@ pub struct UserOpSignerParams {
3226
pub chain_id: ChainId,
3327
}
3428

35-
impl UserOpVersion {
36-
fn to_vault_input(&self, entrypoint: Address) -> StructuredMessageInput {
37-
match self {
38-
UserOpVersion::V0_6(userop) => {
39-
StructuredMessageInput::UserOperationV06Input(UserOperationV06Input {
40-
call_data: userop.call_data.clone(),
41-
init_code: userop.init_code.clone(),
42-
nonce: userop.nonce,
43-
pre_verification_gas: userop.pre_verification_gas,
44-
max_fee_per_gas: userop.max_fee_per_gas,
45-
verification_gas_limit: userop.verification_gas_limit,
46-
sender: userop.sender,
47-
paymaster_and_data: userop.paymaster_and_data.clone(),
48-
signature: userop.signature.clone(),
49-
call_gas_limit: userop.call_gas_limit,
50-
max_priority_fee_per_gas: userop.max_priority_fee_per_gas,
51-
entrypoint,
52-
})
53-
}
54-
UserOpVersion::V0_7(userop) => {
55-
StructuredMessageInput::UserOperationV07Input(UserOperationV07Input {
56-
call_data: userop.call_data.clone(),
57-
nonce: userop.nonce,
58-
pre_verification_gas: userop.pre_verification_gas,
59-
max_fee_per_gas: userop.max_fee_per_gas,
60-
verification_gas_limit: userop.verification_gas_limit,
61-
sender: userop.sender,
62-
paymaster_data: userop.paymaster_data.clone().unwrap_or_default(),
63-
factory: userop.factory.unwrap_or_default(),
64-
factory_data: userop.factory_data.clone().unwrap_or_default(),
65-
paymaster_post_op_gas_limit: userop
66-
.paymaster_post_op_gas_limit
67-
.unwrap_or_default(),
68-
paymaster_verification_gas_limit: userop
69-
.paymaster_verification_gas_limit
70-
.unwrap_or_default(),
71-
signature: userop.signature.clone(),
72-
call_gas_limit: userop.call_gas_limit,
73-
max_priority_fee_per_gas: userop.max_priority_fee_per_gas,
74-
paymaster: userop.paymaster.unwrap_or_default(),
75-
entrypoint,
76-
})
77-
}
29+
fn userop_to_vault_input(userop: &UserOpVersion, entrypoint: Address) -> StructuredMessageInput {
30+
match userop {
31+
UserOpVersion::V0_6(userop) => {
32+
StructuredMessageInput::UserOperationV06Input(UserOperationV06Input {
33+
call_data: userop.call_data.clone(),
34+
init_code: userop.init_code.clone(),
35+
nonce: userop.nonce,
36+
pre_verification_gas: userop.pre_verification_gas,
37+
max_fee_per_gas: userop.max_fee_per_gas,
38+
verification_gas_limit: userop.verification_gas_limit,
39+
sender: userop.sender,
40+
paymaster_and_data: userop.paymaster_and_data.clone(),
41+
signature: userop.signature.clone(),
42+
call_gas_limit: userop.call_gas_limit,
43+
max_priority_fee_per_gas: userop.max_priority_fee_per_gas,
44+
entrypoint,
45+
})
46+
}
47+
UserOpVersion::V0_7(userop) => {
48+
StructuredMessageInput::UserOperationV07Input(UserOperationV07Input {
49+
call_data: userop.call_data.clone(),
50+
nonce: userop.nonce,
51+
pre_verification_gas: userop.pre_verification_gas,
52+
max_fee_per_gas: userop.max_fee_per_gas,
53+
verification_gas_limit: userop.verification_gas_limit,
54+
sender: userop.sender,
55+
paymaster_data: userop.paymaster_data.clone().unwrap_or_default(),
56+
factory: userop.factory.unwrap_or_default(),
57+
factory_data: userop.factory_data.clone().unwrap_or_default(),
58+
paymaster_post_op_gas_limit: userop
59+
.paymaster_post_op_gas_limit
60+
.unwrap_or_default(),
61+
paymaster_verification_gas_limit: userop
62+
.paymaster_verification_gas_limit
63+
.unwrap_or_default(),
64+
signature: userop.signature.clone(),
65+
call_gas_limit: userop.call_gas_limit,
66+
max_priority_fee_per_gas: userop.max_priority_fee_per_gas,
67+
paymaster: userop.paymaster.unwrap_or_default(),
68+
entrypoint,
69+
})
7870
}
7971
}
8072
}
@@ -88,7 +80,7 @@ impl UserOpSigner {
8880
.sign_structured_message(
8981
auth_method.clone(),
9082
params.signer_address,
91-
params.userop.to_vault_input(params.entrypoint),
83+
userop_to_vault_input(&params.userop, params.entrypoint),
9284
Some(params.chain_id),
9385
)
9486
.await
@@ -103,11 +95,23 @@ impl UserOpSigner {
10395
}
10496
})?)
10597
}
106-
SigningCredential::Iaw { auth_token: _, thirdweb_auth: _ } => {
107-
// IAW doesn't support UserOp signing yet
108-
Err(EngineError::ValidationError {
109-
message: "IAW service does not support UserOperation signing".to_string(),
110-
})
98+
SigningCredential::Iaw { auth_token, thirdweb_auth } => {
99+
let result = self.iaw_client.sign_userop(
100+
auth_token.clone(),
101+
thirdweb_auth.clone(),
102+
params.userop,
103+
params.entrypoint,
104+
params.signer_address,
105+
params.chain_id,
106+
).await.map_err(|e| EngineError::ValidationError {
107+
message: format!("Failed to sign userop: {}", e),
108+
})?;
109+
110+
Ok(Bytes::from_hex(&result.signature).map_err(|_| {
111+
EngineError::ValidationError {
112+
message: "Bad signature received from IAW".to_string(),
113+
}
114+
})?)
111115
}
112116
}
113117
}

executors/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ tracing = "0.1.41"
1616
twmq = { version = "0.1.0", path = "../twmq" }
1717
engine-core = { version = "0.1.0", path = "../core" }
1818
engine-aa-core = { version = "0.1.0", path = "../aa-core" }
19+
types-core = { path = "../types-core" }
1920
rand = "0.9.1"
2021
uuid = { version = "1.17.0", features = ["v4"] }
2122
chrono = "0.4.41"

executors/src/external_bundler/send.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,9 @@ use engine_core::{
1616
error::{AlloyRpcErrorToEngineError, EngineError, RpcErrorKind},
1717
execution_options::{WebhookOptions, aa::Erc4337ExecutionOptions},
1818
transaction::InnerTransaction,
19-
userop::{UserOpSigner, UserOpVersion},
19+
userop::{UserOpSigner},
2020
};
21+
use types_core::UserOpVersion;
2122
use serde::{Deserialize, Serialize};
2223
use std::{sync::Arc, time::Duration};
2324
use twmq::{

server/src/main.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ async fn main() -> anyhow::Result<()> {
4343

4444
let signer = Arc::new(UserOpSigner {
4545
vault_client: vault_client.clone(),
46+
iaw_client: iaw_client.clone(),
4647
});
4748
let eoa_signer = Arc::new(EoaSigner::new(vault_client, iaw_client));
4849

thirdweb-core/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,4 @@ thiserror = "2.0.12"
1414
tracing = "0.1.41"
1515
url = "2.5.4"
1616
utoipa = "5.4.0"
17+
types-core = { path = "../types-core" }

0 commit comments

Comments
 (0)