11// Sign Typed Data Operations
22
3- use alloy:: { dyn_abi:: TypedData , primitives :: Address } ;
3+ use alloy:: dyn_abi:: TypedData ;
44use axum:: {
55 extract:: State ,
66 http:: StatusCode ,
77 response:: { IntoResponse , Json } ,
88} ;
9+ use engine_aa_core:: signer:: SmartAccountSignerBuilder ;
910use engine_core:: {
10- error:: EngineError ,
11- signer:: { EoaSigner , SigningOptions , SmartAccountSigningOptions } ,
11+ chain:: ChainService ,
1212 credentials:: SigningCredential ,
13+ defs:: { AddressDef , U256Def } ,
14+ error:: EngineError ,
15+ signer:: { AccountSigner , SigningOptions } ,
1316} ;
14- use engine_aa_core:: signer:: { SmartAccountSigner , SmartAccountSignerBuilder } ;
1517use futures:: future:: join_all;
1618use schemars:: JsonSchema ;
1719use serde:: { Deserialize , Serialize } ;
18- use thirdweb_core :: auth :: ThirdwebAuth ;
20+ use serde_json :: Value ;
1921
2022use crate :: http:: {
2123 error:: ApiEngineError ,
2224 extractors:: { EngineJson , SigningCredentialsExtractor } ,
2325 server:: EngineServerState ,
24- types:: ErrorResponse ,
2526} ;
2627
2728// ===== REQUEST/RESPONSE TYPES =====
@@ -36,15 +37,55 @@ pub struct SignOptions {
3637}
3738
3839/// Request to sign typed data
39- #[ derive( Debug , Clone , Serialize , Deserialize , JsonSchema , utoipa:: ToSchema ) ]
40+ #[ derive( Debug , Clone , Serialize , Deserialize , utoipa:: ToSchema ) ]
4041#[ serde( rename_all = "camelCase" ) ]
4142pub struct SignTypedDataRequest {
4243 /// Configuration options for signing
4344 pub sign_options : SignOptions ,
4445 /// List of typed data to sign
46+ #[ schema( value_type = Vec <TypedDataDef >) ]
4547 pub params : Vec < TypedData > ,
4648}
4749
50+ #[ derive( utoipa:: ToSchema ) ]
51+ pub struct TypedDataDef {
52+ /// Signing domain metadata. The signing domain is the intended context for
53+ /// the signature (e.g. the dapp, protocol, etc. that it's intended for).
54+ /// This data is used to construct the domain separator of the message.
55+ pub domain : TypedDataDomainDef ,
56+
57+ /// The custom types used by this message.
58+ #[ schema( rename = "types" ) ]
59+ pub resolver : Value ,
60+
61+ /// The type of the message.
62+ #[ schema( rename = "primaryType" ) ]
63+ pub primary_type : String ,
64+
65+ /// The message to be signed.
66+ pub message : serde_json:: Value ,
67+ }
68+
69+ #[ derive( utoipa:: ToSchema ) ]
70+ pub struct TypedDataDomainDef {
71+ pub name : Option < String > ,
72+
73+ /// The current major version of the signing domain. Signatures from
74+ /// different versions are not compatible.
75+ pub version : Option < String > ,
76+
77+ /// The EIP-155 chain ID. The user-agent should refuse signing if it does
78+ /// not match the currently active chain.
79+ pub chain_id : Option < U256Def > ,
80+
81+ /// The address of the contract that will verify the signature.
82+ pub verifying_contract : Option < AddressDef > ,
83+
84+ /// A disambiguating salt for the protocol. This can be used as a domain
85+ /// separator of last resort.
86+ pub salt : Option < String > ,
87+ }
88+
4889/// Result of a single typed data signing operation
4990#[ derive( Debug , Clone , Serialize , Deserialize , JsonSchema , utoipa:: ToSchema ) ]
5091#[ serde( untagged) ]
@@ -165,7 +206,12 @@ pub async fn sign_typed_data(
165206) -> Result < impl IntoResponse , ApiEngineError > {
166207 // Process all typed data in parallel
167208 let sign_futures = request. params . iter ( ) . map ( |typed_data| {
168- sign_single_typed_data ( & state. userop_signer , & signing_credential, & request. sign_options . signing_options , typed_data)
209+ sign_single_typed_data (
210+ & state,
211+ & signing_credential,
212+ & request. sign_options . signing_options ,
213+ typed_data,
214+ )
169215 } ) ;
170216
171217 let results: Vec < SignResultItem > = join_all ( sign_futures) . await ;
@@ -181,26 +227,53 @@ pub async fn sign_typed_data(
181227// ===== HELPER FUNCTIONS =====
182228
183229async fn sign_single_typed_data (
184- signer : & Signer ,
230+ state : & EngineServerState ,
185231 signing_credential : & SigningCredential ,
186232 signing_options : & SigningOptions ,
187233 typed_data : & TypedData ,
188234) -> SignResultItem {
189- let params = TypedDataSignerParams {
190- credentials : signing_credential. clone ( ) ,
191- typed_data : typed_data. clone ( ) ,
192- signing_options : signing_options. clone ( ) ,
235+ let result = match signing_options {
236+ SigningOptions :: Eoa ( eoa_options) => {
237+ // Direct EOA signing
238+ state
239+ . eoa_signer
240+ . sign_typed_data ( eoa_options. clone ( ) , typed_data, signing_credential. clone ( ) )
241+ . await
242+ }
243+ SigningOptions :: SmartAccount ( smart_account_options) => {
244+ // Smart account signing via builder
245+ match state. chains . get_chain ( smart_account_options. chain_id ) {
246+ Ok ( chain) => {
247+ match SmartAccountSignerBuilder :: new (
248+ state. eoa_signer . clone ( ) ,
249+ signing_credential. clone ( ) ,
250+ smart_account_options. clone ( ) ,
251+ chain,
252+ )
253+ . build ( )
254+ . await
255+ {
256+ Ok ( smart_signer) => smart_signer. sign_typed_data ( typed_data) . await ,
257+ Err ( e) => Err ( e) ,
258+ }
259+ }
260+ Err ( e) => Err ( EngineError :: ValidationError {
261+ message : format ! (
262+ "Failed to get chain {}: {}" ,
263+ smart_account_options. chain_id, e
264+ ) ,
265+ } ) ,
266+ }
267+ }
193268 } ;
194269
195- let result = signer. sign_typed_data ( params) . await ;
196-
197270 match result {
198271 Ok ( signature) => {
199272 // Convert typed data to JSON string for signed_data field
200273 let signed_data = serde_json:: to_string ( typed_data)
201274 . unwrap_or_else ( |_| "Failed to serialize typed data" . to_string ( ) ) ;
202275 SignResultItem :: success ( signature, signed_data)
203- } ,
276+ }
204277 Err ( e) => SignResultItem :: failure ( e) ,
205278 }
206- }
279+ }
0 commit comments