Skip to content

Commit 3e5ec41

Browse files
committed
shoveling complexity upwards
1 parent 812c999 commit 3e5ec41

2 files changed

Lines changed: 207 additions & 8 deletions

File tree

src/hyperwallet_client/api.rs

Lines changed: 201 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -379,20 +379,82 @@ pub fn build_and_sign_user_operation_for_payment(
379379
"call_data": call_data,
380380
"use_paymaster": use_paymaster,
381381
});
382-
382+
383383
if let Some(v) = value {
384384
params["value"] = serde_json::Value::String(v.to_string());
385385
}
386-
386+
387387
if let Some(pwd) = password {
388388
params["password"] = serde_json::Value::String(pwd.to_string());
389389
}
390-
390+
391391
// Pass metadata through to hyperwallet
392392
if let Some(meta) = metadata {
393393
params["metadata"] = serde_json::Value::Object(meta);
394394
}
395+
396+
let request = build_request(
397+
our,
398+
session_info,
399+
Operation::BuildAndSignUserOperationForPayment,
400+
params,
401+
Some(wallet_id.to_string()),
402+
chain_id,
403+
);
404+
let response = execute_request(request, our)?;
405+
Ok(response.data.unwrap_or_default())
406+
}
395407

408+
/// High-level convenience function for building and signing UserOperations with TBA support.
409+
/// This mirrors your current build_and_sign_user_operation function.
410+
pub fn build_and_sign_user_operation(
411+
our: &Address,
412+
session_info: &SessionInfo,
413+
wallet_id: &str,
414+
target: &str,
415+
call_data: &str,
416+
value: Option<&str>,
417+
use_paymaster: bool,
418+
tba_address: Option<&str>,
419+
password: Option<&str>,
420+
chain_id: Option<u64>,
421+
) -> Result<serde_json::Value, HyperwalletClientError> {
422+
let mut params = serde_json::json!({
423+
"target": target,
424+
"call_data": call_data,
425+
"use_paymaster": use_paymaster,
426+
});
427+
428+
if let Some(v) = value {
429+
params["value"] = serde_json::Value::String(v.to_string());
430+
}
431+
432+
if let Some(pwd) = password {
433+
params["password"] = serde_json::Value::String(pwd.to_string());
434+
}
435+
436+
// Create metadata with Circle paymaster configuration if using paymaster
437+
if use_paymaster {
438+
let mut metadata = serde_json::Map::new();
439+
440+
// Always add Circle paymaster metadata for gasless transactions
441+
// These constants should be defined somewhere accessible
442+
metadata.insert("paymaster_address".to_string(),
443+
serde_json::json!("0x2Ac3c1d3e24b45c6C310534Bc2Dd84B5ed576335")); // Base Circle paymaster
444+
metadata.insert("is_circle_paymaster".to_string(), serde_json::json!(true));
445+
metadata.insert("paymaster_verification_gas".to_string(),
446+
serde_json::json!("0x30000")); // 196608
447+
metadata.insert("paymaster_post_op_gas".to_string(),
448+
serde_json::json!("0x20000")); // 131072
449+
450+
// Add TBA address if provided - tells hyperwallet to use TBA as sender
451+
if let Some(tba) = tba_address {
452+
metadata.insert("tba_address".to_string(), serde_json::json!(tba));
453+
}
454+
455+
params["metadata"] = serde_json::Value::Object(metadata);
456+
}
457+
396458
let request = build_request(
397459
our,
398460
session_info,
@@ -461,6 +523,142 @@ pub fn get_user_operation_receipt(
461523
Ok(response.data.unwrap_or_default())
462524
}
463525

526+
/// High-level convenience function that executes a complete payment flow.
527+
/// This function handles: session management, building UserOp, submitting, and waiting for receipt.
528+
pub fn execute_gasless_payment(
529+
our: &Address,
530+
wallet_id: &str,
531+
target: &str,
532+
call_data: &str,
533+
value: Option<&str>,
534+
tba_address: Option<&str>,
535+
password: Option<&str>,
536+
chain_id: Option<u64>,
537+
) -> Result<serde_json::Value, HyperwalletClientError> {
538+
// Step 1: Initialize session if needed (you might want to cache this)
539+
let session = super::initialize(our, super::HandshakeConfig::new())?;
540+
541+
// Step 2: Build and sign UserOperation
542+
let signed_data = build_and_sign_user_operation(
543+
our,
544+
&session,
545+
wallet_id,
546+
target,
547+
call_data,
548+
value,
549+
true, // Always use paymaster for gasless
550+
tba_address,
551+
password,
552+
chain_id,
553+
)?;
554+
555+
// Step 3: Extract signed UserOperation and entry point
556+
let signed_user_op = signed_data.get("signed_user_operation")
557+
.ok_or_else(|| HyperwalletClientError::ServerError(
558+
super::types::OperationError::internal_error("Missing signed_user_operation in response")
559+
))?
560+
.clone();
561+
562+
let entry_point = signed_data.get("entry_point")
563+
.and_then(|e| e.as_str())
564+
.ok_or_else(|| HyperwalletClientError::ServerError(
565+
super::types::OperationError::internal_error("Missing entry_point in response")
566+
))?;
567+
568+
// Step 4: Submit UserOperation
569+
let user_op_hash = submit_user_operation(
570+
our,
571+
&session,
572+
signed_user_op,
573+
entry_point,
574+
None, // Use default bundler
575+
chain_id,
576+
)?;
577+
578+
// Step 5: Get receipt (you might want to add polling with timeout)
579+
let receipt = get_user_operation_receipt(our, &session, &user_op_hash, chain_id)?;
580+
581+
Ok(receipt)
582+
}
583+
584+
/// Simplified gasless payment function - abstracts away all complexity.
585+
/// This is what the operator should actually use.
586+
pub fn build_and_sign_gasless_payment(
587+
our: &Address,
588+
session_info: &SessionInfo,
589+
signer_wallet_id: &str,
590+
tba_address: &str,
591+
call_data: &str,
592+
chain_id: Option<u64>,
593+
) -> Result<serde_json::Value, HyperwalletClientError> {
594+
let params = serde_json::json!({
595+
"target": tba_address,
596+
"call_data": call_data,
597+
"use_paymaster": true, // Always gasless
598+
"metadata": {
599+
"tba_address": tba_address,
600+
"is_circle_paymaster": true,
601+
"paymaster_address": "0x0578cFB241215b77442a541325d6A4E6dFE700Ec",
602+
"paymaster_verification_gas": "0x7a120",
603+
"paymaster_post_op_gas": "0x493e0"
604+
}
605+
});
606+
607+
let request = build_request(
608+
our,
609+
session_info,
610+
Operation::BuildAndSignUserOperationForPayment,
611+
params,
612+
Some(signer_wallet_id.to_string()),
613+
chain_id,
614+
);
615+
let response = execute_request(request, our)?;
616+
Ok(response.data.unwrap_or_default())
617+
}
618+
619+
/// Simplified submit that extracts entry point automatically from build response.
620+
pub fn submit_gasless_payment(
621+
our: &Address,
622+
session_info: &SessionInfo,
623+
signed_user_op_response: serde_json::Value,
624+
chain_id: Option<u64>,
625+
) -> Result<String, HyperwalletClientError> {
626+
// Extract signed UserOperation and entry point from the build response
627+
let signed_user_op = signed_user_op_response.get("signed_user_operation")
628+
.ok_or_else(|| HyperwalletClientError::ServerError(
629+
super::types::OperationError::internal_error("Missing signed_user_operation in response")
630+
))?
631+
.clone();
632+
633+
let entry_point = signed_user_op_response.get("entry_point")
634+
.and_then(|e| e.as_str())
635+
.ok_or_else(|| HyperwalletClientError::ServerError(
636+
super::types::OperationError::internal_error("Missing entry_point in response")
637+
))?;
638+
639+
// Submit using the extracted data
640+
submit_user_operation(our, session_info, signed_user_op, entry_point, None, chain_id)
641+
}
642+
643+
/// Get receipt with proper transaction hash extraction.
644+
pub fn get_payment_receipt(
645+
our: &Address,
646+
session_info: &SessionInfo,
647+
user_op_hash: &str,
648+
chain_id: Option<u64>,
649+
) -> Result<(String, serde_json::Value), HyperwalletClientError> {
650+
let receipt = get_user_operation_receipt(our, session_info, user_op_hash, chain_id)?;
651+
652+
// Extract transaction hash if available
653+
let tx_hash = receipt.get("receipt")
654+
.and_then(|r| r.get("transactionHash"))
655+
.and_then(|h| h.as_str())
656+
.unwrap_or(user_op_hash) // Fallback to user op hash
657+
.to_string();
658+
659+
Ok((tx_hash, receipt))
660+
}
661+
464662
/// Resolves an identity name to an address via Hypermap.
465663
pub fn resolve_identity(
466664
our: &Address,

src/hyperwallet_client/mod.rs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,12 @@ pub mod api;
1111
pub mod types;
1212

1313
pub use api::{
14-
approve_token, build_and_sign_user_operation_for_payment, check_tba_ownership, create_note,
15-
create_wallet, delete_wallet, execute_via_tba, get_balance, get_token_balance,
16-
get_user_operation_receipt, get_wallet_info, import_wallet, list_wallets, rename_wallet,
17-
resolve_identity, send_eth, send_token, set_wallet_limits, submit_user_operation,
18-
unlock_wallet,
14+
approve_token, build_and_sign_gasless_payment, build_and_sign_user_operation,
15+
build_and_sign_user_operation_for_payment, check_tba_ownership, create_note, create_wallet,
16+
delete_wallet, execute_gasless_payment, execute_via_tba, get_balance, get_payment_receipt,
17+
get_token_balance, get_user_operation_receipt, get_wallet_info, import_wallet, list_wallets,
18+
rename_wallet, resolve_identity, send_eth, send_token, set_wallet_limits, submit_gasless_payment,
19+
submit_user_operation, unlock_wallet,
1920
};
2021
pub use types::{
2122
Balance, HandshakeConfig, Operation, OperationCategory, OperationError, ProcessPermissions,

0 commit comments

Comments
 (0)