diff --git a/.DS_Store b/.DS_Store deleted file mode 100644 index 7b01a1d..0000000 Binary files a/.DS_Store and /dev/null differ diff --git a/.gitignore b/.gitignore index ce26a92..9fbba68 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ target *.swp -.vscode \ No newline at end of file +.vscode +.DS_Store diff --git a/Cargo.lock b/Cargo.lock index 8371f08..7ef55b9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,6 +1,10 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. +<<<<<<< HEAD +version = 3 +======= version = 4 +>>>>>>> hyperwallet-client_lib [[package]] name = "addr2line" @@ -4037,9 +4041,15 @@ dependencies = [ [[package]] name = "zerovec" +<<<<<<< HEAD +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdbb9122ea75b11bf96e7492afb723e8a7fbe12c67417aa95e7e3d18144d37cd" +======= version = "0.11.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e7aa2bd55086f1ab526693ecbe444205da57e25f4489879da80635a46d90e73b" +>>>>>>> hyperwallet-client_lib dependencies = [ "yoke", "zerofrom", diff --git a/Cargo.toml b/Cargo.toml index 628bd81..c91d63c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,6 +10,7 @@ license = "Apache-2.0" [features] logging = ["dep:color-eyre", "dep:tracing", "dep:tracing-error", "dep:tracing-subscriber"] +hyperwallet = [] simulation-mode = [] [dependencies] @@ -25,19 +26,19 @@ alloy = { version = "0.8.1", features = [ "consensus", "network", ] } -hex = "0.4.3" -sha3 = "0.10.8" anyhow = "1.0" base64 = "0.22.1" bincode = "1.3.3" color-eyre = { version = "0.6", features = ["capture-spantrace"], optional = true } +hex = "0.4.3" http = "1.0.0" mime_guess = "2.0" -serde = { version = "1.0", features = ["derive"] } -serde_json = "1.0.120" rand = "0.8" regex = "1.11.1" rmp-serde = "1.1.2" +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0.120" +sha3 = "0.10.8" thiserror = "1.0" tracing = { version = "0.1", optional = true } tracing-error = { version = "0.2", optional = true } diff --git a/hyperware-wit/hyperwallet:sys-v0.wit b/hyperware-wit/hyperwallet:sys-v0.wit new file mode 100644 index 0000000..e5f5048 --- /dev/null +++ b/hyperware-wit/hyperwallet:sys-v0.wit @@ -0,0 +1,651 @@ +interface hyperwallet { + use standard.{address, process-id, node-id}; + + /// JSON is passed over Wasm boundary as a string + type json = string; + + /// Wallet address (Ethereum address) + type wallet-address = string; + + /// Chain ID for blockchain networks + type chain-id = u64; + + /// Session identifier + type session-id = string; + + /// User operation hash + type user-operation-hash = string; + + /// Cryptographic signature as bytes + type signature = list; + + /// Available operations in the Hyperwallet protocol + enum operation { + handshake, + unlock-wallet, + register-process, + update-spending-limits, + create-wallet, + import-wallet, + delete-wallet, + rename-wallet, + export-wallet, + encrypt-wallet, + decrypt-wallet, + get-wallet-info, + list-wallets, + set-wallet-limits, + send-eth, + send-token, + approve-token, + call-contract, + sign-transaction, + sign-message, + execute-via-tba, + check-tba-ownership, + setup-tba-delegation, + build-and-sign-user-operation-for-payment, + submit-user-operation, + build-user-operation, + sign-user-operation, + build-and-sign-user-operation, + estimate-user-operation-gas, + get-user-operation-receipt, + configure-paymaster, + resolve-identity, + create-note, + read-note, + setup-delegation, + verify-delegation, + mint-entry, + get-balance, + get-token-balance, + get-transaction-history, + estimate-gas, + get-gas-price, + get-transaction-receipt, + batch-operations, + schedule-operation, + cancel-operation, + } + + /// Categories of operations + enum operation-category { + system, + process-management, + wallet-management, + ethereum, + token-bound-account, + erc4337, + hypermap, + query, + advanced, + } + + /// Spending limits configuration + record spending-limits { + per-tx-eth: option, + daily-eth: option, + per-tx-usdc: option, + daily-usdc: option, + daily-reset-at: u64, + spent-today-eth: string, + spent-today-usdc: string, + } + + /// Process permissions + record process-permissions { + address: address, + allowed-operations: list, + spending-limits: option, + updatable-settings: list, + registered-at: u64, + } + + /// Settings that can be updated + enum updatable-setting { + spending-limits, + } + + /// Session information + record session-info { + server-version: string, + session-id: session-id, + registered-permissions: process-permissions, + initial-chain-id: chain-id, + } + + /// Handshake protocol steps + variant handshake-step { + client-hello(client-hello), + server-welcome(server-welcome), + register(register-request), + complete(complete-handshake), + } + + record client-hello { + client-version: string, + client-name: string, + } + + record server-welcome { + server-version: string, + supported-operations: list, + supported-chains: list, + features: list, + } + + record register-request { + required-operations: list, + spending-limits: option, + } + + record complete-handshake { + registered-permissions: process-permissions, + session-id: string, + } + + /// Main message structure + record hyperwallet-message { + request: hyperwallet-request, + session-id: session-id, + } + + /// All possible request types + variant hyperwallet-request { + // Session Management + handshake(handshake-step), + unlock-wallet(unlock-wallet-request), + + // Wallet Lifecycle Management + create-wallet(create-wallet-request), + import-wallet(import-wallet-request), + delete-wallet(delete-wallet-request), + rename-wallet(rename-wallet-request), + export-wallet(export-wallet-request), + list-wallets, + get-wallet-info(get-wallet-info-request), + set-wallet-limits(set-wallet-limits-request), + + // Ethereum Operations + send-eth(send-eth-request), + send-token(send-token-request), + approve-token(approve-token-request), + get-balance(get-balance-request), + get-token-balance(get-token-balance-request), + call-contract(call-contract-request), + sign-transaction(sign-transaction-request), + sign-message(sign-message-request), + get-transaction-history(get-transaction-history-request), + estimate-gas(estimate-gas-request), + get-gas-price, + get-transaction-receipt(get-transaction-receipt-request), + + // Token Bound Account Operations + execute-via-tba(execute-via-tba-request), + check-tba-ownership(check-tba-ownership-request), + setup-tba-delegation(setup-tba-delegation-request), + + // Account Abstraction (ERC-4337) + build-and-sign-user-operation-for-payment(build-and-sign-user-operation-for-payment-request), + submit-user-operation(submit-user-operation-request), + get-user-operation-receipt(get-user-operation-receipt-request), + build-user-operation(build-user-operation-request), + sign-user-operation(sign-user-operation-request), + build-and-sign-user-operation(build-and-sign-user-operation-request), + estimate-user-operation-gas(estimate-user-operation-gas-request), + configure-paymaster(configure-paymaster-request), + + // Hypermap Operations + resolve-identity(resolve-identity-request), + create-note(create-note-request), + read-note(read-note-request), + setup-delegation(setup-delegation-request), + verify-delegation(verify-delegation-request), + mint-entry(mint-entry-request), + + // Process Management + update-spending-limits(update-spending-limits-request), + } + + /// Response structure + record hyperwallet-response { + success: bool, + data: option, + error: option, + request-id: option, + } + + /// Error information + record operation-error { + code: error-code, + message: string, + details: option, + } + + /// Error codes + enum error-code { + permission-denied, + wallet-not-found, + insufficient-funds, + invalid-operation, + invalid-params, + spending-limit-exceeded, + chain-not-allowed, + blockchain-error, + internal-error, + authentication-failed, + wallet-locked, + operation-not-supported, + version-mismatch, + } + + /// Response data variants + variant hyperwallet-response-data { + // Session Management + handshake(handshake-step), + unlock-wallet(unlock-wallet-response), + + // Wallet Lifecycle + create-wallet(create-wallet-response), + import-wallet(import-wallet-response), + delete-wallet(delete-wallet-response), + export-wallet(export-wallet-response), + + // Wallet Queries + list-wallets(list-wallets-response), + get-wallet-info(get-wallet-info-response), + get-balance(get-balance-response), + get-token-balance(get-token-balance-response), + set-wallet-limits(set-wallet-limits-response), + + // Transactions + send-eth(send-eth-response), + send-token(send-token-response), + + // ERC4337 Account Abstraction + build-and-sign-user-operation-for-payment(build-and-sign-user-operation-response), + submit-user-operation(submit-user-operation-response), + get-user-operation-receipt(user-operation-receipt-response), + + // Hypermap + create-note(create-note-response), + + // Token Bound Accounts + execute-via-tba(execute-via-tba-response), + check-tba-ownership(check-tba-ownership-response), + } + + // Request types + + record unlock-wallet-request { + session-id: session-id, + wallet-id: string, + password: string, + } + + record create-wallet-request { + name: string, + password: option, + } + + record import-wallet-request { + name: string, + private-key: string, + password: option, + } + + record delete-wallet-request { + wallet-id: string, + } + + record rename-wallet-request { + wallet-id: string, + new-name: string, + } + + record export-wallet-request { + wallet-id: string, + password: option, + } + + record get-wallet-info-request { + wallet-id: string, + } + + /// Set wallet-level spending limits + record set-wallet-limits-request { + wallet-id: string, + limits: wallet-spending-limits, + } + + record get-balance-request { + wallet-id: string, + } + + record send-eth-request { + wallet-id: string, + to: string, + amount: string, + } + + record send-token-request { + wallet-id: string, + token-address: string, + to: string, + amount: string, + } + + record approve-token-request { + token-address: string, + spender: string, + amount: string, + } + + record get-token-balance-request { + wallet-id: string, + token-address: string, + } + + record call-contract-request { + to: string, + data: string, + value: option, + } + + record sign-transaction-request { + to: string, + value: string, + data: option, + gas-limit: option, + gas-price: option, + nonce: option, + } + + record sign-message-request { + message: string, + message-type: message-type, + } + + variant message-type { + plain-text, + eip191, + eip712(eip712-data), + } + + record eip712-data { + domain: json, + types: json, + } + + record get-transaction-history-request { + limit: option, + offset: option, + from-block: option, + to-block: option, + } + + record estimate-gas-request { + to: string, + data: option, + value: option, + } + + record get-transaction-receipt-request { + tx-hash: string, + } + + record execute-via-tba-request { + tba-address: string, + target: string, + call-data: string, + value: option, + } + + record check-tba-ownership-request { + tba-address: string, + signer-address: string, + } + + record setup-tba-delegation-request { + tba-address: string, + delegate-address: string, + permissions: list, + } + + record build-and-sign-user-operation-for-payment-request { + eoa-wallet-id: string, + tba-address: string, + target: string, + call-data: string, + use-paymaster: bool, + paymaster-config: option, + password: option, + } + + record paymaster-config { + is-circle-paymaster: bool, + paymaster-address: string, + paymaster-verification-gas: string, + paymaster-post-op-gas: string, + } + + record submit-user-operation-request { + signed-user-operation: json, + entry-point: string, + bundler-url: option, + } + + record get-user-operation-receipt-request { + user-op-hash: string, + } + + record build-user-operation-request { + target: string, + call-data: string, + value: option, + } + + record sign-user-operation-request { + unsigned-user-operation: json, + entry-point: string, + } + + record build-and-sign-user-operation-request { + target: string, + call-data: string, + value: option, + entry-point: string, + } + + record estimate-user-operation-gas-request { + user-operation: json, + entry-point: string, + } + + record configure-paymaster-request { + paymaster-address: string, + paymaster-data: option, + verification-gas-limit: string, + post-op-gas-limit: string, + } + + record resolve-identity-request { + entry-name: string, + } + + record create-note-request { + note-data: json, + metadata: option, + } + + record read-note-request { + note-id: string, + } + + record setup-delegation-request { + delegate-address: string, + permissions: list, + expiry: option, + } + + record verify-delegation-request { + delegate-address: string, + signature: string, + message: string, + } + + record mint-entry-request { + entry-name: string, + metadata: json, + } + + record update-spending-limits-request { + new-limits: spending-limits, + } + + // Response types + + record unlock-wallet-response { + success: bool, + wallet-id: string, + message: string, + } + + record create-wallet-response { + wallet-id: string, + address: string, + name: string, + } + + record import-wallet-response { + wallet-id: string, + address: string, + name: string, + } + + record delete-wallet-response { + success: bool, + wallet-id: string, + message: string, + } + + record export-wallet-response { + address: string, + private-key: string, + } + + record get-wallet-info-response { + wallet-id: string, + address: string, + name: string, + chain-id: chain-id, + is-locked: bool, + } + + record list-wallets-response { + process: string, + wallets: list, + total: u64, + } + + /// Response for setting wallet limits + record set-wallet-limits-response { + success: bool, + wallet-id: string, + message: string, + } + + record wallet { + address: wallet-address, + name: option, + chain-id: chain-id, + encrypted: bool, + created-at: option, + last-used: option, + spending-limits: option, + } + + record wallet-spending-limits { + max-per-call: option, + max-total: option, + currency: string, + total-spent: string, + set-at: option, + updated-at: option, + } + + record get-balance-response { + balance: balance, + wallet-id: string, + chain-id: chain-id, + } + + record balance { + formatted: string, + raw: string, + } + + record get-token-balance-response { + balance: string, + formatted: option, + decimals: option, + } + + record send-eth-response { + tx-hash: string, + from-address: string, + to-address: string, + amount: string, + chain-id: chain-id, + } + + record send-token-response { + tx-hash: string, + from-address: string, + to-address: string, + token-address: string, + amount: string, + chain-id: chain-id, + } + + record build-and-sign-user-operation-response { + signed-user-operation: json, + entry-point: string, + ready-to-submit: bool, + } + + record submit-user-operation-response { + user-op-hash: string, + } + + record user-operation-receipt-response { + receipt: option, + user-op-hash: string, + status: string, + } + + record create-note-response { + note-id: string, + content-hash: string, + created-at: u64, + } + + record execute-via-tba-response { + tx-hash: string, + tba-address: string, + target-address: string, + success: bool, + } + + record check-tba-ownership-response { + tba-address: string, + owner-address: string, + is-owned: bool, + } +} + +world hyperwallet-sys-v0 { + import hyperwallet; + include process-v1; +} diff --git a/hyperware-wit/process-lib.wit b/hyperware-wit/process-lib.wit index 834f96b..c85f693 100644 --- a/hyperware-wit/process-lib.wit +++ b/hyperware-wit/process-lib.wit @@ -1,5 +1,6 @@ world process-lib { import sign; import hypermap-cacher; + import hyperwallet; include lib; } diff --git a/src/.DS_Store b/src/.DS_Store deleted file mode 100644 index 5e14423..0000000 Binary files a/src/.DS_Store and /dev/null differ diff --git a/src/hyperwallet_client/api.rs b/src/hyperwallet_client/api.rs index 11a2602..0496921 100644 --- a/src/hyperwallet_client/api.rs +++ b/src/hyperwallet_client/api.rs @@ -1,10 +1,9 @@ use super::types::{ self, Balance, BuildAndSignUserOperationForPaymentRequest, BuildAndSignUserOperationResponse, CreateWalletRequest, ExportWalletResponse, GetTokenBalanceResponse, HyperwalletMessage, - HyperwalletRequest, HyperwalletResponse, ImportWalletRequest, ListWalletsResponse, - PaymasterConfig, RenameWalletRequest, SendEthRequest, SendTokenRequest, SessionId, - SubmitUserOperationResponse, TxReceipt, UnlockWalletRequest, UserOperationReceiptResponse, - Wallet, + HyperwalletRequest, ImportWalletRequest, ListWalletsResponse, PaymasterConfig, + RenameWalletRequest, SendEthRequest, SendTokenRequest, SessionId, SetWalletLimitsRequest, + SetWalletLimitsResponse, TxReceipt, UnlockWalletRequest, UserOperationReceiptResponse, Wallet, }; use super::HyperwalletClientError; use crate::wallet; @@ -15,21 +14,31 @@ pub fn create_wallet( name: Option<&str>, password: Option<&str>, ) -> Result { - let request = build_request( + let message = build_message( session_id, - CreateWalletRequest { + HyperwalletRequest::CreateWallet(CreateWalletRequest { name: name.map(|s| s.to_string()).unwrap_or_default(), password: password.map(|s| s.to_string()), - }, + }), ); - let message = HyperwalletMessage::CreateWallet(request); - let response: HyperwalletResponse = super::send_message(message)?; - response.data.ok_or_else(|| { - HyperwalletClientError::ServerError(types::OperationError::internal_error( - "Missing wallet data in response", - )) - }) + let response = super::send_message(message)?; + match response.data { + Some(types::HyperwalletResponseData::CreateWallet(wallet_response)) => { + Ok(types::Wallet { + address: wallet_response.address, + name: Some(wallet_response.name), + chain_id: 8453, // Base mainnet - TODO: get from response + encrypted: password.is_some(), + created_at: None, + last_used: None, + spending_limits: None, + }) + } + _ => Err(HyperwalletClientError::ServerError( + types::OperationError::internal_error("Missing or invalid wallet data in response"), + )), + } } pub fn import_wallet( @@ -38,22 +47,32 @@ pub fn import_wallet( private_key: &str, password: Option<&str>, ) -> Result { - let request = build_request( + let message = build_message( session_id, - ImportWalletRequest { + HyperwalletRequest::ImportWallet(ImportWalletRequest { name: name.to_string(), private_key: private_key.to_string(), password: password.map(|s| s.to_string()), - }, + }), ); - let message = HyperwalletMessage::ImportWallet(request); - let response: HyperwalletResponse = super::send_message(message)?; - response.data.ok_or_else(|| { - HyperwalletClientError::ServerError(types::OperationError::internal_error( - "Missing wallet data in response", - )) - }) + let response = super::send_message(message)?; + match response.data { + Some(types::HyperwalletResponseData::ImportWallet(wallet_response)) => { + Ok(types::Wallet { + address: wallet_response.address, + name: Some(wallet_response.name), + chain_id: 8453, // Base mainnet - TODO: get from response + encrypted: password.is_some(), + created_at: None, + last_used: None, + spending_limits: None, + }) + } + _ => Err(HyperwalletClientError::ServerError( + types::OperationError::internal_error("Missing or invalid wallet data in response"), + )), + } } pub fn unlock_wallet( @@ -62,68 +81,82 @@ pub fn unlock_wallet( wallet_id: &str, password: &str, ) -> Result<(), HyperwalletClientError> { - let request = build_request( + let message = build_message( session_id, - UnlockWalletRequest { + HyperwalletRequest::UnlockWallet(UnlockWalletRequest { session_id: target_session_id.to_string(), wallet_id: wallet_id.to_string(), password: password.to_string(), - }, + }), ); - let message = HyperwalletMessage::UnlockWallet(request); - let _response: HyperwalletResponse<()> = super::send_message(message)?; - Ok(()) + let response = super::send_message(message)?; + match response.data { + Some(types::HyperwalletResponseData::UnlockWallet(_)) => Ok(()), + _ => Err(HyperwalletClientError::ServerError( + types::OperationError::internal_error("Failed to unlock wallet"), + )), + } } pub fn list_wallets(session_id: &SessionId) -> Result { - let request = build_request(session_id, ()); - - let message = HyperwalletMessage::ListWallets(request); - let response: HyperwalletResponse = super::send_message(message)?; - let list_response = response.data.ok_or_else(|| { - HyperwalletClientError::ServerError(types::OperationError::internal_error( - "Missing wallet list in response", - )) - })?; - - Ok(list_response) + let message = build_message(session_id, HyperwalletRequest::ListWallets); + + let response = super::send_message(message)?; + match response.data { + Some(types::HyperwalletResponseData::ListWallets(list_response)) => Ok(list_response), + _ => Err(HyperwalletClientError::ServerError( + types::OperationError::internal_error("Missing or invalid wallet list in response"), + )), + } } pub fn get_wallet_info( session_id: &SessionId, wallet_id: &str, ) -> Result { - let request = build_request( + let message = build_message( session_id, - types::GetWalletInfoRequest { + HyperwalletRequest::GetWalletInfo(types::GetWalletInfoRequest { wallet_id: wallet_id.to_string(), - }, + }), ); - let message = HyperwalletMessage::GetWalletInfo(request); - let response: HyperwalletResponse = super::send_message(message)?; - response.data.ok_or_else(|| { - HyperwalletClientError::ServerError(types::OperationError::internal_error( - "Missing wallet data in response", - )) - }) + let response = super::send_message(message)?; + match response.data { + Some(types::HyperwalletResponseData::GetWalletInfo(info_response)) => Ok(types::Wallet { + address: info_response.address, + name: Some(info_response.name), + chain_id: info_response.chain_id, + encrypted: info_response.is_locked, + created_at: None, + last_used: None, + spending_limits: None, + }), + _ => Err(HyperwalletClientError::ServerError( + types::OperationError::internal_error("Missing or invalid wallet data in response"), + )), + } } pub fn delete_wallet( session_id: &SessionId, wallet_id: &str, ) -> Result<(), HyperwalletClientError> { - let request = build_request( + let message = build_message( session_id, - types::DeleteWalletRequest { + HyperwalletRequest::DeleteWallet(types::DeleteWalletRequest { wallet_id: wallet_id.to_string(), - }, + }), ); - let message = HyperwalletMessage::DeleteWallet(request); - let _response: HyperwalletResponse<()> = super::send_message(message)?; - Ok(()) + let response = super::send_message(message)?; + match response.data { + Some(types::HyperwalletResponseData::DeleteWallet(_)) => Ok(()), + _ => Err(HyperwalletClientError::ServerError( + types::OperationError::internal_error("Failed to delete wallet"), + )), + } } pub fn export_wallet( @@ -131,21 +164,21 @@ pub fn export_wallet( wallet_id: &str, password: Option<&str>, ) -> Result { - let request = build_request( + let message = build_message( session_id, - types::ExportWalletRequest { + HyperwalletRequest::ExportWallet(types::ExportWalletRequest { wallet_id: wallet_id.to_string(), password: password.map(|s| s.to_string()), - }, + }), ); - let message = HyperwalletMessage::ExportWallet(request); - let response: HyperwalletResponse = super::send_message(message)?; - response.data.ok_or_else(|| { - HyperwalletClientError::ServerError(types::OperationError::internal_error( - "Missing export data in response", - )) - }) + let response = super::send_message(message)?; + match response.data { + Some(types::HyperwalletResponseData::ExportWallet(export_response)) => Ok(export_response), + _ => Err(HyperwalletClientError::ServerError( + types::OperationError::internal_error("Missing export data in response"), + )), + } } pub fn rename_wallet( @@ -153,16 +186,46 @@ pub fn rename_wallet( wallet_id: &str, new_name: &str, ) -> Result<(), HyperwalletClientError> { - let request = build_request( + let message = build_message( session_id, - RenameWalletRequest { + HyperwalletRequest::RenameWallet(RenameWalletRequest { wallet_id: wallet_id.to_string(), new_name: new_name.to_string(), - }, + }), ); - let message = HyperwalletMessage::RenameWallet(request); - let _response: HyperwalletResponse<()> = super::send_message(message)?; - Ok(()) + let response = super::send_message(message)?; + // RenameWallet doesn't have a response variant in the enum, so we just check for success + if response.error.is_none() { + Ok(()) + } else { + Err(HyperwalletClientError::ServerError( + response.error.unwrap_or_else(|| { + types::OperationError::internal_error("Failed to rename wallet") + }), + )) + } +} + +pub fn set_wallet_limits( + session_id: &SessionId, + wallet_id: &str, + limits: types::WalletSpendingLimits, +) -> Result { + let message = build_message( + session_id, + HyperwalletRequest::SetWalletLimits(SetWalletLimitsRequest { + wallet_id: wallet_id.to_string(), + limits, + }), + ); + + let response = super::send_message(message)?; + match response.data { + Some(types::HyperwalletResponseData::SetWalletLimits(resp)) => Ok(resp), + _ => Err(HyperwalletClientError::ServerError( + types::OperationError::internal_error("Missing SetWalletLimits response data"), + )), + } } // === TRANSACTIONS === @@ -173,22 +236,30 @@ pub fn send_eth( to_address: &str, amount_eth: &str, ) -> Result { - let request = build_request( + let message = build_message( session_id, - SendEthRequest { + HyperwalletRequest::SendEth(SendEthRequest { wallet_id: wallet_id.to_string(), to: to_address.to_string(), amount: amount_eth.to_string(), - }, + }), ); - let message = HyperwalletMessage::SendEth(request); - let response: HyperwalletResponse = super::send_message(message)?; - response.data.ok_or_else(|| { - HyperwalletClientError::ServerError(types::OperationError::internal_error( - "Missing transaction receipt in response", - )) - }) + let response = super::send_message(message)?; + match response.data { + Some(types::HyperwalletResponseData::SendEth(send_response)) => Ok(TxReceipt { + hash: send_response.tx_hash, + details: serde_json::json!({ + "from": send_response.from_address, + "to": send_response.to_address, + "amount": send_response.amount, + "chain_id": send_response.chain_id, + }), + }), + _ => Err(HyperwalletClientError::ServerError( + types::OperationError::internal_error("Missing transaction receipt in response"), + )), + } } pub fn send_token( @@ -198,23 +269,32 @@ pub fn send_token( to_address: &str, amount: &str, ) -> Result { - let request = build_request( + let message = build_message( session_id, - SendTokenRequest { + HyperwalletRequest::SendToken(SendTokenRequest { wallet_id: wallet_id.to_string(), token_address: token_address.to_string(), to: to_address.to_string(), amount: amount.to_string(), - }, + }), ); - let message = HyperwalletMessage::SendToken(request); - let response: HyperwalletResponse = super::send_message(message)?; - response.data.ok_or_else(|| { - HyperwalletClientError::ServerError(types::OperationError::internal_error( - "Missing transaction receipt in response", - )) - }) + let response = super::send_message(message)?; + match response.data { + Some(types::HyperwalletResponseData::SendToken(send_response)) => Ok(TxReceipt { + hash: send_response.tx_hash, + details: serde_json::json!({ + "from": send_response.from_address, + "to": send_response.to_address, + "token_address": send_response.token_address, + "amount": send_response.amount, + "chain_id": send_response.chain_id, + }), + }), + _ => Err(HyperwalletClientError::ServerError( + types::OperationError::internal_error("Missing transaction receipt in response"), + )), + } } // === QUERIES === @@ -223,20 +303,22 @@ pub fn get_balance( session_id: &SessionId, wallet_id: &str, ) -> Result { - let request = build_request( + let message = build_message( session_id, - types::GetBalanceRequest { + HyperwalletRequest::GetBalance(types::GetBalanceRequest { wallet_id: wallet_id.to_string(), - }, + }), ); - let message = HyperwalletMessage::GetBalance(request); - let response: HyperwalletResponse = super::send_message(message)?; - response.data.ok_or_else(|| { - HyperwalletClientError::ServerError(types::OperationError::internal_error( - "Missing balance data in response", - )) - }) + let response = super::send_message(message)?; + match response.data { + Some(types::HyperwalletResponseData::GetBalance(balance_response)) => { + Ok(balance_response.balance) + } + _ => Err(HyperwalletClientError::ServerError( + types::OperationError::internal_error("Missing balance data in response"), + )), + } } pub fn get_token_balance( @@ -244,21 +326,23 @@ pub fn get_token_balance( wallet_id: &str, token_address: &str, ) -> Result { - let request = build_request( + let message = build_message( session_id, - types::GetTokenBalanceRequest { + HyperwalletRequest::GetTokenBalance(types::GetTokenBalanceRequest { wallet_id: wallet_id.to_string(), token_address: token_address.to_string(), - }, + }), ); - let message = HyperwalletMessage::GetTokenBalance(request); - let response: HyperwalletResponse = super::send_message(message)?; - response.data.ok_or_else(|| { - HyperwalletClientError::ServerError(types::OperationError::internal_error( - "Missing token balance data in response", - )) - }) + let response = super::send_message(message)?; + match response.data { + Some(types::HyperwalletResponseData::GetTokenBalance(balance_response)) => { + Ok(balance_response) + } + _ => Err(HyperwalletClientError::ServerError( + types::OperationError::internal_error("Missing token balance data in response"), + )), + } } // === ACCOUNT ABSTRACTION === @@ -273,27 +357,30 @@ pub fn build_and_sign_user_operation_for_payment( paymaster_config: Option, password: Option<&str>, ) -> Result { - let request = build_request( + let message = build_message( session_id, - BuildAndSignUserOperationForPaymentRequest { - eoa_wallet_id: eoa_wallet_id.to_string(), - tba_address: tba_address.to_string(), - target: target.to_string(), - call_data: call_data.to_string(), - use_paymaster, - paymaster_config, - password: password.map(|s| s.to_string()), - }, + HyperwalletRequest::BuildAndSignUserOperationForPayment( + BuildAndSignUserOperationForPaymentRequest { + eoa_wallet_id: eoa_wallet_id.to_string(), + tba_address: tba_address.to_string(), + target: target.to_string(), + call_data: call_data.to_string(), + use_paymaster, + paymaster_config, + password: password.map(|s| s.to_string()), + }, + ), ); - let message = HyperwalletMessage::BuildAndSignUserOperationForPayment(request); - let response: HyperwalletResponse = - super::send_message(message)?; - response.data.ok_or_else(|| { - HyperwalletClientError::ServerError(types::OperationError::internal_error( - "Missing UserOperation build response data", - )) - }) + let response = super::send_message(message)?; + match response.data { + Some(types::HyperwalletResponseData::BuildAndSignUserOperationForPayment( + build_response, + )) => Ok(build_response), + _ => Err(HyperwalletClientError::ServerError( + types::OperationError::internal_error("Missing UserOperation build response data"), + )), + } } pub fn submit_user_operation( @@ -302,45 +389,47 @@ pub fn submit_user_operation( entry_point: &str, bundler_url: Option<&str>, ) -> Result { - let request = build_request( + let message = build_message( session_id, - types::SubmitUserOperationRequest { - signed_user_operation, + HyperwalletRequest::SubmitUserOperation(types::SubmitUserOperationRequest { + signed_user_operation: serde_json::to_string(&signed_user_operation) + .map_err(HyperwalletClientError::Serialization)?, entry_point: entry_point.to_string(), bundler_url: bundler_url.map(|s| s.to_string()), - }, + }), ); - let message = HyperwalletMessage::SubmitUserOperation(request); - let response: HyperwalletResponse = super::send_message(message)?; - let data = response.data.ok_or_else(|| { - HyperwalletClientError::ServerError(types::OperationError::internal_error( - "Missing UserOperation response data", - )) - })?; - - // Direct field access - no manual JSON parsing needed! - Ok(data.user_op_hash) + let response = super::send_message(message)?; + match response.data { + Some(types::HyperwalletResponseData::SubmitUserOperation(submit_response)) => { + Ok(submit_response.user_op_hash) + } + _ => Err(HyperwalletClientError::ServerError( + types::OperationError::internal_error("Missing UserOperation response data"), + )), + } } pub fn get_user_operation_receipt( session_id: &SessionId, user_op_hash: &str, ) -> Result { - let request = build_request( + let message = build_message( session_id, - types::GetUserOperationReceiptRequest { + HyperwalletRequest::GetUserOperationReceipt(types::GetUserOperationReceiptRequest { user_op_hash: user_op_hash.to_string(), - }, + }), ); - let message = HyperwalletMessage::GetUserOperationReceipt(request); - let response: HyperwalletResponse = super::send_message(message)?; - response.data.ok_or_else(|| { - HyperwalletClientError::ServerError(types::OperationError::internal_error( - "Missing UserOperation receipt data in response", - )) - }) + let response = super::send_message(message)?; + match response.data { + Some(types::HyperwalletResponseData::GetUserOperationReceipt(receipt_response)) => { + Ok(receipt_response) + } + _ => Err(HyperwalletClientError::ServerError( + types::OperationError::internal_error("Missing UserOperation receipt data in response"), + )), + } } // === CONVENIENCE FUNCTIONS === @@ -369,7 +458,8 @@ pub fn execute_gasless_payment( let user_op_hash = submit_user_operation( session_id, - build_response.signed_user_operation, + serde_json::from_str(&build_response.signed_user_operation) + .map_err(HyperwalletClientError::Deserialization)?, &build_response.entry_point, None, // bundler_url )?; @@ -386,10 +476,10 @@ pub fn execute_gasless_payment( let tx_hash = receipt_response .receipt .as_ref() - .and_then(|r| r.get("transactionHash")) - .and_then(|h| h.as_str()) - .unwrap_or(&user_op_hash) // Fallback to user op hash - .to_string(); + .and_then(|r| serde_json::from_str::(r).ok()) + .and_then(|v| v.get("transactionHash").cloned()) + .and_then(|h| h.as_str().map(|s| s.to_string())) + .unwrap_or_else(|| user_op_hash.clone()); Ok(tx_hash) } @@ -425,9 +515,9 @@ pub fn create_tba_payment_calldata( // === INTERNAL HELPERS === -fn build_request(session_id: &SessionId, operation_data: T) -> HyperwalletRequest { - HyperwalletRequest { - data: operation_data, +fn build_message(session_id: &SessionId, request: HyperwalletRequest) -> HyperwalletMessage { + HyperwalletMessage { + request, session_id: session_id.clone(), } } diff --git a/src/hyperwallet_client/mod.rs b/src/hyperwallet_client/mod.rs index 8547aec..adad227 100644 --- a/src/hyperwallet_client/mod.rs +++ b/src/hyperwallet_client/mod.rs @@ -1,17 +1,21 @@ +use crate::println as kiprintln; use crate::Request; use thiserror::Error; pub mod api; +pub mod serde_impls; +mod serde_response_impls; // Proper implementations replacing broken stubs +mod serde_variant_impls; pub mod types; pub use types::{ Balance, BuildAndSignUserOperationForPaymentRequest, BuildAndSignUserOperationResponse, ChainId, CheckTbaOwnershipResponse, CreateNoteResponse, CreateWalletRequest, - CreateWalletResponse, DeleteWalletRequest, DeleteWalletResponse, ErrorCode, + CreateWalletResponse, DeleteWalletRequest, DeleteWalletResponse, Eip712Data, ErrorCode, ExecuteViaTbaResponse, ExportWalletRequest, ExportWalletResponse, GetBalanceRequest, GetBalanceResponse, GetTokenBalanceRequest, GetTokenBalanceResponse, GetWalletInfoRequest, - GetWalletInfoResponse, HandshakeConfig, HandshakeRequest, HandshakeStep, HyperwalletMessage, - HyperwalletRequest, HyperwalletResponse, HyperwalletResponseData, ImportWalletRequest, - ImportWalletResponse, ListWalletsResponse, Operation, OperationCategory, OperationError, + GetWalletInfoResponse, HandshakeConfig, HandshakeStep, HyperwalletMessage, HyperwalletRequest, + HyperwalletResponse, HyperwalletResponseData, ImportWalletRequest, ImportWalletResponse, + ListWalletsResponse, MessageType, Operation, OperationCategory, OperationError, PaymasterConfig, ProcessAddress, ProcessPermissions, RenameWalletRequest, SendEthRequest, SendEthResponse, SendTokenRequest, SendTokenResponse, SessionId, SessionInfo, SpendingLimits, SubmitUserOperationResponse, TxReceipt, UnlockWalletResponse, UpdatableSetting, @@ -43,29 +47,36 @@ pub enum HyperwalletClientError { /// Performs the full handshake and registration protocol with the Hyperwallet service. pub fn initialize(config: HandshakeConfig) -> Result { + const CLIENT_PROTOCOL_VERSION: &str = "0.1.0"; let client_name = config.client_name.expect("Client name is required"); - let hello_step = types::HandshakeStep::ClientHello { - client_version: "0.1.0".to_string(), + let hello_step = types::HandshakeStep::ClientHello(types::ClientHello { + client_version: CLIENT_PROTOCOL_VERSION.to_string(), client_name, + }); + let hello_message = types::HyperwalletMessage { + request: types::HyperwalletRequest::Handshake(hello_step), + session_id: String::new(), }; - let hello_message = - types::HyperwalletMessage::Handshake(types::HandshakeRequest { step: hello_step }); - - let welcome_response: types::HyperwalletResponse = - send_message(hello_message)?; - - let welcome_step = welcome_response.data.ok_or_else(|| { - HyperwalletClientError::ServerError(types::OperationError::internal_error( - "Missing handshake step in ServerWelcome response", - )) - })?; - - let supported_operations = match welcome_step { - types::HandshakeStep::ServerWelcome { - supported_operations, - .. - } => supported_operations, + + let welcome_response = send_message(hello_message)?; + + let welcome_step = match welcome_response.data { + Some(types::HyperwalletResponseData::Handshake(step)) => step, + _ => { + return Err(HyperwalletClientError::ServerError( + types::OperationError::internal_error( + "Missing or invalid handshake step in ServerWelcome response", + ), + )) + } + }; + + let (server_version, supported_operations) = match welcome_step { + types::HandshakeStep::ServerWelcome(server_welcome) => ( + server_welcome.server_version, + server_welcome.supported_operations, + ), _ => { return Err(HyperwalletClientError::ServerError( types::OperationError::internal_error( @@ -75,6 +86,19 @@ pub fn initialize(config: HandshakeConfig) -> Result Result = - send_message(register_message)?; + let register_message = types::HyperwalletMessage { + request: types::HyperwalletRequest::Handshake(register_step), + session_id: String::new(), + }; + + let complete_response = send_message(register_message)?; - let complete_step = complete_response.data.ok_or_else(|| { - HyperwalletClientError::ServerError(types::OperationError::internal_error( - "Complete response contained no data", - )) - })?; + let complete_step = match complete_response.data { + Some(types::HyperwalletResponseData::Handshake(step)) => step, + _ => { + return Err(HyperwalletClientError::ServerError( + types::OperationError::internal_error( + "Complete response contained no data or invalid data type", + ), + )) + } + }; // Extract SessionInfo using pattern matching match complete_step { - types::HandshakeStep::Complete { - registered_permissions, - session_id, - } => { - Ok(SessionInfo { - server_version: "0.1.0".to_string(), //lol, server should send it's version - session_id, - registered_permissions, - initial_chain_id: config.initial_chain_id, - }) - } + types::HandshakeStep::Complete(complete_handshake) => Ok(types::SessionInfo { + server_version, + session_id: complete_handshake.session_id, + registered_permissions: complete_handshake.registered_permissions, + initial_chain_id: config.initial_chain_id, + }), _ => Err(HyperwalletClientError::ServerError( types::OperationError::internal_error( "Expected Complete handshake step, received different step", @@ -129,20 +153,17 @@ pub fn initialize(config: HandshakeConfig) -> Result( +pub(crate) fn send_message( message: types::HyperwalletMessage, -) -> Result, HyperwalletClientError> -where - T: for<'de> serde::Deserialize<'de>, -{ +) -> Result { // Use local address pattern like HTTP client - hyperwallet is always local - let response = Request::to(("our", "hyperwallet", "hyperwallet", "hallman.hypr")) + let response = Request::to(("our", "hyperwallet", "hyperwallet", "sys")) .body(serde_json::to_vec(&message).map_err(HyperwalletClientError::Serialization)?) .send_and_await_response(5) // 5s timeout .map_err(|e| HyperwalletClientError::Communication(e.into()))? .map_err(|e| HyperwalletClientError::Communication(e.into()))?; - let hyperwallet_response: types::HyperwalletResponse = + let hyperwallet_response: types::HyperwalletResponse = serde_json::from_slice(response.body()).map_err(HyperwalletClientError::Deserialization)?; if !hyperwallet_response.success { diff --git a/src/hyperwallet_client/serde_impls.rs b/src/hyperwallet_client/serde_impls.rs new file mode 100644 index 0000000..83b4111 --- /dev/null +++ b/src/hyperwallet_client/serde_impls.rs @@ -0,0 +1,1353 @@ +use crate::hyperware::process::hyperwallet as wit; +use serde::de::{self, MapAccess, Visitor}; +use serde::ser::{Serialize, SerializeStruct}; +use serde::Deserialize; + +// ============================================================================ +// HyperwalletMessage +// ============================================================================ + +impl Serialize for wit::HyperwalletMessage { + fn serialize(&self, serializer: S) -> Result + where + S: serde::ser::Serializer, + { + let mut state = serializer.serialize_struct("HyperwalletMessage", 2)?; + state.serialize_field("request", &self.request)?; + state.serialize_field("session_id", &self.session_id)?; + state.end() + } +} + +impl<'a> Deserialize<'a> for wit::HyperwalletMessage { + fn deserialize(deserializer: D) -> Result + where + D: serde::de::Deserializer<'a>, + { + #[derive(Deserialize)] + #[serde(field_identifier, rename_all = "snake_case")] + enum Field { + Request, + SessionId, + } + + struct HyperwalletMessageVisitor; + + impl<'de> Visitor<'de> for HyperwalletMessageVisitor { + type Value = wit::HyperwalletMessage; + + fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + formatter.write_str("struct HyperwalletMessage") + } + + fn visit_map(self, mut map: V) -> Result + where + V: MapAccess<'de>, + { + let mut request = None; + let mut session_id = None; + + while let Some(key) = map.next_key()? { + match key { + Field::Request => { + if request.is_some() { + return Err(de::Error::duplicate_field("request")); + } + request = Some(map.next_value()?); + } + Field::SessionId => { + if session_id.is_some() { + return Err(de::Error::duplicate_field("session_id")); + } + session_id = Some(map.next_value()?); + } + } + } + + let request = request.ok_or_else(|| de::Error::missing_field("request"))?; + let session_id = + session_id.ok_or_else(|| de::Error::missing_field("session_id"))?; + + Ok(wit::HyperwalletMessage { + request, + session_id, + }) + } + } + + const FIELDS: &[&str] = &["request", "session_id"]; + deserializer.deserialize_struct("HyperwalletMessage", FIELDS, HyperwalletMessageVisitor) + } +} + +// ============================================================================ +// HyperwalletResponse +// ============================================================================ + +impl Serialize for wit::HyperwalletResponse { + fn serialize(&self, serializer: S) -> Result + where + S: serde::ser::Serializer, + { + let mut state = serializer.serialize_struct("HyperwalletResponse", 4)?; + state.serialize_field("success", &self.success)?; + state.serialize_field("data", &self.data)?; + state.serialize_field("error", &self.error)?; + state.serialize_field("request_id", &self.request_id)?; + state.end() + } +} + +impl<'a> Deserialize<'a> for wit::HyperwalletResponse { + fn deserialize(deserializer: D) -> Result + where + D: serde::de::Deserializer<'a>, + { + #[derive(Deserialize)] + #[serde(field_identifier, rename_all = "snake_case")] + enum Field { + Success, + Data, + Error, + RequestId, + } + + struct HyperwalletResponseVisitor; + + impl<'de> Visitor<'de> for HyperwalletResponseVisitor { + type Value = wit::HyperwalletResponse; + + fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + formatter.write_str("struct HyperwalletResponse") + } + + fn visit_map(self, mut map: V) -> Result + where + V: MapAccess<'de>, + { + let mut success = None; + let mut data = None; + let mut error = None; + let mut request_id = None; + + while let Some(key) = map.next_key()? { + match key { + Field::Success => { + if success.is_some() { + return Err(de::Error::duplicate_field("success")); + } + success = Some(map.next_value()?); + } + Field::Data => { + if data.is_some() { + return Err(de::Error::duplicate_field("data")); + } + data = map.next_value()?; + } + Field::Error => { + if error.is_some() { + return Err(de::Error::duplicate_field("error")); + } + error = map.next_value()?; + } + Field::RequestId => { + if request_id.is_some() { + return Err(de::Error::duplicate_field("request_id")); + } + request_id = map.next_value()?; + } + } + } + + let success = success.ok_or_else(|| de::Error::missing_field("success"))?; + + Ok(wit::HyperwalletResponse { + success, + data, + error, + request_id, + }) + } + } + + const FIELDS: &[&str] = &["success", "data", "error", "request_id"]; + deserializer.deserialize_struct("HyperwalletResponse", FIELDS, HyperwalletResponseVisitor) + } +} + +// ============================================================================ +// OperationError +// ============================================================================ + +impl Serialize for wit::OperationError { + fn serialize(&self, serializer: S) -> Result + where + S: serde::ser::Serializer, + { + let mut state = serializer.serialize_struct("OperationError", 3)?; + state.serialize_field("code", &self.code)?; + state.serialize_field("message", &self.message)?; + state.serialize_field("details", &self.details)?; + state.end() + } +} + +impl<'a> Deserialize<'a> for wit::OperationError { + fn deserialize(deserializer: D) -> Result + where + D: serde::de::Deserializer<'a>, + { + #[derive(Deserialize)] + #[serde(field_identifier, rename_all = "snake_case")] + enum Field { + Code, + Message, + Details, + } + + struct OperationErrorVisitor; + + impl<'de> Visitor<'de> for OperationErrorVisitor { + type Value = wit::OperationError; + + fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + formatter.write_str("struct OperationError") + } + + fn visit_map(self, mut map: V) -> Result + where + V: MapAccess<'de>, + { + let mut code = None; + let mut message = None; + let mut details = None; + + while let Some(key) = map.next_key()? { + match key { + Field::Code => { + if code.is_some() { + return Err(de::Error::duplicate_field("code")); + } + code = Some(map.next_value()?); + } + Field::Message => { + if message.is_some() { + return Err(de::Error::duplicate_field("message")); + } + message = Some(map.next_value()?); + } + Field::Details => { + if details.is_some() { + return Err(de::Error::duplicate_field("details")); + } + // Accept Option: null -> None + details = map.next_value()?; + } + } + } + + let code = code.ok_or_else(|| de::Error::missing_field("code"))?; + let message = message.ok_or_else(|| de::Error::missing_field("message"))?; + + Ok(wit::OperationError { + code, + message, + details, + }) + } + } + + const FIELDS: &[&str] = &["code", "message", "details"]; + deserializer.deserialize_struct("OperationError", FIELDS, OperationErrorVisitor) + } +} + +// ============================================================================ +// ErrorCode enum +// ============================================================================ + +impl Serialize for wit::ErrorCode { + fn serialize(&self, serializer: S) -> Result + where + S: serde::ser::Serializer, + { + serializer.serialize_str(match self { + wit::ErrorCode::InternalError => "InternalError", + wit::ErrorCode::InvalidParams => "InvalidParams", + wit::ErrorCode::InvalidOperation => "InvalidOperation", + wit::ErrorCode::PermissionDenied => "PermissionDenied", + wit::ErrorCode::WalletNotFound => "WalletNotFound", + wit::ErrorCode::WalletLocked => "WalletLocked", + wit::ErrorCode::AuthenticationFailed => "AuthenticationFailed", + wit::ErrorCode::InsufficientFunds => "InsufficientFunds", + wit::ErrorCode::SpendingLimitExceeded => "SpendingLimitExceeded", + wit::ErrorCode::BlockchainError => "BlockchainError", + wit::ErrorCode::ChainNotAllowed => "ChainNotAllowed", + wit::ErrorCode::OperationNotSupported => "OperationNotSupported", + wit::ErrorCode::VersionMismatch => "VersionMismatch", + }) + } +} + +impl<'a> Deserialize<'a> for wit::ErrorCode { + fn deserialize(deserializer: D) -> Result + where + D: serde::de::Deserializer<'a>, + { + let s = String::deserialize(deserializer)?; + match s.as_str() { + "InternalError" => Ok(wit::ErrorCode::InternalError), + "InvalidParams" => Ok(wit::ErrorCode::InvalidParams), + "InvalidOperation" => Ok(wit::ErrorCode::InvalidOperation), + "PermissionDenied" => Ok(wit::ErrorCode::PermissionDenied), + "WalletNotFound" => Ok(wit::ErrorCode::WalletNotFound), + "WalletLocked" => Ok(wit::ErrorCode::WalletLocked), + "AuthenticationFailed" => Ok(wit::ErrorCode::AuthenticationFailed), + "InsufficientFunds" => Ok(wit::ErrorCode::InsufficientFunds), + "SpendingLimitExceeded" => Ok(wit::ErrorCode::SpendingLimitExceeded), + "BlockchainError" => Ok(wit::ErrorCode::BlockchainError), + "ChainNotAllowed" => Ok(wit::ErrorCode::ChainNotAllowed), + "OperationNotSupported" => Ok(wit::ErrorCode::OperationNotSupported), + "VersionMismatch" => Ok(wit::ErrorCode::VersionMismatch), + _ => Err(de::Error::unknown_variant( + &s, + &[ + "InternalError", + "InvalidParams", + "InvalidOperation", + "PermissionDenied", + "WalletNotFound", + "WalletLocked", + "AuthenticationFailed", + "InsufficientFunds", + "SpendingLimitExceeded", + "BlockchainError", + "ChainNotAllowed", + "OperationNotSupported", + "VersionMismatch", + ], + )), + } + } +} // ============================================================================ + // Request Types (Records) + // ============================================================================ + +// CreateWalletRequest +impl Serialize for wit::CreateWalletRequest { + fn serialize(&self, serializer: S) -> Result + where + S: serde::ser::Serializer, + { + // Only include password when present to avoid sending null + let field_count = if self.password.is_some() { 2 } else { 1 }; + let mut state = serializer.serialize_struct("CreateWalletRequest", field_count)?; + state.serialize_field("name", &self.name)?; + if let Some(ref pwd) = self.password { + state.serialize_field("password", pwd)?; + } + state.end() + } +} + +impl<'a> Deserialize<'a> for wit::CreateWalletRequest { + fn deserialize(deserializer: D) -> Result + where + D: serde::de::Deserializer<'a>, + { + #[derive(Deserialize)] + #[serde(field_identifier, rename_all = "snake_case")] + enum Field { + Name, + Password, + } + + struct CreateWalletRequestVisitor; + + impl<'de> Visitor<'de> for CreateWalletRequestVisitor { + type Value = wit::CreateWalletRequest; + + fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + formatter.write_str("struct CreateWalletRequest") + } + + fn visit_map(self, mut map: V) -> Result + where + V: MapAccess<'de>, + { + let mut name = None; + let mut password = None; + + while let Some(key) = map.next_key()? { + match key { + Field::Name => { + if name.is_some() { + return Err(de::Error::duplicate_field("name")); + } + name = Some(map.next_value()?); + } + Field::Password => { + if password.is_some() { + return Err(de::Error::duplicate_field("password")); + } + password = Some(map.next_value()?); + } + } + } + + let name = name.ok_or_else(|| de::Error::missing_field("name"))?; + + Ok(wit::CreateWalletRequest { name, password }) + } + } + + const FIELDS: &[&str] = &["name", "password"]; + deserializer.deserialize_struct("CreateWalletRequest", FIELDS, CreateWalletRequestVisitor) + } +} + +// UnlockWalletRequest +impl Serialize for wit::UnlockWalletRequest { + fn serialize(&self, serializer: S) -> Result + where + S: serde::ser::Serializer, + { + let mut state = serializer.serialize_struct("UnlockWalletRequest", 3)?; + state.serialize_field("session_id", &self.session_id)?; + state.serialize_field("wallet_id", &self.wallet_id)?; + state.serialize_field("password", &self.password)?; + state.end() + } +} + +impl<'a> Deserialize<'a> for wit::UnlockWalletRequest { + fn deserialize(deserializer: D) -> Result + where + D: serde::de::Deserializer<'a>, + { + #[derive(Deserialize)] + #[serde(field_identifier, rename_all = "snake_case")] + enum Field { + SessionId, + WalletId, + Password, + } + + struct UnlockWalletRequestVisitor; + + impl<'de> Visitor<'de> for UnlockWalletRequestVisitor { + type Value = wit::UnlockWalletRequest; + + fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + formatter.write_str("struct UnlockWalletRequest") + } + + fn visit_map(self, mut map: V) -> Result + where + V: MapAccess<'de>, + { + let mut session_id = None; + let mut wallet_id = None; + let mut password = None; + + while let Some(key) = map.next_key()? { + match key { + Field::SessionId => { + if session_id.is_some() { + return Err(de::Error::duplicate_field("session_id")); + } + session_id = Some(map.next_value()?); + } + Field::WalletId => { + if wallet_id.is_some() { + return Err(de::Error::duplicate_field("wallet_id")); + } + wallet_id = Some(map.next_value()?); + } + Field::Password => { + if password.is_some() { + return Err(de::Error::duplicate_field("password")); + } + password = Some(map.next_value()?); + } + } + } + + let session_id = + session_id.ok_or_else(|| de::Error::missing_field("session_id"))?; + let wallet_id = wallet_id.ok_or_else(|| de::Error::missing_field("wallet_id"))?; + let password = password.ok_or_else(|| de::Error::missing_field("password"))?; + + Ok(wit::UnlockWalletRequest { + session_id, + wallet_id, + password, + }) + } + } + + const FIELDS: &[&str] = &["session_id", "wallet_id", "password"]; + deserializer.deserialize_struct("UnlockWalletRequest", FIELDS, UnlockWalletRequestVisitor) + } +} + +// ============================================================================ +// Operation enum +// ============================================================================ + +impl Serialize for wit::Operation { + fn serialize(&self, serializer: S) -> Result + where + S: serde::ser::Serializer, + { + use wit::Operation::*; + serializer.serialize_str(match self { + Handshake => "Handshake", + UnlockWallet => "UnlockWallet", + RegisterProcess => "RegisterProcess", + UpdateSpendingLimits => "UpdateSpendingLimits", + CreateWallet => "CreateWallet", + ImportWallet => "ImportWallet", + DeleteWallet => "DeleteWallet", + RenameWallet => "RenameWallet", + ExportWallet => "ExportWallet", + EncryptWallet => "EncryptWallet", + DecryptWallet => "DecryptWallet", + GetWalletInfo => "GetWalletInfo", + ListWallets => "ListWallets", + SetWalletLimits => "SetWalletLimits", + SendEth => "SendEth", + SendToken => "SendToken", + ApproveToken => "ApproveToken", + CallContract => "CallContract", + SignTransaction => "SignTransaction", + SignMessage => "SignMessage", + ExecuteViaTba => "ExecuteViaTba", + CheckTbaOwnership => "CheckTbaOwnership", + SetupTbaDelegation => "SetupTbaDelegation", + BuildAndSignUserOperationForPayment => "BuildAndSignUserOperationForPayment", + SubmitUserOperation => "SubmitUserOperation", + BuildUserOperation => "BuildUserOperation", + SignUserOperation => "SignUserOperation", + BuildAndSignUserOperation => "BuildAndSignUserOperation", + EstimateUserOperationGas => "EstimateUserOperationGas", + GetUserOperationReceipt => "GetUserOperationReceipt", + ConfigurePaymaster => "ConfigurePaymaster", + ResolveIdentity => "ResolveIdentity", + CreateNote => "CreateNote", + ReadNote => "ReadNote", + SetupDelegation => "SetupDelegation", + VerifyDelegation => "VerifyDelegation", + MintEntry => "MintEntry", + GetBalance => "GetBalance", + GetTokenBalance => "GetTokenBalance", + GetTransactionHistory => "GetTransactionHistory", + EstimateGas => "EstimateGas", + GetGasPrice => "GetGasPrice", + GetTransactionReceipt => "GetTransactionReceipt", + BatchOperations => "BatchOperations", + ScheduleOperation => "ScheduleOperation", + CancelOperation => "CancelOperation", + }) + } +} + +impl<'a> Deserialize<'a> for wit::Operation { + fn deserialize(deserializer: D) -> Result + where + D: serde::de::Deserializer<'a>, + { + let s = String::deserialize(deserializer)?; + use wit::Operation::*; + match s.as_str() { + "Handshake" => Ok(Handshake), + "UnlockWallet" => Ok(UnlockWallet), + "RegisterProcess" => Ok(RegisterProcess), + "UpdateSpendingLimits" => Ok(UpdateSpendingLimits), + "CreateWallet" => Ok(CreateWallet), + "ImportWallet" => Ok(ImportWallet), + "DeleteWallet" => Ok(DeleteWallet), + "RenameWallet" => Ok(RenameWallet), + "ExportWallet" => Ok(ExportWallet), + "EncryptWallet" => Ok(EncryptWallet), + "DecryptWallet" => Ok(DecryptWallet), + "GetWalletInfo" => Ok(GetWalletInfo), + "ListWallets" => Ok(ListWallets), + "SetWalletLimits" => Ok(SetWalletLimits), + "SendEth" => Ok(SendEth), + "SendToken" => Ok(SendToken), + "ApproveToken" => Ok(ApproveToken), + "CallContract" => Ok(CallContract), + "SignTransaction" => Ok(SignTransaction), + "SignMessage" => Ok(SignMessage), + "ExecuteViaTba" => Ok(ExecuteViaTba), + "CheckTbaOwnership" => Ok(CheckTbaOwnership), + "SetupTbaDelegation" => Ok(SetupTbaDelegation), + "BuildAndSignUserOperationForPayment" => Ok(BuildAndSignUserOperationForPayment), + "SubmitUserOperation" => Ok(SubmitUserOperation), + "BuildUserOperation" => Ok(BuildUserOperation), + "SignUserOperation" => Ok(SignUserOperation), + "BuildAndSignUserOperation" => Ok(BuildAndSignUserOperation), + "EstimateUserOperationGas" => Ok(EstimateUserOperationGas), + "GetUserOperationReceipt" => Ok(GetUserOperationReceipt), + "ConfigurePaymaster" => Ok(ConfigurePaymaster), + "ResolveIdentity" => Ok(ResolveIdentity), + "CreateNote" => Ok(CreateNote), + "ReadNote" => Ok(ReadNote), + "SetupDelegation" => Ok(SetupDelegation), + "VerifyDelegation" => Ok(VerifyDelegation), + "MintEntry" => Ok(MintEntry), + "GetBalance" => Ok(GetBalance), + "GetTokenBalance" => Ok(GetTokenBalance), + "GetTransactionHistory" => Ok(GetTransactionHistory), + "EstimateGas" => Ok(EstimateGas), + "GetGasPrice" => Ok(GetGasPrice), + "GetTransactionReceipt" => Ok(GetTransactionReceipt), + "BatchOperations" => Ok(BatchOperations), + "ScheduleOperation" => Ok(ScheduleOperation), + "CancelOperation" => Ok(CancelOperation), + _ => Err(de::Error::unknown_variant(&s, &[])), + } + } +} + +// ============================================================================ +// ProcessPermissions +// ============================================================================ + +impl Serialize for wit::ProcessPermissions { + fn serialize(&self, serializer: S) -> Result + where + S: serde::ser::Serializer, + { + let mut state = serializer.serialize_struct("ProcessPermissions", 5)?; + state.serialize_field("address", &self.address)?; + state.serialize_field("allowed_operations", &self.allowed_operations)?; + state.serialize_field("spending_limits", &self.spending_limits)?; + state.serialize_field("updatable_settings", &self.updatable_settings)?; + state.serialize_field("registered_at", &self.registered_at)?; + state.end() + } +} + +impl<'a> Deserialize<'a> for wit::ProcessPermissions { + fn deserialize(deserializer: D) -> Result + where + D: serde::de::Deserializer<'a>, + { + #[derive(Deserialize)] + #[serde(field_identifier, rename_all = "snake_case")] + enum Field { + Address, + AllowedOperations, + SpendingLimits, + UpdatableSettings, + RegisteredAt, + } + + struct ProcessPermissionsVisitor; + + impl<'de> Visitor<'de> for ProcessPermissionsVisitor { + type Value = wit::ProcessPermissions; + + fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + formatter.write_str("struct ProcessPermissions") + } + + fn visit_map(self, mut map: V) -> Result + where + V: MapAccess<'de>, + { + let mut address = None; + let mut allowed_operations = None; + let mut spending_limits = None; + let mut updatable_settings = None; + let mut registered_at = None; + + while let Some(key) = map.next_key()? { + match key { + Field::Address => { + if address.is_some() { + return Err(de::Error::duplicate_field("address")); + } + address = Some(map.next_value()?); + } + Field::AllowedOperations => { + if allowed_operations.is_some() { + return Err(de::Error::duplicate_field("allowed_operations")); + } + allowed_operations = Some(map.next_value()?); + } + Field::SpendingLimits => { + if spending_limits.is_some() { + return Err(de::Error::duplicate_field("spending_limits")); + } + spending_limits = Some(map.next_value()?); + } + Field::UpdatableSettings => { + if updatable_settings.is_some() { + return Err(de::Error::duplicate_field("updatable_settings")); + } + updatable_settings = Some(map.next_value()?); + } + Field::RegisteredAt => { + if registered_at.is_some() { + return Err(de::Error::duplicate_field("registered_at")); + } + registered_at = Some(map.next_value()?); + } + } + } + + let address = address.ok_or_else(|| de::Error::missing_field("address"))?; + let allowed_operations = allowed_operations + .ok_or_else(|| de::Error::missing_field("allowed_operations"))?; + let registered_at = + registered_at.ok_or_else(|| de::Error::missing_field("registered_at"))?; + + Ok(wit::ProcessPermissions { + address, + allowed_operations, + spending_limits, + updatable_settings: updatable_settings.unwrap_or_default(), + registered_at, + }) + } + } + + const FIELDS: &[&str] = &[ + "address", + "allowed_operations", + "spending_limits", + "updatable_settings", + "registered_at", + ]; + deserializer.deserialize_struct("ProcessPermissions", FIELDS, ProcessPermissionsVisitor) + } +} + +// ============================================================================ +// SpendingLimits +// ============================================================================ + +impl Serialize for wit::SpendingLimits { + fn serialize(&self, serializer: S) -> Result + where + S: serde::ser::Serializer, + { + let mut state = serializer.serialize_struct("SpendingLimits", 7)?; + state.serialize_field("per_tx_eth", &self.per_tx_eth)?; + state.serialize_field("daily_eth", &self.daily_eth)?; + state.serialize_field("per_tx_usdc", &self.per_tx_usdc)?; + state.serialize_field("daily_usdc", &self.daily_usdc)?; + state.serialize_field("daily_reset_at", &self.daily_reset_at)?; + state.serialize_field("spent_today_eth", &self.spent_today_eth)?; + state.serialize_field("spent_today_usdc", &self.spent_today_usdc)?; + state.end() + } +} + +impl<'a> Deserialize<'a> for wit::SpendingLimits { + fn deserialize(deserializer: D) -> Result + where + D: serde::de::Deserializer<'a>, + { + #[derive(Deserialize)] + #[serde(field_identifier, rename_all = "snake_case")] + enum Field { + PerTxEth, + DailyEth, + PerTxUsdc, + DailyUsdc, + DailyResetAt, + SpentTodayEth, + SpentTodayUsdc, + } + + struct SpendingLimitsVisitor; + + impl<'de> Visitor<'de> for SpendingLimitsVisitor { + type Value = wit::SpendingLimits; + + fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + formatter.write_str("struct SpendingLimits") + } + + fn visit_map(self, mut map: V) -> Result + where + V: MapAccess<'de>, + { + let mut per_tx_eth = None; + let mut daily_eth = None; + let mut per_tx_usdc = None; + let mut daily_usdc = None; + let mut daily_reset_at = None; + let mut spent_today_eth = None; + let mut spent_today_usdc = None; + + while let Some(key) = map.next_key()? { + match key { + Field::PerTxEth => { + if per_tx_eth.is_some() { + return Err(de::Error::duplicate_field("per_tx_eth")); + } + per_tx_eth = Some(map.next_value()?); + } + Field::DailyEth => { + if daily_eth.is_some() { + return Err(de::Error::duplicate_field("daily_eth")); + } + daily_eth = Some(map.next_value()?); + } + Field::PerTxUsdc => { + if per_tx_usdc.is_some() { + return Err(de::Error::duplicate_field("per_tx_usdc")); + } + per_tx_usdc = Some(map.next_value()?); + } + Field::DailyUsdc => { + if daily_usdc.is_some() { + return Err(de::Error::duplicate_field("daily_usdc")); + } + daily_usdc = Some(map.next_value()?); + } + Field::DailyResetAt => { + if daily_reset_at.is_some() { + return Err(de::Error::duplicate_field("daily_reset_at")); + } + daily_reset_at = Some(map.next_value()?); + } + Field::SpentTodayEth => { + if spent_today_eth.is_some() { + return Err(de::Error::duplicate_field("spent_today_eth")); + } + spent_today_eth = Some(map.next_value()?); + } + Field::SpentTodayUsdc => { + if spent_today_usdc.is_some() { + return Err(de::Error::duplicate_field("spent_today_usdc")); + } + spent_today_usdc = Some(map.next_value()?); + } + } + } + + let daily_reset_at = + daily_reset_at.ok_or_else(|| de::Error::missing_field("daily_reset_at"))?; + let spent_today_eth = + spent_today_eth.ok_or_else(|| de::Error::missing_field("spent_today_eth"))?; + let spent_today_usdc = + spent_today_usdc.ok_or_else(|| de::Error::missing_field("spent_today_usdc"))?; + + Ok(wit::SpendingLimits { + per_tx_eth, + daily_eth, + per_tx_usdc, + daily_usdc, + daily_reset_at, + spent_today_eth, + spent_today_usdc, + }) + } + } + + const FIELDS: &[&str] = &[ + "per_tx_eth", + "daily_eth", + "per_tx_usdc", + "daily_usdc", + "daily_reset_at", + "spent_today_eth", + "spent_today_usdc", + ]; + deserializer.deserialize_struct("SpendingLimits", FIELDS, SpendingLimitsVisitor) + } +} + +// ============================================================================ +// UpdatableSetting enum +// ============================================================================ + +impl Serialize for wit::UpdatableSetting { + fn serialize(&self, serializer: S) -> Result + where + S: serde::ser::Serializer, + { + use wit::UpdatableSetting::*; + serializer.serialize_str(match self { + SpendingLimits => "SpendingLimits", + }) + } +} + +impl<'a> Deserialize<'a> for wit::UpdatableSetting { + fn deserialize(deserializer: D) -> Result + where + D: serde::de::Deserializer<'a>, + { + let s = String::deserialize(deserializer)?; + use wit::UpdatableSetting::*; + match s.as_str() { + "SpendingLimits" => Ok(SpendingLimits), + _ => Err(de::Error::unknown_variant(&s, &["SpendingLimits"])), + } + } +} + +// ============================================================================ +// HandshakeStep variant type +// ============================================================================ + +impl Serialize for wit::HandshakeStep { + fn serialize(&self, serializer: S) -> Result + where + S: serde::ser::Serializer, + { + use wit::HandshakeStep::*; + + match self { + ClientHello(data) => { + let mut state = serializer.serialize_struct("HandshakeStep", 2)?; + state.serialize_field("type", "ClientHello")?; + state.serialize_field("data", data)?; + state.end() + } + ServerWelcome(data) => { + let mut state = serializer.serialize_struct("HandshakeStep", 2)?; + state.serialize_field("type", "ServerWelcome")?; + state.serialize_field("data", data)?; + state.end() + } + Register(data) => { + let mut state = serializer.serialize_struct("HandshakeStep", 2)?; + state.serialize_field("type", "Register")?; + state.serialize_field("data", data)?; + state.end() + } + Complete(data) => { + let mut state = serializer.serialize_struct("HandshakeStep", 2)?; + state.serialize_field("type", "Complete")?; + state.serialize_field("data", data)?; + state.end() + } + } + } +} + +impl<'a> Deserialize<'a> for wit::HandshakeStep { + fn deserialize(deserializer: D) -> Result + where + D: serde::de::Deserializer<'a>, + { + struct HandshakeStepVisitor; + + impl<'de> Visitor<'de> for HandshakeStepVisitor { + type Value = wit::HandshakeStep; + + fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + formatter.write_str("a HandshakeStep variant") + } + + fn visit_map(self, mut map: V) -> Result + where + V: MapAccess<'de>, + { + let mut variant_type = None; + let mut data = None; + + while let Some(key) = map.next_key::()? { + match key.as_str() { + "type" => { + if variant_type.is_some() { + return Err(de::Error::duplicate_field("type")); + } + variant_type = Some(map.next_value::()?); + } + "data" => { + if data.is_some() { + return Err(de::Error::duplicate_field("data")); + } + data = Some(map.next_value::()?); + } + _ => { + let _ = map.next_value::()?; + } + } + } + + let variant_type = variant_type.ok_or_else(|| de::Error::missing_field("type"))?; + + use wit::HandshakeStep::*; + match variant_type.as_str() { + "ClientHello" => { + let data = data.ok_or_else(|| de::Error::missing_field("data"))?; + let hello = serde_json::from_value(data).map_err(|e| { + de::Error::custom(format!("Failed to deserialize ClientHello: {}", e)) + })?; + Ok(ClientHello(hello)) + } + "ServerWelcome" => { + let data = data.ok_or_else(|| de::Error::missing_field("data"))?; + let welcome = serde_json::from_value(data).map_err(|e| { + de::Error::custom(format!("Failed to deserialize ServerWelcome: {}", e)) + })?; + Ok(ServerWelcome(welcome)) + } + "Register" => { + let data = data.ok_or_else(|| de::Error::missing_field("data"))?; + let register = serde_json::from_value(data).map_err(|e| { + de::Error::custom(format!( + "Failed to deserialize RegisterRequest: {}", + e + )) + })?; + Ok(Register(register)) + } + "Complete" => { + let data = data.ok_or_else(|| de::Error::missing_field("data"))?; + let complete = serde_json::from_value(data).map_err(|e| { + de::Error::custom(format!( + "Failed to deserialize CompleteHandshake: {}", + e + )) + })?; + Ok(Complete(complete)) + } + _ => Err(de::Error::unknown_variant( + &variant_type, + &["ClientHello", "ServerWelcome", "Register", "Complete"], + )), + } + } + } + + const FIELDS: &[&str] = &["type", "data"]; + deserializer.deserialize_struct("HandshakeStep", FIELDS, HandshakeStepVisitor) + } +} + +// ClientHello +impl Serialize for wit::ClientHello { + fn serialize(&self, serializer: S) -> Result + where + S: serde::ser::Serializer, + { + let mut state = serializer.serialize_struct("ClientHello", 2)?; + state.serialize_field("client_version", &self.client_version)?; + state.serialize_field("client_name", &self.client_name)?; + state.end() + } +} + +impl<'a> Deserialize<'a> for wit::ClientHello { + fn deserialize(deserializer: D) -> Result + where + D: serde::de::Deserializer<'a>, + { + #[derive(Deserialize)] + #[serde(field_identifier, rename_all = "snake_case")] + enum Field { + ClientVersion, + ClientName, + } + + struct ClientHelloVisitor; + + impl<'de> Visitor<'de> for ClientHelloVisitor { + type Value = wit::ClientHello; + + fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + formatter.write_str("struct ClientHello") + } + + fn visit_map(self, mut map: V) -> Result + where + V: MapAccess<'de>, + { + let mut client_version = None; + let mut client_name = None; + + while let Some(key) = map.next_key()? { + match key { + Field::ClientVersion => { + if client_version.is_some() { + return Err(de::Error::duplicate_field("client_version")); + } + client_version = Some(map.next_value()?); + } + Field::ClientName => { + if client_name.is_some() { + return Err(de::Error::duplicate_field("client_name")); + } + client_name = Some(map.next_value()?); + } + } + } + + let client_version = + client_version.ok_or_else(|| de::Error::missing_field("client_version"))?; + let client_name = + client_name.ok_or_else(|| de::Error::missing_field("client_name"))?; + + Ok(wit::ClientHello { + client_version, + client_name, + }) + } + } + + const FIELDS: &[&str] = &["client_version", "client_name"]; + deserializer.deserialize_struct("ClientHello", FIELDS, ClientHelloVisitor) + } +} + +// ServerWelcome +impl Serialize for wit::ServerWelcome { + fn serialize(&self, serializer: S) -> Result + where + S: serde::ser::Serializer, + { + let mut state = serializer.serialize_struct("ServerWelcome", 4)?; + state.serialize_field("server_version", &self.server_version)?; + state.serialize_field("supported_operations", &self.supported_operations)?; + state.serialize_field("supported_chains", &self.supported_chains)?; + state.serialize_field("features", &self.features)?; + state.end() + } +} + +impl<'a> Deserialize<'a> for wit::ServerWelcome { + fn deserialize(deserializer: D) -> Result + where + D: serde::de::Deserializer<'a>, + { + #[derive(Deserialize)] + #[serde(field_identifier, rename_all = "snake_case")] + enum Field { + ServerVersion, + SupportedOperations, + SupportedChains, + Features, + } + + struct ServerWelcomeVisitor; + + impl<'de> Visitor<'de> for ServerWelcomeVisitor { + type Value = wit::ServerWelcome; + + fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + formatter.write_str("struct ServerWelcome") + } + + fn visit_map(self, mut map: V) -> Result + where + V: MapAccess<'de>, + { + let mut server_version = None; + let mut supported_operations = None; + let mut supported_chains = None; + let mut features = None; + + while let Some(key) = map.next_key()? { + match key { + Field::ServerVersion => { + if server_version.is_some() { + return Err(de::Error::duplicate_field("server_version")); + } + server_version = Some(map.next_value()?); + } + Field::SupportedOperations => { + if supported_operations.is_some() { + return Err(de::Error::duplicate_field("supported_operations")); + } + supported_operations = Some(map.next_value()?); + } + Field::SupportedChains => { + if supported_chains.is_some() { + return Err(de::Error::duplicate_field("supported_chains")); + } + supported_chains = Some(map.next_value()?); + } + Field::Features => { + if features.is_some() { + return Err(de::Error::duplicate_field("features")); + } + features = Some(map.next_value()?); + } + } + } + + let server_version = + server_version.ok_or_else(|| de::Error::missing_field("server_version"))?; + let supported_operations = supported_operations + .ok_or_else(|| de::Error::missing_field("supported_operations"))?; + let supported_chains = + supported_chains.ok_or_else(|| de::Error::missing_field("supported_chains"))?; + let features = features.ok_or_else(|| de::Error::missing_field("features"))?; + + Ok(wit::ServerWelcome { + server_version, + supported_operations, + supported_chains, + features, + }) + } + } + + const FIELDS: &[&str] = &[ + "server_version", + "supported_operations", + "supported_chains", + "features", + ]; + deserializer.deserialize_struct("ServerWelcome", FIELDS, ServerWelcomeVisitor) + } +} + +// RegisterRequest +impl Serialize for wit::RegisterRequest { + fn serialize(&self, serializer: S) -> Result + where + S: serde::ser::Serializer, + { + let mut state = serializer.serialize_struct("RegisterRequest", 2)?; + state.serialize_field("required_operations", &self.required_operations)?; + state.serialize_field("spending_limits", &self.spending_limits)?; + state.end() + } +} + +impl<'a> Deserialize<'a> for wit::RegisterRequest { + fn deserialize(deserializer: D) -> Result + where + D: serde::de::Deserializer<'a>, + { + #[derive(Deserialize)] + #[serde(field_identifier, rename_all = "snake_case")] + enum Field { + RequiredOperations, + SpendingLimits, + } + + struct RegisterRequestVisitor; + + impl<'de> Visitor<'de> for RegisterRequestVisitor { + type Value = wit::RegisterRequest; + + fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + formatter.write_str("struct RegisterRequest") + } + + fn visit_map(self, mut map: V) -> Result + where + V: MapAccess<'de>, + { + let mut required_operations = None; + let mut spending_limits = None; + + while let Some(key) = map.next_key()? { + match key { + Field::RequiredOperations => { + if required_operations.is_some() { + return Err(de::Error::duplicate_field("required_operations")); + } + required_operations = Some(map.next_value()?); + } + Field::SpendingLimits => { + if spending_limits.is_some() { + return Err(de::Error::duplicate_field("spending_limits")); + } + spending_limits = Some(map.next_value()?); + } + } + } + + let required_operations = required_operations + .ok_or_else(|| de::Error::missing_field("required_operations"))?; + + Ok(wit::RegisterRequest { + required_operations, + spending_limits, + }) + } + } + + const FIELDS: &[&str] = &["required_operations", "spending_limits"]; + deserializer.deserialize_struct("RegisterRequest", FIELDS, RegisterRequestVisitor) + } +} + +// CompleteHandshake +impl Serialize for wit::CompleteHandshake { + fn serialize(&self, serializer: S) -> Result + where + S: serde::ser::Serializer, + { + let mut state = serializer.serialize_struct("CompleteHandshake", 2)?; + state.serialize_field("session_id", &self.session_id)?; + state.serialize_field("registered_permissions", &self.registered_permissions)?; + state.end() + } +} + +impl<'a> Deserialize<'a> for wit::CompleteHandshake { + fn deserialize(deserializer: D) -> Result + where + D: serde::de::Deserializer<'a>, + { + #[derive(Deserialize)] + #[serde(field_identifier, rename_all = "snake_case")] + enum Field { + SessionId, + RegisteredPermissions, + } + + struct CompleteHandshakeVisitor; + + impl<'de> Visitor<'de> for CompleteHandshakeVisitor { + type Value = wit::CompleteHandshake; + + fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + formatter.write_str("struct CompleteHandshake") + } + + fn visit_map(self, mut map: V) -> Result + where + V: MapAccess<'de>, + { + let mut session_id = None; + let mut registered_permissions = None; + + while let Some(key) = map.next_key()? { + match key { + Field::SessionId => { + if session_id.is_some() { + return Err(de::Error::duplicate_field("session_id")); + } + session_id = Some(map.next_value()?); + } + Field::RegisteredPermissions => { + if registered_permissions.is_some() { + return Err(de::Error::duplicate_field("registered_permissions")); + } + registered_permissions = Some(map.next_value()?); + } + } + } + + let session_id = + session_id.ok_or_else(|| de::Error::missing_field("session_id"))?; + let registered_permissions = registered_permissions + .ok_or_else(|| de::Error::missing_field("registered_permissions"))?; + + Ok(wit::CompleteHandshake { + session_id, + registered_permissions, + }) + } + } + + const FIELDS: &[&str] = &["session_id", "registered_permissions"]; + deserializer.deserialize_struct("CompleteHandshake", FIELDS, CompleteHandshakeVisitor) + } +} diff --git a/src/hyperwallet_client/serde_request_response_impls.rs b/src/hyperwallet_client/serde_request_response_impls.rs new file mode 100644 index 0000000..88bc9d4 --- /dev/null +++ b/src/hyperwallet_client/serde_request_response_impls.rs @@ -0,0 +1,127 @@ +use crate::hyperware::process::hyperwallet as wit; +use serde::de::{self}; +use serde::ser::Serialize; +use serde::Deserialize; + +// Create a macro to generate stub implementations for request/response types +macro_rules! impl_stub_serde { + ($type:ty, $name:literal) => { + impl Serialize for $type { + fn serialize(&self, serializer: S) -> Result + where + S: serde::ser::Serializer, + { + // For now, serialize as debug representation + let json_val = serde_json::json!({ + "type": $name, + "data": format!("{:?}", self) + }); + json_val.serialize(serializer) + } + } + + impl<'a> Deserialize<'a> for $type { + fn deserialize(deserializer: D) -> Result<$type, D::Error> + where + D: serde::de::Deserializer<'a>, + { + // The hyperwallet service is sending the response wrapped in a debug format + // We need to deserialize the actual JSON payload + let val = serde_json::Value::deserialize(deserializer)?; + + // Try normal JSON deserialization first + match serde_json::from_value(val.clone()) { + Ok(result) => Ok(result), + Err(_) => { + // If that fails, it might be wrapped in the debug format + // For now, we can't parse debug format strings back to structs + // This is a fundamental issue with the hyperwallet service + Err(de::Error::custom(format!( + "{} cannot be deserialized from debug format. The hyperwallet service needs to be fixed to return proper JSON.", + $name + ))) + } + } + } + } + }; +} + +// Request types +impl_stub_serde!( + wit::UpdateSpendingLimitsRequest, + "UpdateSpendingLimitsRequest" +); +impl_stub_serde!(wit::ImportWalletRequest, "ImportWalletRequest"); +impl_stub_serde!(wit::DeleteWalletRequest, "DeleteWalletRequest"); +impl_stub_serde!(wit::RenameWalletRequest, "RenameWalletRequest"); +impl_stub_serde!(wit::ExportWalletRequest, "ExportWalletRequest"); +impl_stub_serde!(wit::GetWalletInfoRequest, "GetWalletInfoRequest"); +impl_stub_serde!(wit::SendEthRequest, "SendEthRequest"); +impl_stub_serde!(wit::SendTokenRequest, "SendTokenRequest"); +impl_stub_serde!(wit::ApproveTokenRequest, "ApproveTokenRequest"); +impl_stub_serde!(wit::GetBalanceRequest, "GetBalanceRequest"); +impl_stub_serde!(wit::GetTokenBalanceRequest, "GetTokenBalanceRequest"); +impl_stub_serde!(wit::CallContractRequest, "CallContractRequest"); +impl_stub_serde!(wit::SignTransactionRequest, "SignTransactionRequest"); +impl_stub_serde!(wit::SignMessageRequest, "SignMessageRequest"); +impl_stub_serde!( + wit::BuildAndSignUserOperationForPaymentRequest, + "BuildAndSignUserOperationForPaymentRequest" +); +impl_stub_serde!( + wit::SubmitUserOperationRequest, + "SubmitUserOperationRequest" +); +impl_stub_serde!( + wit::GetUserOperationReceiptRequest, + "GetUserOperationReceiptRequest" +); +impl_stub_serde!( + wit::GetTransactionHistoryRequest, + "GetTransactionHistoryRequest" +); +impl_stub_serde!(wit::EstimateGasRequest, "EstimateGasRequest"); + +// Response types +impl_stub_serde!(wit::UnlockWalletResponse, "UnlockWalletResponse"); +impl_stub_serde!(wit::CreateWalletResponse, "CreateWalletResponse"); +impl_stub_serde!(wit::ImportWalletResponse, "ImportWalletResponse"); +impl_stub_serde!(wit::DeleteWalletResponse, "DeleteWalletResponse"); +impl_stub_serde!(wit::ExportWalletResponse, "ExportWalletResponse"); +impl_stub_serde!(wit::ListWalletsResponse, "ListWalletsResponse"); +impl_stub_serde!(wit::GetWalletInfoResponse, "GetWalletInfoResponse"); +impl_stub_serde!(wit::GetBalanceResponse, "GetBalanceResponse"); +impl_stub_serde!(wit::GetTokenBalanceResponse, "GetTokenBalanceResponse"); +impl_stub_serde!(wit::SendEthResponse, "SendEthResponse"); +impl_stub_serde!(wit::SendTokenResponse, "SendTokenResponse"); +impl_stub_serde!(wit::BuildAndSignUserOperationResponse,"BuildAndSignUserOperationResponse"); +impl_stub_serde!(wit::SubmitUserOperationResponse,"SubmitUserOperationResponse"); +impl_stub_serde!(wit::UserOperationReceiptResponse,"UserOperationReceiptResponse"); + +// Other request types +impl_stub_serde!(wit::GetTransactionReceiptRequest,"GetTransactionReceiptRequest"); +impl_stub_serde!(wit::BuildUserOperationRequest, "BuildUserOperationRequest"); +impl_stub_serde!(wit::SignUserOperationRequest, "SignUserOperationRequest"); +impl_stub_serde!(wit::BuildAndSignUserOperationRequest,"BuildAndSignUserOperationRequest"); +impl_stub_serde!(wit::EstimateUserOperationGasRequest,"EstimateUserOperationGasRequest"); +impl_stub_serde!(wit::ConfigurePaymasterRequest, "ConfigurePaymasterRequest"); +impl_stub_serde!(wit::ExecuteViaTbaRequest, "ExecuteViaTbaRequest"); +impl_stub_serde!(wit::CheckTbaOwnershipRequest, "CheckTbaOwnershipRequest"); +impl_stub_serde!(wit::SetupTbaDelegationRequest, "SetupTbaDelegationRequest"); +impl_stub_serde!(wit::CreateNoteRequest, "CreateNoteRequest"); +impl_stub_serde!(wit::ReadNoteRequest, "ReadNoteRequest"); +impl_stub_serde!(wit::ResolveIdentityRequest, "ResolveIdentityRequest"); +impl_stub_serde!(wit::SetupDelegationRequest, "SetupDelegationRequest"); +impl_stub_serde!(wit::VerifyDelegationRequest, "VerifyDelegationRequest"); +impl_stub_serde!(wit::MintEntryRequest, "MintEntryRequest"); + +// Other response types +impl_stub_serde!(wit::CreateNoteResponse, "CreateNoteResponse"); +impl_stub_serde!(wit::ExecuteViaTbaResponse, "ExecuteViaTbaResponse"); +impl_stub_serde!(wit::CheckTbaOwnershipResponse, "CheckTbaOwnershipResponse"); + +// Other types that may need serde +impl_stub_serde!(wit::Balance, "Balance"); +impl_stub_serde!(wit::Wallet, "Wallet"); +impl_stub_serde!(wit::WalletSpendingLimits, "WalletSpendingLimits"); diff --git a/src/hyperwallet_client/serde_response_impls.rs b/src/hyperwallet_client/serde_response_impls.rs new file mode 100644 index 0000000..2d4e764 --- /dev/null +++ b/src/hyperwallet_client/serde_response_impls.rs @@ -0,0 +1,2028 @@ +// Proper serde implementations for request/response types + +use crate::hyperware::process::hyperwallet as wit; +// no direct use of serde::de; rely on derives and serde_json where necessary +use serde::ser::SerializeStruct; +use serde::{Deserialize, Serialize}; + +// ============== REQUEST TYPES ============== +// SetWalletLimitsRequest +impl Serialize for wit::SetWalletLimitsRequest { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let mut state = serializer.serialize_struct("SetWalletLimitsRequest", 2)?; + state.serialize_field("wallet_id", &self.wallet_id)?; + state.serialize_field("limits", &self.limits)?; + state.end() + } +} + +impl<'de> Deserialize<'de> for wit::SetWalletLimitsRequest { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + #[derive(Deserialize)] + struct Helper { + wallet_id: String, + limits: wit::WalletSpendingLimits, + } + let h = Helper::deserialize(deserializer)?; + Ok(wit::SetWalletLimitsRequest { + wallet_id: h.wallet_id, + limits: h.limits, + }) + } +} + +// ImportWalletRequest +impl Serialize for wit::ImportWalletRequest { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let field_count = if self.password.is_some() { 3 } else { 2 }; + let mut state = serializer.serialize_struct("ImportWalletRequest", field_count)?; + state.serialize_field("name", &self.name)?; + state.serialize_field("private_key", &self.private_key)?; + if let Some(ref pwd) = self.password { + state.serialize_field("password", pwd)?; + } + state.end() + } +} + +impl<'de> Deserialize<'de> for wit::ImportWalletRequest { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + #[derive(Deserialize)] + struct Helper { + name: String, + private_key: String, + password: Option, + } + let h = Helper::deserialize(deserializer)?; + Ok(wit::ImportWalletRequest { + name: h.name, + private_key: h.private_key, + password: h.password, + }) + } +} + +// DeleteWalletRequest +impl Serialize for wit::DeleteWalletRequest { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let mut state = serializer.serialize_struct("DeleteWalletRequest", 1)?; + state.serialize_field("wallet_id", &self.wallet_id)?; + state.end() + } +} + +impl<'de> Deserialize<'de> for wit::DeleteWalletRequest { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + #[derive(Deserialize)] + struct Helper { + wallet_id: String, + } + let h = Helper::deserialize(deserializer)?; + Ok(wit::DeleteWalletRequest { + wallet_id: h.wallet_id, + }) + } +} + +// RenameWalletRequest +impl Serialize for wit::RenameWalletRequest { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let mut state = serializer.serialize_struct("RenameWalletRequest", 2)?; + state.serialize_field("wallet_id", &self.wallet_id)?; + state.serialize_field("new_name", &self.new_name)?; + state.end() + } +} + +impl<'de> Deserialize<'de> for wit::RenameWalletRequest { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + #[derive(Deserialize)] + struct Helper { + wallet_id: String, + new_name: String, + } + let h = Helper::deserialize(deserializer)?; + Ok(wit::RenameWalletRequest { + wallet_id: h.wallet_id, + new_name: h.new_name, + }) + } +} + +// ExportWalletRequest +impl Serialize for wit::ExportWalletRequest { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let field_count = if self.password.is_some() { 2 } else { 1 }; + let mut state = serializer.serialize_struct("ExportWalletRequest", field_count)?; + state.serialize_field("wallet_id", &self.wallet_id)?; + if let Some(ref pwd) = self.password { + state.serialize_field("password", pwd)?; + } + state.end() + } +} + +impl<'de> Deserialize<'de> for wit::ExportWalletRequest { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + #[derive(Deserialize)] + struct Helper { + wallet_id: String, + password: Option, + } + let h = Helper::deserialize(deserializer)?; + Ok(wit::ExportWalletRequest { + wallet_id: h.wallet_id, + password: h.password, + }) + } +} + +// GetWalletInfoRequest +impl Serialize for wit::GetWalletInfoRequest { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let mut state = serializer.serialize_struct("GetWalletInfoRequest", 1)?; + state.serialize_field("wallet_id", &self.wallet_id)?; + state.end() + } +} + +impl<'de> Deserialize<'de> for wit::GetWalletInfoRequest { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + #[derive(Deserialize)] + struct Helper { + wallet_id: String, + } + let h = Helper::deserialize(deserializer)?; + Ok(wit::GetWalletInfoRequest { + wallet_id: h.wallet_id, + }) + } +} + +// SendEthRequest +impl Serialize for wit::SendEthRequest { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let mut state = serializer.serialize_struct("SendEthRequest", 3)?; + state.serialize_field("wallet_id", &self.wallet_id)?; + state.serialize_field("to", &self.to)?; + state.serialize_field("amount", &self.amount)?; + state.end() + } +} + +impl<'de> Deserialize<'de> for wit::SendEthRequest { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + #[derive(Deserialize)] + struct Helper { + wallet_id: String, + to: String, + amount: String, + } + let h = Helper::deserialize(deserializer)?; + Ok(wit::SendEthRequest { + wallet_id: h.wallet_id, + to: h.to, + amount: h.amount, + }) + } +} + +// SendTokenRequest +impl Serialize for wit::SendTokenRequest { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let mut state = serializer.serialize_struct("SendTokenRequest", 4)?; + state.serialize_field("wallet_id", &self.wallet_id)?; + state.serialize_field("token_address", &self.token_address)?; + state.serialize_field("to", &self.to)?; + state.serialize_field("amount", &self.amount)?; + state.end() + } +} + +impl<'de> Deserialize<'de> for wit::SendTokenRequest { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + #[derive(Deserialize)] + struct Helper { + wallet_id: String, + token_address: String, + to: String, + amount: String, + } + let h = Helper::deserialize(deserializer)?; + Ok(wit::SendTokenRequest { + wallet_id: h.wallet_id, + token_address: h.token_address, + to: h.to, + amount: h.amount, + }) + } +} + +// GetBalanceRequest +impl Serialize for wit::GetBalanceRequest { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let mut state = serializer.serialize_struct("GetBalanceRequest", 1)?; + state.serialize_field("wallet_id", &self.wallet_id)?; + state.end() + } +} + +impl<'de> Deserialize<'de> for wit::GetBalanceRequest { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + #[derive(Deserialize)] + struct Helper { + wallet_id: String, + } + let h = Helper::deserialize(deserializer)?; + Ok(wit::GetBalanceRequest { + wallet_id: h.wallet_id, + }) + } +} + +// GetTokenBalanceRequest +impl Serialize for wit::GetTokenBalanceRequest { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let mut state = serializer.serialize_struct("GetTokenBalanceRequest", 2)?; + state.serialize_field("wallet_id", &self.wallet_id)?; + state.serialize_field("token_address", &self.token_address)?; + state.end() + } +} + +impl<'de> Deserialize<'de> for wit::GetTokenBalanceRequest { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + #[derive(Deserialize)] + struct Helper { + wallet_id: String, + token_address: String, + } + let h = Helper::deserialize(deserializer)?; + Ok(wit::GetTokenBalanceRequest { + wallet_id: h.wallet_id, + token_address: h.token_address, + }) + } +} + +// BuildAndSignUserOperationForPaymentRequest +impl Serialize for wit::BuildAndSignUserOperationForPaymentRequest { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let mut state = + serializer.serialize_struct("BuildAndSignUserOperationForPaymentRequest", 7)?; + state.serialize_field("eoa_wallet_id", &self.eoa_wallet_id)?; + state.serialize_field("tba_address", &self.tba_address)?; + state.serialize_field("target", &self.target)?; + state.serialize_field("call_data", &self.call_data)?; + state.serialize_field("use_paymaster", &self.use_paymaster)?; + state.serialize_field("paymaster_config", &self.paymaster_config)?; + state.serialize_field("password", &self.password)?; + state.end() + } +} + +impl<'de> Deserialize<'de> for wit::BuildAndSignUserOperationForPaymentRequest { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + #[derive(Deserialize)] + struct Helper { + eoa_wallet_id: String, + tba_address: String, + target: String, + call_data: String, + use_paymaster: bool, + paymaster_config: Option, + password: Option, + } + let h = Helper::deserialize(deserializer)?; + Ok(wit::BuildAndSignUserOperationForPaymentRequest { + eoa_wallet_id: h.eoa_wallet_id, + tba_address: h.tba_address, + target: h.target, + call_data: h.call_data, + use_paymaster: h.use_paymaster, + paymaster_config: h.paymaster_config, + password: h.password, + }) + } +} + +// SubmitUserOperationRequest +impl Serialize for wit::SubmitUserOperationRequest { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let mut state = serializer.serialize_struct("SubmitUserOperationRequest", 3)?; + state.serialize_field("signed_user_operation", &self.signed_user_operation)?; + state.serialize_field("entry_point", &self.entry_point)?; + state.serialize_field("bundler_url", &self.bundler_url)?; + state.end() + } +} + +impl<'de> Deserialize<'de> for wit::SubmitUserOperationRequest { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + #[derive(Deserialize)] + struct Helper { + signed_user_operation: String, + entry_point: String, + bundler_url: Option, + } + let h = Helper::deserialize(deserializer)?; + Ok(wit::SubmitUserOperationRequest { + signed_user_operation: h.signed_user_operation, + entry_point: h.entry_point, + bundler_url: h.bundler_url, + }) + } +} + +// GetUserOperationReceiptRequest +impl Serialize for wit::GetUserOperationReceiptRequest { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let mut state = serializer.serialize_struct("GetUserOperationReceiptRequest", 1)?; + state.serialize_field("user_op_hash", &self.user_op_hash)?; + state.end() + } +} + +impl<'de> Deserialize<'de> for wit::GetUserOperationReceiptRequest { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + #[derive(Deserialize)] + struct Helper { + user_op_hash: String, + } + let h = Helper::deserialize(deserializer)?; + Ok(wit::GetUserOperationReceiptRequest { + user_op_hash: h.user_op_hash, + }) + } +} + +// ============== RESPONSE TYPES ============== +// SetWalletLimitsResponse +impl Serialize for wit::SetWalletLimitsResponse { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let mut state = serializer.serialize_struct("SetWalletLimitsResponse", 3)?; + state.serialize_field("success", &self.success)?; + state.serialize_field("wallet_id", &self.wallet_id)?; + state.serialize_field("message", &self.message)?; + state.end() + } +} + +impl<'de> Deserialize<'de> for wit::SetWalletLimitsResponse { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + #[derive(Deserialize)] + struct Helper { + success: bool, + wallet_id: String, + message: String, + } + let h = Helper::deserialize(deserializer)?; + Ok(wit::SetWalletLimitsResponse { + success: h.success, + wallet_id: h.wallet_id, + message: h.message, + }) + } +} + +// CreateWalletResponse +impl Serialize for wit::CreateWalletResponse { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let mut state = serializer.serialize_struct("CreateWalletResponse", 3)?; + state.serialize_field("wallet_id", &self.wallet_id)?; + state.serialize_field("address", &self.address)?; + state.serialize_field("name", &self.name)?; + state.end() + } +} + +impl<'de> Deserialize<'de> for wit::CreateWalletResponse { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + #[derive(Deserialize)] + struct Helper { + wallet_id: String, + address: String, + name: String, + } + let h = Helper::deserialize(deserializer)?; + Ok(wit::CreateWalletResponse { + wallet_id: h.wallet_id, + address: h.address, + name: h.name, + }) + } +} + +// ImportWalletResponse +impl Serialize for wit::ImportWalletResponse { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let mut state = serializer.serialize_struct("ImportWalletResponse", 3)?; + state.serialize_field("wallet_id", &self.wallet_id)?; + state.serialize_field("address", &self.address)?; + state.serialize_field("name", &self.name)?; + state.end() + } +} + +impl<'de> Deserialize<'de> for wit::ImportWalletResponse { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + #[derive(Deserialize)] + struct Helper { + wallet_id: String, + address: String, + name: String, + } + let h = Helper::deserialize(deserializer)?; + Ok(wit::ImportWalletResponse { + wallet_id: h.wallet_id, + address: h.address, + name: h.name, + }) + } +} + +// DeleteWalletResponse +impl Serialize for wit::DeleteWalletResponse { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let mut state = serializer.serialize_struct("DeleteWalletResponse", 3)?; + state.serialize_field("success", &self.success)?; + state.serialize_field("wallet_id", &self.wallet_id)?; + state.serialize_field("message", &self.message)?; + state.end() + } +} + +impl<'de> Deserialize<'de> for wit::DeleteWalletResponse { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + #[derive(Deserialize)] + struct Helper { + success: bool, + wallet_id: String, + message: String, + } + let h = Helper::deserialize(deserializer)?; + Ok(wit::DeleteWalletResponse { + success: h.success, + wallet_id: h.wallet_id, + message: h.message, + }) + } +} + +// ExportWalletResponse +impl Serialize for wit::ExportWalletResponse { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let mut state = serializer.serialize_struct("ExportWalletResponse", 2)?; + state.serialize_field("address", &self.address)?; + state.serialize_field("private_key", &self.private_key)?; + state.end() + } +} + +impl<'de> Deserialize<'de> for wit::ExportWalletResponse { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + #[derive(Deserialize)] + struct Helper { + address: String, + private_key: String, + } + let h = Helper::deserialize(deserializer)?; + Ok(wit::ExportWalletResponse { + address: h.address, + private_key: h.private_key, + }) + } +} + +// ListWalletsResponse +impl Serialize for wit::ListWalletsResponse { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let mut state = serializer.serialize_struct("ListWalletsResponse", 3)?; + state.serialize_field("process", &self.process)?; + state.serialize_field("wallets", &self.wallets)?; + state.serialize_field("total", &self.total)?; + state.end() + } +} + +impl<'de> Deserialize<'de> for wit::ListWalletsResponse { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + #[derive(Deserialize)] + struct Helper { + process: String, + wallets: Vec, + total: u64, + } + let h = Helper::deserialize(deserializer)?; + Ok(wit::ListWalletsResponse { + process: h.process, + wallets: h.wallets, + total: h.total, + }) + } +} + +// GetWalletInfoResponse +impl Serialize for wit::GetWalletInfoResponse { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let mut state = serializer.serialize_struct("GetWalletInfoResponse", 5)?; + state.serialize_field("wallet_id", &self.wallet_id)?; + state.serialize_field("address", &self.address)?; + state.serialize_field("name", &self.name)?; + state.serialize_field("chain_id", &self.chain_id)?; + state.serialize_field("is_locked", &self.is_locked)?; + state.end() + } +} + +impl<'de> Deserialize<'de> for wit::GetWalletInfoResponse { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + #[derive(Deserialize)] + struct Helper { + wallet_id: String, + address: String, + name: String, + chain_id: u64, + is_locked: bool, + } + let h = Helper::deserialize(deserializer)?; + Ok(wit::GetWalletInfoResponse { + wallet_id: h.wallet_id, + address: h.address, + name: h.name, + chain_id: h.chain_id, + is_locked: h.is_locked, + }) + } +} + +// GetBalanceResponse +impl Serialize for wit::GetBalanceResponse { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let mut state = serializer.serialize_struct("GetBalanceResponse", 3)?; + state.serialize_field("balance", &self.balance)?; + state.serialize_field("wallet_id", &self.wallet_id)?; + state.serialize_field("chain_id", &self.chain_id)?; + state.end() + } +} + +impl<'de> Deserialize<'de> for wit::GetBalanceResponse { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + #[derive(Deserialize)] + struct Helper { + balance: wit::Balance, + wallet_id: String, + chain_id: u64, + } + let h = Helper::deserialize(deserializer)?; + Ok(wit::GetBalanceResponse { + balance: h.balance, + wallet_id: h.wallet_id, + chain_id: h.chain_id, + }) + } +} + +// GetTokenBalanceResponse +impl Serialize for wit::GetTokenBalanceResponse { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let mut state = serializer.serialize_struct("GetTokenBalanceResponse", 3)?; + state.serialize_field("balance", &self.balance)?; + state.serialize_field("formatted", &self.formatted)?; + state.serialize_field("decimals", &self.decimals)?; + state.end() + } +} + +impl<'de> Deserialize<'de> for wit::GetTokenBalanceResponse { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + #[derive(Deserialize)] + struct Helper { + balance: String, + formatted: Option, + decimals: Option, + } + let h = Helper::deserialize(deserializer)?; + Ok(wit::GetTokenBalanceResponse { + balance: h.balance, + formatted: h.formatted, + decimals: h.decimals, + }) + } +} + +// SendEthResponse +impl Serialize for wit::SendEthResponse { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let mut state = serializer.serialize_struct("SendEthResponse", 5)?; + state.serialize_field("tx_hash", &self.tx_hash)?; + state.serialize_field("from_address", &self.from_address)?; + state.serialize_field("to_address", &self.to_address)?; + state.serialize_field("amount", &self.amount)?; + state.serialize_field("chain_id", &self.chain_id)?; + state.end() + } +} + +impl<'de> Deserialize<'de> for wit::SendEthResponse { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + #[derive(Deserialize)] + struct Helper { + tx_hash: String, + from_address: String, + to_address: String, + amount: String, + chain_id: u64, + } + let h = Helper::deserialize(deserializer)?; + Ok(wit::SendEthResponse { + tx_hash: h.tx_hash, + from_address: h.from_address, + to_address: h.to_address, + amount: h.amount, + chain_id: h.chain_id, + }) + } +} + +// SendTokenResponse +impl Serialize for wit::SendTokenResponse { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let mut state = serializer.serialize_struct("SendTokenResponse", 6)?; + state.serialize_field("tx_hash", &self.tx_hash)?; + state.serialize_field("from_address", &self.from_address)?; + state.serialize_field("to_address", &self.to_address)?; + state.serialize_field("token_address", &self.token_address)?; + state.serialize_field("amount", &self.amount)?; + state.serialize_field("chain_id", &self.chain_id)?; + state.end() + } +} + +impl<'de> Deserialize<'de> for wit::SendTokenResponse { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + #[derive(Deserialize)] + struct Helper { + tx_hash: String, + from_address: String, + to_address: String, + token_address: String, + amount: String, + chain_id: u64, + } + let h = Helper::deserialize(deserializer)?; + Ok(wit::SendTokenResponse { + tx_hash: h.tx_hash, + from_address: h.from_address, + to_address: h.to_address, + token_address: h.token_address, + amount: h.amount, + chain_id: h.chain_id, + }) + } +} + +// UnlockWalletResponse +impl Serialize for wit::UnlockWalletResponse { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let mut state = serializer.serialize_struct("UnlockWalletResponse", 3)?; + state.serialize_field("success", &self.success)?; + state.serialize_field("wallet_id", &self.wallet_id)?; + state.serialize_field("message", &self.message)?; + state.end() + } +} + +impl<'de> Deserialize<'de> for wit::UnlockWalletResponse { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + #[derive(Deserialize)] + struct Helper { + success: bool, + wallet_id: String, + message: String, + } + let h = Helper::deserialize(deserializer)?; + Ok(wit::UnlockWalletResponse { + success: h.success, + wallet_id: h.wallet_id, + message: h.message, + }) + } +} + +// BuildAndSignUserOperationResponse +impl Serialize for wit::BuildAndSignUserOperationResponse { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let mut state = serializer.serialize_struct("BuildAndSignUserOperationResponse", 3)?; + state.serialize_field("signed_user_operation", &self.signed_user_operation)?; + state.serialize_field("entry_point", &self.entry_point)?; + state.serialize_field("ready_to_submit", &self.ready_to_submit)?; + state.end() + } +} + +impl<'de> Deserialize<'de> for wit::BuildAndSignUserOperationResponse { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + #[derive(Deserialize)] + struct Helper { + signed_user_operation: String, + entry_point: String, + ready_to_submit: bool, + } + let h = Helper::deserialize(deserializer)?; + Ok(wit::BuildAndSignUserOperationResponse { + signed_user_operation: h.signed_user_operation, + entry_point: h.entry_point, + ready_to_submit: h.ready_to_submit, + }) + } +} + +// SubmitUserOperationResponse +impl Serialize for wit::SubmitUserOperationResponse { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let mut state = serializer.serialize_struct("SubmitUserOperationResponse", 1)?; + state.serialize_field("user_op_hash", &self.user_op_hash)?; + state.end() + } +} + +impl<'de> Deserialize<'de> for wit::SubmitUserOperationResponse { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + #[derive(Deserialize)] + struct Helper { + user_op_hash: String, + } + let h = Helper::deserialize(deserializer)?; + Ok(wit::SubmitUserOperationResponse { + user_op_hash: h.user_op_hash, + }) + } +} + +// UserOperationReceiptResponse +impl Serialize for wit::UserOperationReceiptResponse { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let mut state = serializer.serialize_struct("UserOperationReceiptResponse", 3)?; + state.serialize_field("receipt", &self.receipt)?; + state.serialize_field("user_op_hash", &self.user_op_hash)?; + state.serialize_field("status", &self.status)?; + state.end() + } +} + +impl<'de> Deserialize<'de> for wit::UserOperationReceiptResponse { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + #[derive(Deserialize)] + struct Helper { + receipt: Option, + user_op_hash: String, + status: String, + } + let h = Helper::deserialize(deserializer)?; + Ok(wit::UserOperationReceiptResponse { + receipt: h.receipt, + user_op_hash: h.user_op_hash, + status: h.status, + }) + } +} + +// ============== SUPPORTING TYPES ============== + +// Balance +impl Serialize for wit::Balance { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let mut state = serializer.serialize_struct("Balance", 2)?; + state.serialize_field("formatted", &self.formatted)?; + state.serialize_field("raw", &self.raw)?; + state.end() + } +} + +impl<'de> Deserialize<'de> for wit::Balance { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + #[derive(Deserialize)] + struct Helper { + formatted: String, + raw: String, + } + let h = Helper::deserialize(deserializer)?; + Ok(wit::Balance { + formatted: h.formatted, + raw: h.raw, + }) + } +} + +// Wallet +impl Serialize for wit::Wallet { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let mut state = serializer.serialize_struct("Wallet", 7)?; + state.serialize_field("address", &self.address)?; + state.serialize_field("name", &self.name)?; + state.serialize_field("chain_id", &self.chain_id)?; + state.serialize_field("encrypted", &self.encrypted)?; + state.serialize_field("created_at", &self.created_at)?; + state.serialize_field("last_used", &self.last_used)?; + state.serialize_field("spending_limits", &self.spending_limits)?; + state.end() + } +} + +impl<'de> Deserialize<'de> for wit::Wallet { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + #[derive(Deserialize)] + struct Helper { + address: String, + name: Option, + chain_id: u64, + encrypted: bool, + created_at: Option, + last_used: Option, + spending_limits: Option, + } + let h = Helper::deserialize(deserializer)?; + Ok(wit::Wallet { + address: h.address, + name: h.name, + chain_id: h.chain_id, + encrypted: h.encrypted, + created_at: h.created_at, + last_used: h.last_used, + spending_limits: h.spending_limits, + }) + } +} + +// WalletSpendingLimits +impl Serialize for wit::WalletSpendingLimits { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let mut state = serializer.serialize_struct("WalletSpendingLimits", 6)?; + state.serialize_field("max_per_call", &self.max_per_call)?; + state.serialize_field("max_total", &self.max_total)?; + state.serialize_field("currency", &self.currency)?; + state.serialize_field("total_spent", &self.total_spent)?; + state.serialize_field("set_at", &self.set_at)?; + state.serialize_field("updated_at", &self.updated_at)?; + state.end() + } +} + +impl<'de> Deserialize<'de> for wit::WalletSpendingLimits { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + #[derive(Deserialize)] + struct Helper { + max_per_call: Option, + max_total: Option, + currency: String, + total_spent: String, + set_at: Option, + updated_at: Option, + } + let h = Helper::deserialize(deserializer)?; + Ok(wit::WalletSpendingLimits { + max_per_call: h.max_per_call, + max_total: h.max_total, + currency: h.currency, + total_spent: h.total_spent, + set_at: h.set_at, + updated_at: h.updated_at, + }) + } +} + +// ============== MISSING REQUEST TYPES ============== + +// UpdateSpendingLimitsRequest +impl Serialize for wit::UpdateSpendingLimitsRequest { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let mut state = serializer.serialize_struct("UpdateSpendingLimitsRequest", 1)?; + state.serialize_field("new_limits", &self.new_limits)?; + state.end() + } +} + +impl<'de> Deserialize<'de> for wit::UpdateSpendingLimitsRequest { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + #[derive(Deserialize)] + struct Helper { + new_limits: wit::SpendingLimits, + } + let h = Helper::deserialize(deserializer)?; + Ok(wit::UpdateSpendingLimitsRequest { + new_limits: h.new_limits, + }) + } +} + +// ApproveTokenRequest +impl Serialize for wit::ApproveTokenRequest { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let mut state = serializer.serialize_struct("ApproveTokenRequest", 3)?; + state.serialize_field("token_address", &self.token_address)?; + state.serialize_field("spender", &self.spender)?; + state.serialize_field("amount", &self.amount)?; + state.end() + } +} + +impl<'de> Deserialize<'de> for wit::ApproveTokenRequest { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + #[derive(Deserialize)] + struct Helper { + token_address: String, + spender: String, + amount: String, + } + let h = Helper::deserialize(deserializer)?; + Ok(wit::ApproveTokenRequest { + token_address: h.token_address, + spender: h.spender, + amount: h.amount, + }) + } +} + +// CallContractRequest +impl Serialize for wit::CallContractRequest { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let mut state = serializer.serialize_struct("CallContractRequest", 3)?; + state.serialize_field("to", &self.to)?; + state.serialize_field("data", &self.data)?; + state.serialize_field("value", &self.value)?; + state.end() + } +} + +impl<'de> Deserialize<'de> for wit::CallContractRequest { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + #[derive(Deserialize)] + struct Helper { + to: String, + data: String, + value: Option, + } + let h = Helper::deserialize(deserializer)?; + Ok(wit::CallContractRequest { + to: h.to, + data: h.data, + value: h.value, + }) + } +} + +// SignTransactionRequest +impl Serialize for wit::SignTransactionRequest { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let mut state = serializer.serialize_struct("SignTransactionRequest", 6)?; + state.serialize_field("to", &self.to)?; + state.serialize_field("value", &self.value)?; + state.serialize_field("data", &self.data)?; + state.serialize_field("gas_limit", &self.gas_limit)?; + state.serialize_field("gas_price", &self.gas_price)?; + state.serialize_field("nonce", &self.nonce)?; + state.end() + } +} + +impl<'de> Deserialize<'de> for wit::SignTransactionRequest { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + #[derive(Deserialize)] + struct Helper { + to: String, + value: String, + data: Option, + gas_limit: Option, + gas_price: Option, + nonce: Option, + } + let h = Helper::deserialize(deserializer)?; + Ok(wit::SignTransactionRequest { + to: h.to, + value: h.value, + data: h.data, + gas_limit: h.gas_limit, + gas_price: h.gas_price, + nonce: h.nonce, + }) + } +} + +// MessageType enum (needed for SignMessageRequest) +impl Serialize for wit::MessageType { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + match self { + wit::MessageType::PlainText => { + serializer.serialize_unit_variant("MessageType", 0, "plain_text") + } + wit::MessageType::Eip191 => { + serializer.serialize_unit_variant("MessageType", 1, "eip191") + } + wit::MessageType::Eip712(data) => { + use serde::ser::SerializeStructVariant; + let mut state = + serializer.serialize_struct_variant("MessageType", 2, "eip712", 1)?; + state.serialize_field("data", data)?; + state.end() + } + } + } +} + +impl<'de> Deserialize<'de> for wit::MessageType { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + #[derive(Deserialize)] + #[serde(rename_all = "snake_case")] + enum MessageTypeHelper { + PlainText, + Eip191, + Eip712 { data: wit::Eip712Data }, + } + + match MessageTypeHelper::deserialize(deserializer)? { + MessageTypeHelper::PlainText => Ok(wit::MessageType::PlainText), + MessageTypeHelper::Eip191 => Ok(wit::MessageType::Eip191), + MessageTypeHelper::Eip712 { data } => Ok(wit::MessageType::Eip712(data)), + } + } +} + +// Eip712Data struct (needed for MessageType::Eip712) +impl Serialize for wit::Eip712Data { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let mut state = serializer.serialize_struct("Eip712Data", 2)?; + state.serialize_field("domain", &self.domain)?; + state.serialize_field("types", &self.types)?; + state.end() + } +} + +impl<'de> Deserialize<'de> for wit::Eip712Data { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + #[derive(Deserialize)] + struct Helper { + domain: String, + types: String, + } + let h = Helper::deserialize(deserializer)?; + Ok(wit::Eip712Data { + domain: h.domain, + types: h.types, + }) + } +} + +// SignMessageRequest +impl Serialize for wit::SignMessageRequest { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let mut state = serializer.serialize_struct("SignMessageRequest", 2)?; + state.serialize_field("message", &self.message)?; + state.serialize_field("message_type", &self.message_type)?; + state.end() + } +} + +impl<'de> Deserialize<'de> for wit::SignMessageRequest { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + #[derive(Deserialize)] + struct Helper { + message: String, + message_type: wit::MessageType, + } + let h = Helper::deserialize(deserializer)?; + Ok(wit::SignMessageRequest { + message: h.message, + message_type: h.message_type, + }) + } +} + +// GetTransactionHistoryRequest +impl Serialize for wit::GetTransactionHistoryRequest { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let mut state = serializer.serialize_struct("GetTransactionHistoryRequest", 4)?; + state.serialize_field("limit", &self.limit)?; + state.serialize_field("offset", &self.offset)?; + state.serialize_field("from_block", &self.from_block)?; + state.serialize_field("to_block", &self.to_block)?; + state.end() + } +} + +impl<'de> Deserialize<'de> for wit::GetTransactionHistoryRequest { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + #[derive(Deserialize)] + struct Helper { + limit: Option, + offset: Option, + from_block: Option, + to_block: Option, + } + let h = Helper::deserialize(deserializer)?; + Ok(wit::GetTransactionHistoryRequest { + limit: h.limit, + offset: h.offset, + from_block: h.from_block, + to_block: h.to_block, + }) + } +} + +// EstimateGasRequest +impl Serialize for wit::EstimateGasRequest { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let mut state = serializer.serialize_struct("EstimateGasRequest", 3)?; + state.serialize_field("to", &self.to)?; + state.serialize_field("data", &self.data)?; + state.serialize_field("value", &self.value)?; + state.end() + } +} + +impl<'de> Deserialize<'de> for wit::EstimateGasRequest { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + #[derive(Deserialize)] + struct Helper { + to: String, + data: Option, + value: Option, + } + let h = Helper::deserialize(deserializer)?; + Ok(wit::EstimateGasRequest { + to: h.to, + data: h.data, + value: h.value, + }) + } +} + +// GetTransactionReceiptRequest +impl Serialize for wit::GetTransactionReceiptRequest { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let mut state = serializer.serialize_struct("GetTransactionReceiptRequest", 1)?; + state.serialize_field("tx_hash", &self.tx_hash)?; + state.end() + } +} + +impl<'de> Deserialize<'de> for wit::GetTransactionReceiptRequest { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + #[derive(Deserialize)] + struct Helper { + tx_hash: String, + } + let h = Helper::deserialize(deserializer)?; + Ok(wit::GetTransactionReceiptRequest { tx_hash: h.tx_hash }) + } +} + +// BuildUserOperationRequest +impl Serialize for wit::BuildUserOperationRequest { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let mut state = serializer.serialize_struct("BuildUserOperationRequest", 3)?; + state.serialize_field("target", &self.target)?; + state.serialize_field("call_data", &self.call_data)?; + state.serialize_field("value", &self.value)?; + state.end() + } +} + +impl<'de> Deserialize<'de> for wit::BuildUserOperationRequest { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + #[derive(Deserialize)] + struct Helper { + target: String, + call_data: String, + value: Option, + } + let h = Helper::deserialize(deserializer)?; + Ok(wit::BuildUserOperationRequest { + target: h.target, + call_data: h.call_data, + value: h.value, + }) + } +} + +// SignUserOperationRequest +impl Serialize for wit::SignUserOperationRequest { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let mut state = serializer.serialize_struct("SignUserOperationRequest", 2)?; + state.serialize_field("unsigned_user_operation", &self.unsigned_user_operation)?; + state.serialize_field("entry_point", &self.entry_point)?; + state.end() + } +} + +impl<'de> Deserialize<'de> for wit::SignUserOperationRequest { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + #[derive(Deserialize)] + struct Helper { + unsigned_user_operation: String, + entry_point: String, + } + let h = Helper::deserialize(deserializer)?; + Ok(wit::SignUserOperationRequest { + unsigned_user_operation: h.unsigned_user_operation, + entry_point: h.entry_point, + }) + } +} + +// BuildAndSignUserOperationRequest +impl Serialize for wit::BuildAndSignUserOperationRequest { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let mut state = serializer.serialize_struct("BuildAndSignUserOperationRequest", 4)?; + state.serialize_field("target", &self.target)?; + state.serialize_field("call_data", &self.call_data)?; + state.serialize_field("value", &self.value)?; + state.serialize_field("entry_point", &self.entry_point)?; + state.end() + } +} + +impl<'de> Deserialize<'de> for wit::BuildAndSignUserOperationRequest { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + #[derive(Deserialize)] + struct Helper { + target: String, + call_data: String, + value: Option, + entry_point: String, + } + let h = Helper::deserialize(deserializer)?; + Ok(wit::BuildAndSignUserOperationRequest { + target: h.target, + call_data: h.call_data, + value: h.value, + entry_point: h.entry_point, + }) + } +} + +// EstimateUserOperationGasRequest +impl Serialize for wit::EstimateUserOperationGasRequest { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let mut state = serializer.serialize_struct("EstimateUserOperationGasRequest", 2)?; + state.serialize_field("user_operation", &self.user_operation)?; + state.serialize_field("entry_point", &self.entry_point)?; + state.end() + } +} + +impl<'de> Deserialize<'de> for wit::EstimateUserOperationGasRequest { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + #[derive(Deserialize)] + struct Helper { + user_operation: String, + entry_point: String, + } + let h = Helper::deserialize(deserializer)?; + Ok(wit::EstimateUserOperationGasRequest { + user_operation: h.user_operation, + entry_point: h.entry_point, + }) + } +} + +// ConfigurePaymasterRequest +impl Serialize for wit::ConfigurePaymasterRequest { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let mut state = serializer.serialize_struct("ConfigurePaymasterRequest", 4)?; + state.serialize_field("paymaster_address", &self.paymaster_address)?; + state.serialize_field("paymaster_data", &self.paymaster_data)?; + state.serialize_field("verification_gas_limit", &self.verification_gas_limit)?; + state.serialize_field("post_op_gas_limit", &self.post_op_gas_limit)?; + state.end() + } +} + +impl<'de> Deserialize<'de> for wit::ConfigurePaymasterRequest { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + #[derive(Deserialize)] + struct Helper { + paymaster_address: String, + paymaster_data: Option, + verification_gas_limit: String, + post_op_gas_limit: String, + } + let h = Helper::deserialize(deserializer)?; + Ok(wit::ConfigurePaymasterRequest { + paymaster_address: h.paymaster_address, + paymaster_data: h.paymaster_data, + verification_gas_limit: h.verification_gas_limit, + post_op_gas_limit: h.post_op_gas_limit, + }) + } +} + +// ExecuteViaTbaRequest +impl Serialize for wit::ExecuteViaTbaRequest { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let mut state = serializer.serialize_struct("ExecuteViaTbaRequest", 4)?; + state.serialize_field("tba_address", &self.tba_address)?; + state.serialize_field("target", &self.target)?; + state.serialize_field("call_data", &self.call_data)?; + state.serialize_field("value", &self.value)?; + state.end() + } +} + +impl<'de> Deserialize<'de> for wit::ExecuteViaTbaRequest { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + #[derive(Deserialize)] + struct Helper { + tba_address: String, + target: String, + call_data: String, + value: Option, + } + let h = Helper::deserialize(deserializer)?; + Ok(wit::ExecuteViaTbaRequest { + tba_address: h.tba_address, + target: h.target, + call_data: h.call_data, + value: h.value, + }) + } +} + +// CheckTbaOwnershipRequest +impl Serialize for wit::CheckTbaOwnershipRequest { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let mut state = serializer.serialize_struct("CheckTbaOwnershipRequest", 2)?; + state.serialize_field("tba_address", &self.tba_address)?; + state.serialize_field("signer_address", &self.signer_address)?; + state.end() + } +} + +impl<'de> Deserialize<'de> for wit::CheckTbaOwnershipRequest { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + #[derive(Deserialize)] + struct Helper { + tba_address: String, + signer_address: String, + } + let h = Helper::deserialize(deserializer)?; + Ok(wit::CheckTbaOwnershipRequest { + tba_address: h.tba_address, + signer_address: h.signer_address, + }) + } +} + +// SetupTbaDelegationRequest +impl Serialize for wit::SetupTbaDelegationRequest { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let mut state = serializer.serialize_struct("SetupTbaDelegationRequest", 3)?; + state.serialize_field("tba_address", &self.tba_address)?; + state.serialize_field("delegate_address", &self.delegate_address)?; + state.serialize_field("permissions", &self.permissions)?; + state.end() + } +} + +impl<'de> Deserialize<'de> for wit::SetupTbaDelegationRequest { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + #[derive(Deserialize)] + struct Helper { + tba_address: String, + delegate_address: String, + permissions: Vec, + } + let h = Helper::deserialize(deserializer)?; + Ok(wit::SetupTbaDelegationRequest { + tba_address: h.tba_address, + delegate_address: h.delegate_address, + permissions: h.permissions, + }) + } +} + +// CreateNoteRequest +impl Serialize for wit::CreateNoteRequest { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let mut state = serializer.serialize_struct("CreateNoteRequest", 2)?; + state.serialize_field("note_data", &self.note_data)?; + state.serialize_field("metadata", &self.metadata)?; + state.end() + } +} + +impl<'de> Deserialize<'de> for wit::CreateNoteRequest { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + #[derive(Deserialize)] + struct Helper { + note_data: String, + metadata: Option, + } + let h = Helper::deserialize(deserializer)?; + Ok(wit::CreateNoteRequest { + note_data: h.note_data, + metadata: h.metadata, + }) + } +} + +// ReadNoteRequest +impl Serialize for wit::ReadNoteRequest { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let mut state = serializer.serialize_struct("ReadNoteRequest", 1)?; + state.serialize_field("note_id", &self.note_id)?; + state.end() + } +} + +impl<'de> Deserialize<'de> for wit::ReadNoteRequest { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + #[derive(Deserialize)] + struct Helper { + note_id: String, + } + let h = Helper::deserialize(deserializer)?; + Ok(wit::ReadNoteRequest { note_id: h.note_id }) + } +} + +// ResolveIdentityRequest +impl Serialize for wit::ResolveIdentityRequest { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let mut state = serializer.serialize_struct("ResolveIdentityRequest", 1)?; + state.serialize_field("entry_name", &self.entry_name)?; + state.end() + } +} + +impl<'de> Deserialize<'de> for wit::ResolveIdentityRequest { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + #[derive(Deserialize)] + struct Helper { + entry_name: String, + } + let h = Helper::deserialize(deserializer)?; + Ok(wit::ResolveIdentityRequest { + entry_name: h.entry_name, + }) + } +} + +// SetupDelegationRequest +impl Serialize for wit::SetupDelegationRequest { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let mut state = serializer.serialize_struct("SetupDelegationRequest", 3)?; + state.serialize_field("delegate_address", &self.delegate_address)?; + state.serialize_field("permissions", &self.permissions)?; + state.serialize_field("expiry", &self.expiry)?; + state.end() + } +} + +impl<'de> Deserialize<'de> for wit::SetupDelegationRequest { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + #[derive(Deserialize)] + struct Helper { + delegate_address: String, + permissions: Vec, + expiry: Option, + } + let h = Helper::deserialize(deserializer)?; + Ok(wit::SetupDelegationRequest { + delegate_address: h.delegate_address, + permissions: h.permissions, + expiry: h.expiry, + }) + } +} + +// VerifyDelegationRequest +impl Serialize for wit::VerifyDelegationRequest { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let mut state = serializer.serialize_struct("VerifyDelegationRequest", 3)?; + state.serialize_field("delegate_address", &self.delegate_address)?; + state.serialize_field("signature", &self.signature)?; + state.serialize_field("message", &self.message)?; + state.end() + } +} + +impl<'de> Deserialize<'de> for wit::VerifyDelegationRequest { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + #[derive(Deserialize)] + struct Helper { + delegate_address: String, + signature: String, + message: String, + } + let h = Helper::deserialize(deserializer)?; + Ok(wit::VerifyDelegationRequest { + delegate_address: h.delegate_address, + signature: h.signature, + message: h.message, + }) + } +} + +// MintEntryRequest +impl Serialize for wit::MintEntryRequest { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let mut state = serializer.serialize_struct("MintEntryRequest", 2)?; + state.serialize_field("entry_name", &self.entry_name)?; + state.serialize_field("metadata", &self.metadata)?; + state.end() + } +} + +impl<'de> Deserialize<'de> for wit::MintEntryRequest { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + #[derive(Deserialize)] + struct Helper { + entry_name: String, + metadata: String, + } + let h = Helper::deserialize(deserializer)?; + Ok(wit::MintEntryRequest { + entry_name: h.entry_name, + metadata: h.metadata, + }) + } +} + +// ============== MISSING RESPONSE TYPES ============== + +// CreateNoteResponse +impl Serialize for wit::CreateNoteResponse { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let mut state = serializer.serialize_struct("CreateNoteResponse", 3)?; + state.serialize_field("note_id", &self.note_id)?; + state.serialize_field("content_hash", &self.content_hash)?; + state.serialize_field("created_at", &self.created_at)?; + state.end() + } +} + +impl<'de> Deserialize<'de> for wit::CreateNoteResponse { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + #[derive(Deserialize)] + struct Helper { + note_id: String, + content_hash: String, + created_at: u64, + } + let h = Helper::deserialize(deserializer)?; + Ok(wit::CreateNoteResponse { + note_id: h.note_id, + content_hash: h.content_hash, + created_at: h.created_at, + }) + } +} + +// ExecuteViaTbaResponse +impl Serialize for wit::ExecuteViaTbaResponse { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let mut state = serializer.serialize_struct("ExecuteViaTbaResponse", 4)?; + state.serialize_field("tx_hash", &self.tx_hash)?; + state.serialize_field("tba_address", &self.tba_address)?; + state.serialize_field("target_address", &self.target_address)?; + state.serialize_field("success", &self.success)?; + state.end() + } +} + +impl<'de> Deserialize<'de> for wit::ExecuteViaTbaResponse { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + #[derive(Deserialize)] + struct Helper { + tx_hash: String, + tba_address: String, + target_address: String, + success: bool, + } + let h = Helper::deserialize(deserializer)?; + Ok(wit::ExecuteViaTbaResponse { + tx_hash: h.tx_hash, + tba_address: h.tba_address, + target_address: h.target_address, + success: h.success, + }) + } +} + +// CheckTbaOwnershipResponse +impl Serialize for wit::CheckTbaOwnershipResponse { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let mut state = serializer.serialize_struct("CheckTbaOwnershipResponse", 3)?; + state.serialize_field("tba_address", &self.tba_address)?; + state.serialize_field("owner_address", &self.owner_address)?; + state.serialize_field("is_owned", &self.is_owned)?; + state.end() + } +} + +impl<'de> Deserialize<'de> for wit::CheckTbaOwnershipResponse { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + #[derive(Deserialize)] + struct Helper { + tba_address: String, + owner_address: String, + is_owned: bool, + } + let h = Helper::deserialize(deserializer)?; + Ok(wit::CheckTbaOwnershipResponse { + tba_address: h.tba_address, + owner_address: h.owner_address, + is_owned: h.is_owned, + }) + } +} + +// PaymasterConfig +impl Serialize for wit::PaymasterConfig { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let mut state = serializer.serialize_struct("PaymasterConfig", 4)?; + state.serialize_field("is_circle_paymaster", &self.is_circle_paymaster)?; + state.serialize_field("paymaster_address", &self.paymaster_address)?; + state.serialize_field( + "paymaster_verification_gas", + &self.paymaster_verification_gas, + )?; + state.serialize_field("paymaster_post_op_gas", &self.paymaster_post_op_gas)?; + state.end() + } +} + +impl<'de> Deserialize<'de> for wit::PaymasterConfig { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + #[derive(Deserialize)] + struct Helper { + is_circle_paymaster: bool, + paymaster_address: String, + paymaster_verification_gas: String, + paymaster_post_op_gas: String, + } + let h = Helper::deserialize(deserializer)?; + Ok(wit::PaymasterConfig { + is_circle_paymaster: h.is_circle_paymaster, + paymaster_address: h.paymaster_address, + paymaster_verification_gas: h.paymaster_verification_gas, + paymaster_post_op_gas: h.paymaster_post_op_gas, + }) + } +} diff --git a/src/hyperwallet_client/serde_variant_impls.rs b/src/hyperwallet_client/serde_variant_impls.rs new file mode 100644 index 0000000..cca4380 --- /dev/null +++ b/src/hyperwallet_client/serde_variant_impls.rs @@ -0,0 +1,1020 @@ +use crate::hyperware::process::hyperwallet as wit; +use serde::de::{self, MapAccess, Visitor}; +use serde::ser::{Serialize, SerializeStruct}; +use serde::Deserialize; + +// ============================================================================ +// HyperwalletRequest variant type +// ============================================================================ + +impl Serialize for wit::HyperwalletRequest { + fn serialize(&self, serializer: S) -> Result + where + S: serde::ser::Serializer, + { + use wit::HyperwalletRequest::*; + + let mut state = serializer.serialize_struct("HyperwalletRequest", 2)?; + + match self { + Handshake(data) => { + state.serialize_field("type", "Handshake")?; + state.serialize_field("data", data)?; + } + UnlockWallet(data) => { + state.serialize_field("type", "UnlockWallet")?; + state.serialize_field("data", data)?; + } + UpdateSpendingLimits(data) => { + state.serialize_field("type", "UpdateSpendingLimits")?; + state.serialize_field("data", data)?; + } + CreateWallet(data) => { + state.serialize_field("type", "CreateWallet")?; + state.serialize_field("data", data)?; + } + ImportWallet(data) => { + state.serialize_field("type", "ImportWallet")?; + state.serialize_field("data", data)?; + } + DeleteWallet(data) => { + state.serialize_field("type", "DeleteWallet")?; + state.serialize_field("data", data)?; + } + RenameWallet(data) => { + state.serialize_field("type", "RenameWallet")?; + state.serialize_field("data", data)?; + } + ExportWallet(data) => { + state.serialize_field("type", "ExportWallet")?; + state.serialize_field("data", data)?; + } + ListWallets => { + state.serialize_field("type", "ListWallets")?; + state.serialize_field("data", &serde_json::Value::Null)?; + } + GetWalletInfo(data) => { + state.serialize_field("type", "GetWalletInfo")?; + state.serialize_field("data", data)?; + } + SendEth(data) => { + state.serialize_field("type", "SendEth")?; + state.serialize_field("data", data)?; + } + SendToken(data) => { + state.serialize_field("type", "SendToken")?; + state.serialize_field("data", data)?; + } + ApproveToken(data) => { + state.serialize_field("type", "ApproveToken")?; + state.serialize_field("data", data)?; + } + GetBalance(data) => { + state.serialize_field("type", "GetBalance")?; + state.serialize_field("data", data)?; + } + GetTokenBalance(data) => { + state.serialize_field("type", "GetTokenBalance")?; + state.serialize_field("data", data)?; + } + CallContract(data) => { + state.serialize_field("type", "CallContract")?; + state.serialize_field("data", data)?; + } + SignTransaction(data) => { + state.serialize_field("type", "SignTransaction")?; + state.serialize_field("data", data)?; + } + SignMessage(data) => { + state.serialize_field("type", "SignMessage")?; + state.serialize_field("data", data)?; + } + BuildAndSignUserOperationForPayment(data) => { + state.serialize_field("type", "BuildAndSignUserOperationForPayment")?; + state.serialize_field("data", data)?; + } + SubmitUserOperation(data) => { + state.serialize_field("type", "SubmitUserOperation")?; + state.serialize_field("data", data)?; + } + GetUserOperationReceipt(data) => { + state.serialize_field("type", "GetUserOperationReceipt")?; + state.serialize_field("data", data)?; + } + GetTransactionHistory(data) => { + state.serialize_field("type", "GetTransactionHistory")?; + state.serialize_field("data", data)?; + } + EstimateGas(data) => { + state.serialize_field("type", "EstimateGas")?; + state.serialize_field("data", data)?; + } + GetGasPrice => { + state.serialize_field("type", "GetGasPrice")?; + state.serialize_field("data", &serde_json::Value::Null)?; + } + GetTransactionReceipt(data) => { + state.serialize_field("type", "GetTransactionReceipt")?; + state.serialize_field("data", data)?; + } + BuildUserOperation(data) => { + state.serialize_field("type", "BuildUserOperation")?; + state.serialize_field("data", data)?; + } + SignUserOperation(data) => { + state.serialize_field("type", "SignUserOperation")?; + state.serialize_field("data", data)?; + } + BuildAndSignUserOperation(data) => { + state.serialize_field("type", "BuildAndSignUserOperation")?; + state.serialize_field("data", data)?; + } + EstimateUserOperationGas(data) => { + state.serialize_field("type", "EstimateUserOperationGas")?; + state.serialize_field("data", data)?; + } + ConfigurePaymaster(data) => { + state.serialize_field("type", "ConfigurePaymaster")?; + state.serialize_field("data", data)?; + } + ExecuteViaTba(data) => { + state.serialize_field("type", "ExecuteViaTba")?; + state.serialize_field("data", data)?; + } + CheckTbaOwnership(data) => { + state.serialize_field("type", "CheckTbaOwnership")?; + state.serialize_field("data", data)?; + } + SetupTbaDelegation(data) => { + state.serialize_field("type", "SetupTbaDelegation")?; + state.serialize_field("data", data)?; + } + CreateNote(data) => { + state.serialize_field("type", "CreateNote")?; + state.serialize_field("data", data)?; + } + ReadNote(data) => { + state.serialize_field("type", "ReadNote")?; + state.serialize_field("data", data)?; + } + ResolveIdentity(data) => { + state.serialize_field("type", "ResolveIdentity")?; + state.serialize_field("data", data)?; + } + SetupDelegation(data) => { + state.serialize_field("type", "SetupDelegation")?; + state.serialize_field("data", data)?; + } + VerifyDelegation(data) => { + state.serialize_field("type", "VerifyDelegation")?; + state.serialize_field("data", data)?; + } + MintEntry(data) => { + state.serialize_field("type", "MintEntry")?; + state.serialize_field("data", data)?; + } + SetWalletLimits(data) => { + state.serialize_field("type", "SetWalletLimits")?; + state.serialize_field("data", data)?; + } + } + + state.end() + } +} + +impl<'a> Deserialize<'a> for wit::HyperwalletRequest { + fn deserialize(deserializer: D) -> Result + where + D: serde::de::Deserializer<'a>, + { + struct HyperwalletRequestVisitor; + + impl<'de> Visitor<'de> for HyperwalletRequestVisitor { + type Value = wit::HyperwalletRequest; + + fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + formatter.write_str("a HyperwalletRequest variant") + } + + fn visit_map(self, mut map: V) -> Result + where + V: MapAccess<'de>, + { + let mut variant_type = None; + let mut data = None; + + while let Some(key) = map.next_key::()? { + match key.as_str() { + "type" => { + if variant_type.is_some() { + return Err(de::Error::duplicate_field("type")); + } + variant_type = Some(map.next_value::()?); + } + "data" => { + if data.is_some() { + return Err(de::Error::duplicate_field("data")); + } + data = Some(map.next_value::()?); + } + _ => { + let _ = map.next_value::()?; + } + } + } + + let variant_type = variant_type.ok_or_else(|| de::Error::missing_field("type"))?; + + use wit::HyperwalletRequest::*; + match variant_type.as_str() { + "Handshake" => { + let data = data.ok_or_else(|| de::Error::missing_field("data"))?; + let step = serde_json::from_value(data).map_err(|e| { + de::Error::custom(format!("Failed to deserialize HandshakeStep: {}", e)) + })?; + Ok(Handshake(step)) + } + "ImportWallet" => { + let data = data.ok_or_else(|| de::Error::missing_field("data"))?; + let req = serde_json::from_value(data).map_err(|e| { + de::Error::custom(format!( + "Failed to deserialize ImportWalletRequest: {}", + e + )) + })?; + Ok(ImportWallet(req)) + } + "DeleteWallet" => { + let data = data.ok_or_else(|| de::Error::missing_field("data"))?; + let req = serde_json::from_value(data).map_err(|e| { + de::Error::custom(format!( + "Failed to deserialize DeleteWalletRequest: {}", + e + )) + })?; + Ok(DeleteWallet(req)) + } + "RenameWallet" => { + let data = data.ok_or_else(|| de::Error::missing_field("data"))?; + let req = serde_json::from_value(data).map_err(|e| { + de::Error::custom(format!( + "Failed to deserialize RenameWalletRequest: {}", + e + )) + })?; + Ok(RenameWallet(req)) + } + "ExportWallet" => { + let data = data.ok_or_else(|| de::Error::missing_field("data"))?; + let req = serde_json::from_value(data).map_err(|e| { + de::Error::custom(format!( + "Failed to deserialize ExportWalletRequest: {}", + e + )) + })?; + Ok(ExportWallet(req)) + } + "UnlockWallet" => { + let data = data.ok_or_else(|| de::Error::missing_field("data"))?; + let req = serde_json::from_value(data).map_err(|e| { + de::Error::custom(format!( + "Failed to deserialize UnlockWalletRequest: {}", + e + )) + })?; + Ok(UnlockWallet(req)) + } + "CreateWallet" => { + let data = data.ok_or_else(|| de::Error::missing_field("data"))?; + let req = serde_json::from_value(data).map_err(|e| { + de::Error::custom(format!( + "Failed to deserialize CreateWalletRequest: {}", + e + )) + })?; + Ok(CreateWallet(req)) + } + "ListWallets" => Ok(ListWallets), + "GetWalletInfo" => { + let data = data.ok_or_else(|| de::Error::missing_field("data"))?; + let req = serde_json::from_value(data).map_err(|e| { + de::Error::custom(format!( + "Failed to deserialize GetWalletInfoRequest: {}", + e + )) + })?; + Ok(GetWalletInfo(req)) + } + "SendEth" => { + let data = data.ok_or_else(|| de::Error::missing_field("data"))?; + let req = serde_json::from_value(data).map_err(|e| { + de::Error::custom(format!( + "Failed to deserialize SendEthRequest: {}", + e + )) + })?; + Ok(SendEth(req)) + } + "SendToken" => { + let data = data.ok_or_else(|| de::Error::missing_field("data"))?; + let req = serde_json::from_value(data).map_err(|e| { + de::Error::custom(format!( + "Failed to deserialize SendTokenRequest: {}", + e + )) + })?; + Ok(SendToken(req)) + } + "ApproveToken" => { + let data = data.ok_or_else(|| de::Error::missing_field("data"))?; + let req = serde_json::from_value(data).map_err(|e| { + de::Error::custom(format!( + "Failed to deserialize ApproveTokenRequest: {}", + e + )) + })?; + Ok(ApproveToken(req)) + } + "GetBalance" => { + let data = data.ok_or_else(|| de::Error::missing_field("data"))?; + let req = serde_json::from_value(data).map_err(|e| { + de::Error::custom(format!( + "Failed to deserialize GetBalanceRequest: {}", + e + )) + })?; + Ok(GetBalance(req)) + } + "GetTokenBalance" => { + let data = data.ok_or_else(|| de::Error::missing_field("data"))?; + let req = serde_json::from_value(data).map_err(|e| { + de::Error::custom(format!( + "Failed to deserialize GetTokenBalanceRequest: {}", + e + )) + })?; + Ok(GetTokenBalance(req)) + } + "CallContract" => { + let data = data.ok_or_else(|| de::Error::missing_field("data"))?; + let req = serde_json::from_value(data).map_err(|e| { + de::Error::custom(format!( + "Failed to deserialize CallContractRequest: {}", + e + )) + })?; + Ok(CallContract(req)) + } + "SignTransaction" => { + let data = data.ok_or_else(|| de::Error::missing_field("data"))?; + let req = serde_json::from_value(data).map_err(|e| { + de::Error::custom(format!( + "Failed to deserialize SignTransactionRequest: {}", + e + )) + })?; + Ok(SignTransaction(req)) + } + "SignMessage" => { + let data = data.ok_or_else(|| de::Error::missing_field("data"))?; + let req = serde_json::from_value(data).map_err(|e| { + de::Error::custom(format!( + "Failed to deserialize SignMessageRequest: {}", + e + )) + })?; + Ok(SignMessage(req)) + } + "GetTransactionHistory" => { + let data = data.ok_or_else(|| de::Error::missing_field("data"))?; + let req = serde_json::from_value(data).map_err(|e| { + de::Error::custom(format!( + "Failed to deserialize GetTransactionHistoryRequest: {}", + e + )) + })?; + Ok(GetTransactionHistory(req)) + } + "EstimateGas" => { + let data = data.ok_or_else(|| de::Error::missing_field("data"))?; + let req = serde_json::from_value(data).map_err(|e| { + de::Error::custom(format!( + "Failed to deserialize EstimateGasRequest: {}", + e + )) + })?; + Ok(EstimateGas(req)) + } + "GetGasPrice" => Ok(GetGasPrice), + "GetTransactionReceipt" => { + let data = data.ok_or_else(|| de::Error::missing_field("data"))?; + let req = serde_json::from_value(data).map_err(|e| { + de::Error::custom(format!( + "Failed to deserialize GetTransactionReceiptRequest: {}", + e + )) + })?; + Ok(GetTransactionReceipt(req)) + } + "BuildAndSignUserOperationForPayment" => { + let data = data.ok_or_else(|| de::Error::missing_field("data"))?; + let req = serde_json::from_value(data).map_err(|e| { + de::Error::custom(format!( + "Failed to deserialize BuildAndSignUserOperationForPaymentRequest: {}", + e + )) + })?; + Ok(BuildAndSignUserOperationForPayment(req)) + } + "SubmitUserOperation" => { + let data = data.ok_or_else(|| de::Error::missing_field("data"))?; + let req = serde_json::from_value(data).map_err(|e| { + de::Error::custom(format!( + "Failed to deserialize SubmitUserOperationRequest: {}", + e + )) + })?; + Ok(SubmitUserOperation(req)) + } + "GetUserOperationReceipt" => { + let data = data.ok_or_else(|| de::Error::missing_field("data"))?; + let req = serde_json::from_value(data).map_err(|e| { + de::Error::custom(format!( + "Failed to deserialize GetUserOperationReceiptRequest: {}", + e + )) + })?; + Ok(GetUserOperationReceipt(req)) + } + "SetWalletLimits" => { + let data = data.ok_or_else(|| de::Error::missing_field("data"))?; + let req = serde_json::from_value(data).map_err(|e| { + de::Error::custom(format!( + "Failed to deserialize SetWalletLimitsRequest: {}", + e + )) + })?; + Ok(SetWalletLimits(req)) + } + "BuildUserOperation" => { + let data = data.ok_or_else(|| de::Error::missing_field("data"))?; + let req = serde_json::from_value(data).map_err(|e| { + de::Error::custom(format!( + "Failed to deserialize BuildUserOperationRequest: {}", + e + )) + })?; + Ok(BuildUserOperation(req)) + } + "SignUserOperation" => { + let data = data.ok_or_else(|| de::Error::missing_field("data"))?; + let req = serde_json::from_value(data).map_err(|e| { + de::Error::custom(format!( + "Failed to deserialize SignUserOperationRequest: {}", + e + )) + })?; + Ok(SignUserOperation(req)) + } + "BuildAndSignUserOperation" => { + let data = data.ok_or_else(|| de::Error::missing_field("data"))?; + let req = serde_json::from_value(data).map_err(|e| { + de::Error::custom(format!( + "Failed to deserialize BuildAndSignUserOperationRequest: {}", + e + )) + })?; + Ok(BuildAndSignUserOperation(req)) + } + "EstimateUserOperationGas" => { + let data = data.ok_or_else(|| de::Error::missing_field("data"))?; + let req = serde_json::from_value(data).map_err(|e| { + de::Error::custom(format!( + "Failed to deserialize EstimateUserOperationGasRequest: {}", + e + )) + })?; + Ok(EstimateUserOperationGas(req)) + } + "ConfigurePaymaster" => { + let data = data.ok_or_else(|| de::Error::missing_field("data"))?; + let req = serde_json::from_value(data).map_err(|e| { + de::Error::custom(format!( + "Failed to deserialize ConfigurePaymasterRequest: {}", + e + )) + })?; + Ok(ConfigurePaymaster(req)) + } + "ExecuteViaTba" => { + let data = data.ok_or_else(|| de::Error::missing_field("data"))?; + let req = serde_json::from_value(data).map_err(|e| { + de::Error::custom(format!( + "Failed to deserialize ExecuteViaTbaRequest: {}", + e + )) + })?; + Ok(ExecuteViaTba(req)) + } + "CheckTbaOwnership" => { + let data = data.ok_or_else(|| de::Error::missing_field("data"))?; + let req = serde_json::from_value(data).map_err(|e| { + de::Error::custom(format!( + "Failed to deserialize CheckTbaOwnershipRequest: {}", + e + )) + })?; + Ok(CheckTbaOwnership(req)) + } + "SetupTbaDelegation" => { + let data = data.ok_or_else(|| de::Error::missing_field("data"))?; + let req = serde_json::from_value(data).map_err(|e| { + de::Error::custom(format!( + "Failed to deserialize SetupTbaDelegationRequest: {}", + e + )) + })?; + Ok(SetupTbaDelegation(req)) + } + "CreateNote" => { + let data = data.ok_or_else(|| de::Error::missing_field("data"))?; + let req = serde_json::from_value(data).map_err(|e| { + de::Error::custom(format!( + "Failed to deserialize CreateNoteRequest: {}", + e + )) + })?; + Ok(CreateNote(req)) + } + "ReadNote" => { + let data = data.ok_or_else(|| de::Error::missing_field("data"))?; + let req = serde_json::from_value(data).map_err(|e| { + de::Error::custom(format!( + "Failed to deserialize ReadNoteRequest: {}", + e + )) + })?; + Ok(ReadNote(req)) + } + "ResolveIdentity" => { + let data = data.ok_or_else(|| de::Error::missing_field("data"))?; + let req = serde_json::from_value(data).map_err(|e| { + de::Error::custom(format!( + "Failed to deserialize ResolveIdentityRequest: {}", + e + )) + })?; + Ok(ResolveIdentity(req)) + } + "SetupDelegation" => { + let data = data.ok_or_else(|| de::Error::missing_field("data"))?; + let req = serde_json::from_value(data).map_err(|e| { + de::Error::custom(format!( + "Failed to deserialize SetupDelegationRequest: {}", + e + )) + })?; + Ok(SetupDelegation(req)) + } + "VerifyDelegation" => { + let data = data.ok_or_else(|| de::Error::missing_field("data"))?; + let req = serde_json::from_value(data).map_err(|e| { + de::Error::custom(format!( + "Failed to deserialize VerifyDelegationRequest: {}", + e + )) + })?; + Ok(VerifyDelegation(req)) + } + "MintEntry" => { + let data = data.ok_or_else(|| de::Error::missing_field("data"))?; + let req = serde_json::from_value(data).map_err(|e| { + de::Error::custom(format!( + "Failed to deserialize MintEntryRequest: {}", + e + )) + })?; + Ok(MintEntry(req)) + } + _ => { + // For unimplemented variants, return an error with helpful message + Err(de::Error::unknown_variant( + &variant_type, + &[ + "Handshake", + "ImportWallet", + "DeleteWallet", + "RenameWallet", + "ExportWallet", + "UnlockWallet", + "CreateWallet", + "ListWallets", + "GetWalletInfo", + "SendEth", + "SendToken", + "ApproveToken", + "GetBalance", + "GetTokenBalance", + "CallContract", + "SignTransaction", + "SignMessage", + "GetTransactionHistory", + "EstimateGas", + "GetGasPrice", + "GetTransactionReceipt", + "BuildAndSignUserOperationForPayment", + "SubmitUserOperation", + "GetUserOperationReceipt", + "BuildUserOperation", + "SignUserOperation", + "BuildAndSignUserOperation", + "EstimateUserOperationGas", + "ConfigurePaymaster", + "ExecuteViaTba", + "CheckTbaOwnership", + "SetupTbaDelegation", + "CreateNote", + "ReadNote", + "ResolveIdentity", + "SetupDelegation", + "VerifyDelegation", + "MintEntry", + ], + )) + } + } + } + } + + const FIELDS: &[&str] = &["type", "data"]; + deserializer.deserialize_struct("HyperwalletRequest", FIELDS, HyperwalletRequestVisitor) + } +} + +// ============================================================================ +// HyperwalletResponseData variant type +// ============================================================================ + +impl Serialize for wit::HyperwalletResponseData { + fn serialize(&self, serializer: S) -> Result + where + S: serde::ser::Serializer, + { + use wit::HyperwalletResponseData::*; + + let mut state = serializer.serialize_struct("HyperwalletResponseData", 2)?; + + match self { + Handshake(data) => { + state.serialize_field("type", "Handshake")?; + state.serialize_field("data", data)?; + } + UnlockWallet(data) => { + state.serialize_field("type", "UnlockWallet")?; + state.serialize_field("data", data)?; + } + CreateWallet(data) => { + state.serialize_field("type", "CreateWallet")?; + state.serialize_field("data", data)?; + } + ImportWallet(data) => { + state.serialize_field("type", "ImportWallet")?; + state.serialize_field("data", data)?; + } + DeleteWallet(data) => { + state.serialize_field("type", "DeleteWallet")?; + state.serialize_field("data", data)?; + } + ExportWallet(data) => { + state.serialize_field("type", "ExportWallet")?; + state.serialize_field("data", data)?; + } + ListWallets(data) => { + state.serialize_field("type", "ListWallets")?; + state.serialize_field("data", data)?; + } + GetWalletInfo(data) => { + state.serialize_field("type", "GetWalletInfo")?; + state.serialize_field("data", data)?; + } + GetBalance(data) => { + state.serialize_field("type", "GetBalance")?; + state.serialize_field("data", data)?; + } + GetTokenBalance(data) => { + state.serialize_field("type", "GetTokenBalance")?; + state.serialize_field("data", data)?; + } + SendEth(data) => { + state.serialize_field("type", "SendEth")?; + state.serialize_field("data", data)?; + } + SendToken(data) => { + state.serialize_field("type", "SendToken")?; + state.serialize_field("data", data)?; + } + BuildAndSignUserOperationForPayment(data) => { + state.serialize_field("type", "BuildAndSignUserOperationForPayment")?; + state.serialize_field("data", data)?; + } + SubmitUserOperation(data) => { + state.serialize_field("type", "SubmitUserOperation")?; + state.serialize_field("data", data)?; + } + GetUserOperationReceipt(data) => { + state.serialize_field("type", "GetUserOperationReceipt")?; + state.serialize_field("data", data)?; + } + CreateNote(data) => { + state.serialize_field("type", "CreateNote")?; + state.serialize_field("data", data)?; + } + ExecuteViaTba(data) => { + state.serialize_field("type", "ExecuteViaTba")?; + state.serialize_field("data", data)?; + } + CheckTbaOwnership(data) => { + state.serialize_field("type", "CheckTbaOwnership")?; + state.serialize_field("data", data)?; + } + SetWalletLimits(data) => { + state.serialize_field("type", "SetWalletLimits")?; + state.serialize_field("data", data)?; + } + } + + state.end() + } +} + +impl<'a> Deserialize<'a> for wit::HyperwalletResponseData { + fn deserialize(deserializer: D) -> Result + where + D: serde::de::Deserializer<'a>, + { + struct HyperwalletResponseDataVisitor; + + impl<'de> Visitor<'de> for HyperwalletResponseDataVisitor { + type Value = wit::HyperwalletResponseData; + + fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + formatter.write_str("a HyperwalletResponseData variant") + } + + fn visit_map(self, mut map: V) -> Result + where + V: MapAccess<'de>, + { + let mut variant_type = None; + let mut data = None; + + while let Some(key) = map.next_key::()? { + match key.as_str() { + "type" => { + if variant_type.is_some() { + return Err(de::Error::duplicate_field("type")); + } + variant_type = Some(map.next_value::()?); + } + "data" => { + if data.is_some() { + return Err(de::Error::duplicate_field("data")); + } + data = Some(map.next_value::()?); + } + _ => { + let _ = map.next_value::()?; + } + } + } + + let variant_type = variant_type.ok_or_else(|| de::Error::missing_field("type"))?; + + use wit::HyperwalletResponseData::*; + match variant_type.as_str() { + "Handshake" => { + let data = data.ok_or_else(|| de::Error::missing_field("data"))?; + let step = serde_json::from_value(data).map_err(|e| { + de::Error::custom(format!("Failed to deserialize HandshakeStep: {}", e)) + })?; + Ok(Handshake(step)) + } + "ImportWallet" => { + let data = data.ok_or_else(|| de::Error::missing_field("data"))?; + let response = serde_json::from_value(data).map_err(|e| { + de::Error::custom(format!( + "Failed to deserialize ImportWalletResponse: {}", + e + )) + })?; + Ok(ImportWallet(response)) + } + "DeleteWallet" => { + let data = data.ok_or_else(|| de::Error::missing_field("data"))?; + let response = serde_json::from_value(data).map_err(|e| { + de::Error::custom(format!( + "Failed to deserialize DeleteWalletResponse: {}", + e + )) + })?; + Ok(DeleteWallet(response)) + } + "ExportWallet" => { + let data = data.ok_or_else(|| de::Error::missing_field("data"))?; + let response = serde_json::from_value(data).map_err(|e| { + de::Error::custom(format!( + "Failed to deserialize ExportWalletResponse: {}", + e + )) + })?; + Ok(ExportWallet(response)) + } + "ListWallets" => { + let data = data.ok_or_else(|| de::Error::missing_field("data"))?; + let response = serde_json::from_value(data).map_err(|e| { + de::Error::custom(format!( + "Failed to deserialize ListWalletsResponse: {}", + e + )) + })?; + Ok(ListWallets(response)) + } + "GetWalletInfo" => { + let data = data.ok_or_else(|| de::Error::missing_field("data"))?; + let response = serde_json::from_value(data).map_err(|e| { + de::Error::custom(format!( + "Failed to deserialize GetWalletInfoResponse: {}", + e + )) + })?; + Ok(GetWalletInfo(response)) + } + "CreateWallet" => { + let data = data.ok_or_else(|| de::Error::missing_field("data"))?; + let response = serde_json::from_value(data).map_err(|e| { + de::Error::custom(format!( + "Failed to deserialize CreateWalletResponse: {}", + e + )) + })?; + Ok(CreateWallet(response)) + } + "UnlockWallet" => { + let data = data.ok_or_else(|| de::Error::missing_field("data"))?; + let response = serde_json::from_value(data).map_err(|e| { + de::Error::custom(format!( + "Failed to deserialize UnlockWalletResponse: {}", + e + )) + })?; + Ok(UnlockWallet(response)) + } + "SendEth" => { + let data = data.ok_or_else(|| de::Error::missing_field("data"))?; + let response = serde_json::from_value(data).map_err(|e| { + de::Error::custom(format!( + "Failed to deserialize SendEthResponse: {}", + e + )) + })?; + Ok(SendEth(response)) + } + "SendToken" => { + let data = data.ok_or_else(|| de::Error::missing_field("data"))?; + let response = serde_json::from_value(data).map_err(|e| { + de::Error::custom(format!( + "Failed to deserialize SendTokenResponse: {}", + e + )) + })?; + Ok(SendToken(response)) + } + "BuildAndSignUserOperationForPayment" => { + let data = data.ok_or_else(|| de::Error::missing_field("data"))?; + let response = serde_json::from_value(data).map_err(|e| { + de::Error::custom(format!( + "Failed to deserialize BuildAndSignUserOperationResponse: {}", + e + )) + })?; + Ok(BuildAndSignUserOperationForPayment(response)) + } + "SubmitUserOperation" => { + let data = data.ok_or_else(|| de::Error::missing_field("data"))?; + let response = serde_json::from_value(data).map_err(|e| { + de::Error::custom(format!( + "Failed to deserialize SubmitUserOperationResponse: {}", + e + )) + })?; + Ok(SubmitUserOperation(response)) + } + "GetUserOperationReceipt" => { + let data = data.ok_or_else(|| de::Error::missing_field("data"))?; + let response = serde_json::from_value(data).map_err(|e| { + de::Error::custom(format!( + "Failed to deserialize UserOperationReceiptResponse: {}", + e + )) + })?; + Ok(GetUserOperationReceipt(response)) + } + "GetBalance" => { + let data = data.ok_or_else(|| de::Error::missing_field("data"))?; + let response = serde_json::from_value(data).map_err(|e| { + de::Error::custom(format!( + "Failed to deserialize GetBalanceResponse: {}", + e + )) + })?; + Ok(GetBalance(response)) + } + "GetTokenBalance" => { + let data = data.ok_or_else(|| de::Error::missing_field("data"))?; + let response = serde_json::from_value(data).map_err(|e| { + de::Error::custom(format!( + "Failed to deserialize GetTokenBalanceResponse: {}", + e + )) + })?; + Ok(GetTokenBalance(response)) + } + "SetWalletLimits" => { + let data = data.ok_or_else(|| de::Error::missing_field("data"))?; + let response = serde_json::from_value(data).map_err(|e| { + de::Error::custom(format!( + "Failed to deserialize SetWalletLimitsResponse: {}", + e + )) + })?; + Ok(SetWalletLimits(response)) + } + "CreateNote" => { + let data = data.ok_or_else(|| de::Error::missing_field("data"))?; + let response = serde_json::from_value(data).map_err(|e| { + de::Error::custom(format!( + "Failed to deserialize CreateNoteResponse: {}", + e + )) + })?; + Ok(CreateNote(response)) + } + "ExecuteViaTba" => { + let data = data.ok_or_else(|| de::Error::missing_field("data"))?; + let response = serde_json::from_value(data).map_err(|e| { + de::Error::custom(format!( + "Failed to deserialize ExecuteViaTbaResponse: {}", + e + )) + })?; + Ok(ExecuteViaTba(response)) + } + "CheckTbaOwnership" => { + let data = data.ok_or_else(|| de::Error::missing_field("data"))?; + let response = serde_json::from_value(data).map_err(|e| { + de::Error::custom(format!( + "Failed to deserialize CheckTbaOwnershipResponse: {}", + e + )) + })?; + Ok(CheckTbaOwnership(response)) + } + _ => { + // For unimplemented variants, return an error with helpful message + Err(de::Error::unknown_variant( + &variant_type, + &[ + "Handshake", + "ImportWallet", + "DeleteWallet", + "ExportWallet", + "ListWallets", + "GetWalletInfo", + "CreateWallet", + "UnlockWallet", + "SendEth", + "SendToken", + "BuildAndSignUserOperationForPayment", + "SubmitUserOperation", + "GetUserOperationReceipt", + "GetBalance", + "GetTokenBalance", + "CreateNote", + "ExecuteViaTba", + "CheckTbaOwnership", + ], + )) + } + } + } + } + + const FIELDS: &[&str] = &["type", "data"]; + deserializer.deserialize_struct( + "HyperwalletResponseData", + FIELDS, + HyperwalletResponseDataVisitor, + ) + } +} diff --git a/src/hyperwallet_client/types.rs b/src/hyperwallet_client/types.rs index d9754c6..b2f4119 100644 --- a/src/hyperwallet_client/types.rs +++ b/src/hyperwallet_client/types.rs @@ -1,192 +1,99 @@ -//! Shared types for the Hyperwallet protocol, used by both the client and the server. - -use serde::{Deserialize, Serialize}; -use std::collections::HashSet; +//! Hyperwallet types exposed via WIT interface +//! This module re-exports the WIT-generated types and provides conversion utilities + +use crate::hyperware::process::hyperwallet as wit; + +// Re-export WIT types with original names for backwards compatibility +pub use wit::{ + Balance, ChainId, ClientHello, CompleteHandshake, CreateNoteResponse, CreateWalletRequest, + CreateWalletResponse, DeleteWalletRequest, DeleteWalletResponse, ErrorCode, + ExecuteViaTbaResponse, ExportWalletRequest, ExportWalletResponse, GetBalanceRequest, + GetBalanceResponse, GetTokenBalanceRequest, GetTokenBalanceResponse, GetWalletInfoRequest, + GetWalletInfoResponse, ImportWalletRequest, ImportWalletResponse, ListWalletsResponse, + OperationError, PaymasterConfig, RegisterRequest, RenameWalletRequest, SendEthRequest, + SendEthResponse, SendTokenRequest, SendTokenResponse, ServerWelcome, SessionId, Signature, + SpendingLimits, UnlockWalletRequest, UnlockWalletResponse, UpdatableSetting, UserOperationHash, + UserOperationReceiptResponse, Wallet, WalletAddress, WalletSpendingLimits, +}; + +// Re-export enum variants +pub use wit::{ + HandshakeStep, HyperwalletRequest, HyperwalletResponse, HyperwalletResponseData, MessageType, + Operation, OperationCategory, +}; + +// Additional WIT types needed by clients +pub use wit::Eip712Data; + +// Implement Default for PaymasterConfig +impl Default for wit::PaymasterConfig { + fn default() -> Self { + Self { + is_circle_paymaster: true, + paymaster_address: "0x0578cFB241215b77442a541325d6A4E6dFE700Ec".to_string(), + paymaster_verification_gas: "0x7a120".to_string(), + paymaster_post_op_gas: "0x493e0".to_string(), + } + } +} +// Re-export request types +pub use wit::{ + ApproveTokenRequest, BuildAndSignUserOperationForPaymentRequest, + BuildAndSignUserOperationRequest, BuildUserOperationRequest, CallContractRequest, + CheckTbaOwnershipRequest, ConfigurePaymasterRequest, CreateNoteRequest, EstimateGasRequest, + EstimateUserOperationGasRequest, ExecuteViaTbaRequest, GetTransactionHistoryRequest, + GetTransactionReceiptRequest, GetUserOperationReceiptRequest, MintEntryRequest, + ReadNoteRequest, ResolveIdentityRequest, SetWalletLimitsRequest, SetupDelegationRequest, + SetupTbaDelegationRequest, SignMessageRequest, SignTransactionRequest, + SignUserOperationRequest, SubmitUserOperationRequest, UpdateSpendingLimitsRequest, + VerifyDelegationRequest, +}; + +// SetWalletLimits request/response types (wallet-level limits) +// Not available in current WIT; requires WIT update to add request/response and variants. + +// Re-export response types +pub use wit::{ + BuildAndSignUserOperationResponse, CheckTbaOwnershipResponse, SetWalletLimitsResponse, + SubmitUserOperationResponse, +}; + +// Type aliases for compatibility pub type ProcessAddress = crate::Address; -pub type WalletAddress = String; -pub type ChainId = u64; -pub type SessionId = String; -pub type UserOperationHash = String; -pub type Signature = Vec; -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)] -pub enum Operation { - Handshake, - UnlockWallet, - RegisterProcess, // Deprecated - UpdateSpendingLimits, - CreateWallet, - ImportWallet, - DeleteWallet, - RenameWallet, - ExportWallet, - EncryptWallet, - DecryptWallet, - GetWalletInfo, - ListWallets, - SetWalletLimits, - SendEth, - SendToken, - ApproveToken, - CallContract, - SignTransaction, - SignMessage, - ExecuteViaTba, - CheckTbaOwnership, - SetupTbaDelegation, - BuildAndSignUserOperationForPayment, - SubmitUserOperation, - BuildUserOperation, - SignUserOperation, - BuildAndSignUserOperation, - EstimateUserOperationGas, - GetUserOperationReceipt, - ConfigurePaymaster, - ResolveIdentity, - CreateNote, - ReadNote, - SetupDelegation, - VerifyDelegation, - MintEntry, - GetBalance, - GetTokenBalance, - GetTransactionHistory, - EstimateGas, - GetGasPrice, - GetTransactionReceipt, - BatchOperations, - ScheduleOperation, - CancelOperation, -} +// Additional types that need conversion or special handling +pub use wit::HyperwalletMessage; +pub use wit::ProcessPermissions; -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] -pub enum OperationCategory { - System, - ProcessManagement, - WalletManagement, - Ethereum, - TokenBoundAccount, - ERC4337, - Hypermap, - Query, - Advanced, +/// Session information combining handshake completion with metadata +#[derive(Debug, Clone)] +pub struct SessionInfo { + pub server_version: String, + pub session_id: SessionId, + pub registered_permissions: ProcessPermissions, + pub initial_chain_id: ChainId, } -impl Operation { - pub fn all() -> Vec { - vec![ - Operation::Handshake, - Operation::UnlockWallet, - Operation::RegisterProcess, - Operation::UpdateSpendingLimits, - Operation::CreateWallet, - Operation::ImportWallet, - Operation::DeleteWallet, - Operation::RenameWallet, - Operation::ExportWallet, - Operation::EncryptWallet, - Operation::DecryptWallet, - Operation::GetWalletInfo, - Operation::ListWallets, - Operation::SetWalletLimits, - Operation::SendEth, - Operation::SendToken, - Operation::ApproveToken, - Operation::CallContract, - Operation::SignTransaction, - Operation::SignMessage, - Operation::ExecuteViaTba, - Operation::CheckTbaOwnership, - Operation::SetupTbaDelegation, - Operation::BuildAndSignUserOperationForPayment, - Operation::SubmitUserOperation, - Operation::BuildUserOperation, - Operation::SignUserOperation, - Operation::BuildAndSignUserOperation, - Operation::EstimateUserOperationGas, - Operation::GetUserOperationReceipt, - Operation::ConfigurePaymaster, - Operation::ResolveIdentity, - Operation::CreateNote, - Operation::ReadNote, - Operation::SetupDelegation, - Operation::VerifyDelegation, - Operation::MintEntry, - Operation::GetBalance, - Operation::GetTokenBalance, - Operation::GetTransactionHistory, - Operation::EstimateGas, - Operation::GetGasPrice, - Operation::GetTransactionReceipt, - Operation::BatchOperations, - Operation::ScheduleOperation, - Operation::CancelOperation, - ] +impl SessionInfo { + /// Returns true when the server registered this operation for the session + pub fn supports(&self, operation: &Operation) -> bool { + self.registered_permissions + .allowed_operations + .iter() + .any(|op| op == operation) } - pub fn category(&self) -> OperationCategory { - match self { - Operation::Handshake | Operation::UnlockWallet => OperationCategory::System, - - Operation::RegisterProcess | Operation::UpdateSpendingLimits => { - OperationCategory::ProcessManagement - } - - Operation::CreateWallet - | Operation::ImportWallet - | Operation::DeleteWallet - | Operation::RenameWallet - | Operation::ExportWallet - | Operation::EncryptWallet - | Operation::DecryptWallet - | Operation::GetWalletInfo - | Operation::ListWallets - | Operation::SetWalletLimits => OperationCategory::WalletManagement, - - Operation::SendEth - | Operation::SendToken - | Operation::ApproveToken - | Operation::CallContract - | Operation::SignTransaction - | Operation::SignMessage - | Operation::GetBalance - | Operation::GetTokenBalance - | Operation::GetTransactionHistory - | Operation::EstimateGas - | Operation::GetGasPrice - | Operation::GetTransactionReceipt => OperationCategory::Ethereum, - - Operation::ExecuteViaTba - | Operation::CheckTbaOwnership - | Operation::SetupTbaDelegation => OperationCategory::TokenBoundAccount, - - Operation::BuildAndSignUserOperationForPayment - | Operation::SubmitUserOperation - | Operation::BuildUserOperation - | Operation::SignUserOperation - | Operation::BuildAndSignUserOperation - | Operation::EstimateUserOperationGas - | Operation::GetUserOperationReceipt - | Operation::ConfigurePaymaster => OperationCategory::ERC4337, - - Operation::ResolveIdentity - | Operation::CreateNote - | Operation::ReadNote - | Operation::SetupDelegation - | Operation::VerifyDelegation - | Operation::MintEntry => OperationCategory::Hypermap, - - Operation::BatchOperations - | Operation::ScheduleOperation - | Operation::CancelOperation => OperationCategory::Advanced, - } + /// Returns the list of operations the server registered for the session + pub fn allowed_operations(&self) -> &Vec { + &self.registered_permissions.allowed_operations } } +/// Configuration for the handshake process #[derive(Debug)] pub struct HandshakeConfig { - pub(crate) required_operations: HashSet, + pub(crate) required_operations: Vec, pub(crate) spending_limits: Option, pub(crate) client_name: Option, pub(crate) initial_chain_id: ChainId, @@ -195,7 +102,7 @@ pub struct HandshakeConfig { impl Default for HandshakeConfig { fn default() -> Self { Self { - required_operations: HashSet::new(), + required_operations: Vec::new(), spending_limits: None, client_name: None, initial_chain_id: 8453, // Default to Base mainnet @@ -209,16 +116,24 @@ impl HandshakeConfig { } pub fn with_operations(mut self, operations: &[Operation]) -> Self { - self.required_operations.extend(operations.iter().cloned()); + for op in operations { + if !self.required_operations.iter().any(|o| o == op) { + self.required_operations.push(op.clone()); + } + } self } pub fn require_category(mut self, category: OperationCategory) -> Self { - self.required_operations.extend( - Operation::all() - .into_iter() - .filter(|op| op.category() == category), - ); + // Convert operations matching the category + let all_ops = all_operations(); + for op in all_ops { + if operation_category(&op) == category + && !self.required_operations.iter().any(|o| o == &op) + { + self.required_operations.push(op); + } + } self } @@ -238,185 +153,181 @@ impl HandshakeConfig { } } -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct SessionInfo { - pub server_version: String, - pub session_id: SessionId, - pub registered_permissions: ProcessPermissions, - pub initial_chain_id: ChainId, -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -#[serde(tag = "step")] -pub enum HandshakeStep { - ClientHello { - client_version: String, - client_name: String, - }, - ServerWelcome { - server_version: String, - supported_operations: Vec, - supported_chains: Vec, - features: Vec, - }, - Register { - required_operations: Vec, - spending_limits: Option, - }, - Complete { - registered_permissions: ProcessPermissions, - session_id: String, - }, -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct HyperwalletRequest { - pub data: T, - pub session_id: SessionId, -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct HandshakeRequest { - pub step: T, -} - -/// Typed message enum for type-safe communication -#[derive(Debug, Clone, Serialize, Deserialize)] -#[serde(tag = "operation")] -pub enum HyperwalletMessage { - // Session Management (Unauthenticated) - Handshake(HandshakeRequest), - - // Session Management (Authenticated) - UnlockWallet(HyperwalletRequest), - - // Wallet Lifecycle Management - CreateWallet(HyperwalletRequest), - ImportWallet(HyperwalletRequest), - DeleteWallet(HyperwalletRequest), - RenameWallet(HyperwalletRequest), - ExportWallet(HyperwalletRequest), - ListWallets(HyperwalletRequest<()>), // No parameters needed - GetWalletInfo(HyperwalletRequest), - - // Ethereum Operations - SendEth(HyperwalletRequest), - SendToken(HyperwalletRequest), - ApproveToken(HyperwalletRequest), - GetBalance(HyperwalletRequest), - GetTokenBalance(HyperwalletRequest), - CallContract(HyperwalletRequest), - SignTransaction(HyperwalletRequest), - SignMessage(HyperwalletRequest), - GetTransactionHistory(HyperwalletRequest), - EstimateGas(HyperwalletRequest), - GetGasPrice(HyperwalletRequest<()>), // No parameters needed - GetTransactionReceipt(HyperwalletRequest), - - // Token Bound Account Operations - ExecuteViaTba(HyperwalletRequest), - CheckTbaOwnership(HyperwalletRequest), - SetupTbaDelegation(HyperwalletRequest), - - // Account Abstraction (ERC-4337) - BuildAndSignUserOperationForPayment( - HyperwalletRequest, - ), - SubmitUserOperation(HyperwalletRequest), - GetUserOperationReceipt(HyperwalletRequest), - BuildUserOperation(HyperwalletRequest), - SignUserOperation(HyperwalletRequest), - BuildAndSignUserOperation(HyperwalletRequest), - EstimateUserOperationGas(HyperwalletRequest), - ConfigurePaymaster(HyperwalletRequest), - - // Hypermap Operations - ResolveIdentity(HyperwalletRequest), - CreateNote(HyperwalletRequest), - ReadNote(HyperwalletRequest), - SetupDelegation(HyperwalletRequest), - VerifyDelegation(HyperwalletRequest), - MintEntry(HyperwalletRequest), - - // Process Management (Legacy) - UpdateSpendingLimits(HyperwalletRequest), -} - -impl HyperwalletMessage { - /// Get the operation type for this message - used for permission checking and routing - pub fn operation_type(&self) -> Operation { - match self { - // Session Management - HyperwalletMessage::Handshake(_) => Operation::Handshake, - HyperwalletMessage::UnlockWallet(_) => Operation::UnlockWallet, - - // Wallet Lifecycle Management - HyperwalletMessage::CreateWallet(_) => Operation::CreateWallet, - HyperwalletMessage::ImportWallet(_) => Operation::ImportWallet, - HyperwalletMessage::DeleteWallet(_) => Operation::DeleteWallet, - HyperwalletMessage::RenameWallet(_) => Operation::RenameWallet, - HyperwalletMessage::ExportWallet(_) => Operation::ExportWallet, - HyperwalletMessage::ListWallets(_) => Operation::ListWallets, - HyperwalletMessage::GetWalletInfo(_) => Operation::GetWalletInfo, - - // Ethereum Operations - HyperwalletMessage::SendEth(_) => Operation::SendEth, - HyperwalletMessage::SendToken(_) => Operation::SendToken, - HyperwalletMessage::ApproveToken(_) => Operation::ApproveToken, - HyperwalletMessage::GetBalance(_) => Operation::GetBalance, - HyperwalletMessage::GetTokenBalance(_) => Operation::GetTokenBalance, - HyperwalletMessage::CallContract(_) => Operation::CallContract, - HyperwalletMessage::SignTransaction(_) => Operation::SignTransaction, - HyperwalletMessage::SignMessage(_) => Operation::SignMessage, - HyperwalletMessage::GetTransactionHistory(_) => Operation::GetTransactionHistory, - HyperwalletMessage::EstimateGas(_) => Operation::EstimateGas, - HyperwalletMessage::GetGasPrice(_) => Operation::GetGasPrice, - HyperwalletMessage::GetTransactionReceipt(_) => Operation::GetTransactionReceipt, - - // Token Bound Account Operations - HyperwalletMessage::ExecuteViaTba(_) => Operation::ExecuteViaTba, - HyperwalletMessage::CheckTbaOwnership(_) => Operation::CheckTbaOwnership, - HyperwalletMessage::SetupTbaDelegation(_) => Operation::SetupTbaDelegation, - - // Account Abstraction (ERC-4337) - HyperwalletMessage::BuildAndSignUserOperationForPayment(_) => { - Operation::BuildAndSignUserOperationForPayment - } - HyperwalletMessage::SubmitUserOperation(_) => Operation::SubmitUserOperation, - HyperwalletMessage::GetUserOperationReceipt(_) => Operation::GetUserOperationReceipt, - HyperwalletMessage::BuildUserOperation(_) => Operation::BuildUserOperation, - HyperwalletMessage::SignUserOperation(_) => Operation::SignUserOperation, - HyperwalletMessage::BuildAndSignUserOperation(_) => { - Operation::BuildAndSignUserOperation - } - HyperwalletMessage::EstimateUserOperationGas(_) => Operation::EstimateUserOperationGas, - HyperwalletMessage::ConfigurePaymaster(_) => Operation::ConfigurePaymaster, +/// Get all available operations +pub fn all_operations() -> Vec { + vec![ + Operation::Handshake, + Operation::UnlockWallet, + Operation::RegisterProcess, + Operation::UpdateSpendingLimits, + Operation::CreateWallet, + Operation::ImportWallet, + Operation::DeleteWallet, + Operation::RenameWallet, + Operation::ExportWallet, + Operation::EncryptWallet, + Operation::DecryptWallet, + Operation::GetWalletInfo, + Operation::ListWallets, + Operation::SetWalletLimits, + Operation::SendEth, + Operation::SendToken, + Operation::ApproveToken, + Operation::CallContract, + Operation::SignTransaction, + Operation::SignMessage, + Operation::ExecuteViaTba, + Operation::CheckTbaOwnership, + Operation::SetupTbaDelegation, + Operation::BuildAndSignUserOperationForPayment, + Operation::SubmitUserOperation, + Operation::BuildUserOperation, + Operation::SignUserOperation, + Operation::BuildAndSignUserOperation, + Operation::EstimateUserOperationGas, + Operation::GetUserOperationReceipt, + Operation::ConfigurePaymaster, + Operation::ResolveIdentity, + Operation::CreateNote, + Operation::ReadNote, + Operation::SetupDelegation, + Operation::VerifyDelegation, + Operation::MintEntry, + Operation::GetBalance, + Operation::GetTokenBalance, + Operation::GetTransactionHistory, + Operation::EstimateGas, + Operation::GetGasPrice, + Operation::GetTransactionReceipt, + Operation::BatchOperations, + Operation::ScheduleOperation, + Operation::CancelOperation, + ] +} + +/// Get the category for an operation +pub fn operation_category(op: &Operation) -> OperationCategory { + match op { + Operation::Handshake | Operation::UnlockWallet => OperationCategory::System, + + Operation::RegisterProcess | Operation::UpdateSpendingLimits => { + OperationCategory::ProcessManagement + } - // Hypermap Operations - HyperwalletMessage::ResolveIdentity(_) => Operation::ResolveIdentity, - HyperwalletMessage::CreateNote(_) => Operation::CreateNote, - HyperwalletMessage::ReadNote(_) => Operation::ReadNote, - HyperwalletMessage::SetupDelegation(_) => Operation::SetupDelegation, - HyperwalletMessage::VerifyDelegation(_) => Operation::VerifyDelegation, - HyperwalletMessage::MintEntry(_) => Operation::MintEntry, + Operation::CreateWallet + | Operation::ImportWallet + | Operation::DeleteWallet + | Operation::RenameWallet + | Operation::ExportWallet + | Operation::EncryptWallet + | Operation::DecryptWallet + | Operation::GetWalletInfo + | Operation::ListWallets + | Operation::SetWalletLimits => OperationCategory::WalletManagement, + + Operation::SendEth + | Operation::SendToken + | Operation::ApproveToken + | Operation::CallContract + | Operation::SignTransaction + | Operation::SignMessage + | Operation::GetBalance + | Operation::GetTokenBalance + | Operation::GetTransactionHistory + | Operation::EstimateGas + | Operation::GetGasPrice + | Operation::GetTransactionReceipt => OperationCategory::Ethereum, + + Operation::ExecuteViaTba | Operation::CheckTbaOwnership | Operation::SetupTbaDelegation => { + OperationCategory::TokenBoundAccount + } - // Process Management (Legacy) - HyperwalletMessage::UpdateSpendingLimits(_) => Operation::UpdateSpendingLimits, + Operation::BuildAndSignUserOperationForPayment + | Operation::SubmitUserOperation + | Operation::BuildUserOperation + | Operation::SignUserOperation + | Operation::BuildAndSignUserOperation + | Operation::EstimateUserOperationGas + | Operation::GetUserOperationReceipt + | Operation::ConfigurePaymaster => OperationCategory::Erc4337, + + Operation::ResolveIdentity + | Operation::CreateNote + | Operation::ReadNote + | Operation::SetupDelegation + | Operation::VerifyDelegation + | Operation::MintEntry => OperationCategory::Hypermap, + + Operation::BatchOperations | Operation::ScheduleOperation | Operation::CancelOperation => { + OperationCategory::Advanced } } } -/// Unified response type -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct HyperwalletResponse { - pub success: bool, - pub data: Option, - pub error: Option, - pub request_id: Option, +/// Get the operation type for a HyperwalletRequest +pub fn operation_type(request: &HyperwalletRequest) -> Operation { + match request { + // Session Management + HyperwalletRequest::Handshake(_) => Operation::Handshake, + HyperwalletRequest::UnlockWallet(_) => Operation::UnlockWallet, + + // Wallet Lifecycle Management + HyperwalletRequest::CreateWallet(_) => Operation::CreateWallet, + HyperwalletRequest::ImportWallet(_) => Operation::ImportWallet, + HyperwalletRequest::DeleteWallet(_) => Operation::DeleteWallet, + HyperwalletRequest::RenameWallet(_) => Operation::RenameWallet, + HyperwalletRequest::ExportWallet(_) => Operation::ExportWallet, + HyperwalletRequest::ListWallets => Operation::ListWallets, + HyperwalletRequest::GetWalletInfo(_) => Operation::GetWalletInfo, + + // Ethereum Operations + HyperwalletRequest::SendEth(_) => Operation::SendEth, + HyperwalletRequest::SendToken(_) => Operation::SendToken, + HyperwalletRequest::ApproveToken(_) => Operation::ApproveToken, + HyperwalletRequest::GetBalance(_) => Operation::GetBalance, + HyperwalletRequest::GetTokenBalance(_) => Operation::GetTokenBalance, + HyperwalletRequest::CallContract(_) => Operation::CallContract, + HyperwalletRequest::SignTransaction(_) => Operation::SignTransaction, + HyperwalletRequest::SignMessage(_) => Operation::SignMessage, + HyperwalletRequest::GetTransactionHistory(_) => Operation::GetTransactionHistory, + HyperwalletRequest::EstimateGas(_) => Operation::EstimateGas, + HyperwalletRequest::GetGasPrice => Operation::GetGasPrice, + HyperwalletRequest::GetTransactionReceipt(_) => Operation::GetTransactionReceipt, + + // Token Bound Account Operations + HyperwalletRequest::ExecuteViaTba(_) => Operation::ExecuteViaTba, + HyperwalletRequest::CheckTbaOwnership(_) => Operation::CheckTbaOwnership, + HyperwalletRequest::SetupTbaDelegation(_) => Operation::SetupTbaDelegation, + + // Account Abstraction (ERC-4337) + HyperwalletRequest::BuildAndSignUserOperationForPayment(_) => { + Operation::BuildAndSignUserOperationForPayment + } + HyperwalletRequest::SubmitUserOperation(_) => Operation::SubmitUserOperation, + HyperwalletRequest::GetUserOperationReceipt(_) => Operation::GetUserOperationReceipt, + HyperwalletRequest::BuildUserOperation(_) => Operation::BuildUserOperation, + HyperwalletRequest::SignUserOperation(_) => Operation::SignUserOperation, + HyperwalletRequest::BuildAndSignUserOperation(_) => Operation::BuildAndSignUserOperation, + HyperwalletRequest::EstimateUserOperationGas(_) => Operation::EstimateUserOperationGas, + HyperwalletRequest::ConfigurePaymaster(_) => Operation::ConfigurePaymaster, + HyperwalletRequest::SetWalletLimits(_) => Operation::SetWalletLimits, + + // Hypermap Operations + HyperwalletRequest::ResolveIdentity(_) => Operation::ResolveIdentity, + HyperwalletRequest::CreateNote(_) => Operation::CreateNote, + HyperwalletRequest::ReadNote(_) => Operation::ReadNote, + HyperwalletRequest::SetupDelegation(_) => Operation::SetupDelegation, + HyperwalletRequest::VerifyDelegation(_) => Operation::VerifyDelegation, + HyperwalletRequest::MintEntry(_) => Operation::MintEntry, + + // Process Management (Legacy) + HyperwalletRequest::UpdateSpendingLimits(_) => Operation::UpdateSpendingLimits, + } } -impl HyperwalletResponse { - pub fn success(data: T) -> Self { +// Helper functions for HyperwalletResponse +impl wit::HyperwalletResponse { + pub fn success(data: HyperwalletResponseData) -> Self { Self { success: true, data: Some(data), @@ -433,301 +344,10 @@ impl HyperwalletResponse { request_id: None, } } - - pub fn map(self, f: F) -> HyperwalletResponse - where - F: FnOnce(T) -> U, - { - HyperwalletResponse { - success: self.success, - data: self.data.map(f), - error: self.error, - request_id: self.request_id, - } - } -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct BuildAndSignUserOperationForPaymentRequest { - pub eoa_wallet_id: String, - pub tba_address: String, - pub target: String, - pub call_data: String, - pub use_paymaster: bool, - pub paymaster_config: Option, - pub password: Option, -} - -/// Configuration for Circle paymaster (gasless transactions) -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct PaymasterConfig { - pub is_circle_paymaster: bool, - pub paymaster_address: String, - pub paymaster_verification_gas: String, - pub paymaster_post_op_gas: String, -} - -impl Default for PaymasterConfig { - fn default() -> Self { - Self { - is_circle_paymaster: true, - paymaster_address: "0x0578cFB241215b77442a541325d6A4E6dFE700Ec".to_string(), // Base Circle paymaster - //paymaster_address: "0x861a1Be40c595db980341e41A7a5D09C772f7c2b".to_string(), // Base Hyperware paymaster - paymaster_verification_gas: "0x7a120".to_string(), - paymaster_post_op_gas: "0x493e0".to_string(), - } - } -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct CreateWalletRequest { - pub name: String, - pub password: Option, -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct UnlockWalletRequest { - pub session_id: SessionId, - pub wallet_id: String, - pub password: String, -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct ImportWalletRequest { - pub name: String, - pub private_key: String, - pub password: Option, -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct RenameWalletRequest { - pub wallet_id: String, - pub new_name: String, -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct ExportWalletRequest { - pub wallet_id: String, - pub password: Option, -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct SendEthRequest { - pub wallet_id: String, - pub to: String, - pub amount: String, -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct SendTokenRequest { - pub wallet_id: String, - pub token_address: String, - pub to: String, - pub amount: String, -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct ApproveTokenRequest { - pub token_address: String, - pub spender: String, - pub amount: String, -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct GetTokenBalanceRequest { - pub wallet_id: String, - pub token_address: String, -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct ExecuteViaTbaRequest { - pub tba_address: String, - pub target: String, - pub call_data: String, - pub value: Option, -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct CheckTbaOwnershipRequest { - pub tba_address: String, - pub signer_address: String, -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct SubmitUserOperationRequest { - pub signed_user_operation: serde_json::Value, - pub entry_point: String, - pub bundler_url: Option, -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct GetUserOperationReceiptRequest { - pub user_op_hash: String, -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct ResolveIdentityRequest { - pub entry_name: String, -} - -// === NEW PROPERLY TYPED REQUEST STRUCTS === - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct CallContractRequest { - pub to: String, - pub data: String, - pub value: Option, -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct SignTransactionRequest { - pub to: String, - pub value: String, - pub data: Option, - pub gas_limit: Option, - pub gas_price: Option, - pub nonce: Option, -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct SignMessageRequest { - pub message: String, - pub message_type: MessageType, } -#[derive(Debug, Clone, Serialize, Deserialize)] -pub enum MessageType { - PlainText, - Eip191, - Eip712 { - domain: serde_json::Value, - types: serde_json::Value, - }, -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct GetTransactionHistoryRequest { - pub limit: Option, - pub offset: Option, - pub from_block: Option, - pub to_block: Option, -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct EstimateGasRequest { - pub to: String, - pub data: Option, - pub value: Option, -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct GetTransactionReceiptRequest { - pub tx_hash: String, -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct SetupTbaDelegationRequest { - pub tba_address: String, - pub delegate_address: String, - pub permissions: Vec, -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct BuildUserOperationRequest { - pub target: String, - pub call_data: String, - pub value: Option, -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct SignUserOperationRequest { - pub unsigned_user_operation: serde_json::Value, - pub entry_point: String, -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct BuildAndSignUserOperationRequest { - pub target: String, - pub call_data: String, - pub value: Option, - pub entry_point: String, -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct EstimateUserOperationGasRequest { - pub user_operation: serde_json::Value, - pub entry_point: String, -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct ConfigurePaymasterRequest { - pub paymaster_address: String, - pub paymaster_data: Option, - pub verification_gas_limit: String, - pub post_op_gas_limit: String, -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct ReadNoteRequest { - pub note_id: String, -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct SetupDelegationRequest { - pub delegate_address: String, - pub permissions: Vec, - pub expiry: Option, -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct VerifyDelegationRequest { - pub delegate_address: String, - pub signature: String, - pub message: String, -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct MintEntryRequest { - pub entry_name: String, - pub metadata: serde_json::Value, -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct UpdateSpendingLimitsRequest { - pub new_limits: SpendingLimits, -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct CreateNoteRequest { - pub note_data: serde_json::Value, - pub metadata: Option, -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct DeleteWalletRequest { - pub wallet_id: String, -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct GetWalletInfoRequest { - pub wallet_id: String, -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct GetBalanceRequest { - pub wallet_id: String, -} - -// === ESSENTIAL TYPES (NOT LEGACY) === - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct OperationError { - pub code: ErrorCode, - pub message: String, - pub details: Option, -} - -impl OperationError { +// Helper functions for OperationError +impl wit::OperationError { pub fn internal_error(message: &str) -> Self { Self { code: ErrorCode::InternalError, @@ -820,54 +440,19 @@ impl OperationError { } } -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] -pub enum ErrorCode { - PermissionDenied, - WalletNotFound, - InsufficientFunds, - InvalidOperation, - InvalidParams, - SpendingLimitExceeded, - ChainNotAllowed, - BlockchainError, - InternalError, - AuthenticationFailed, - WalletLocked, - OperationNotSupported, - VersionMismatch, -} - -#[derive(Debug, Clone, Serialize, Deserialize, Default)] -pub struct SpendingLimits { - pub per_tx_eth: Option, - pub daily_eth: Option, - pub per_tx_usdc: Option, - pub daily_usdc: Option, - pub daily_reset_at: u64, - pub spent_today_eth: String, - pub spent_today_usdc: String, -} - -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] -pub enum UpdatableSetting { - SpendingLimits, -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct ProcessPermissions { - pub address: ProcessAddress, - pub allowed_operations: HashSet, - pub spending_limits: Option, - pub updatable_settings: Vec, - pub registered_at: u64, +// Add operation_type method to HyperwalletMessage +impl wit::HyperwalletMessage { + pub fn operation_type(&self) -> Operation { + operation_type(&self.request) + } } -impl ProcessPermissions { - /// Create new ProcessPermissions for a process during handshake registration +// Conversion function for ProcessPermissions +impl wit::ProcessPermissions { pub fn new(address: crate::Address, required_operations: Vec) -> Self { Self { address, - allowed_operations: required_operations.into_iter().collect(), + allowed_operations: required_operations, spending_limits: None, updatable_settings: vec![], registered_at: std::time::SystemTime::now() @@ -878,258 +463,9 @@ impl ProcessPermissions { } } -// API Result Structs - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct UnlockWalletResponse { - pub success: bool, - pub wallet_id: String, - pub message: String, -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct CreateWalletResponse { - pub wallet_id: String, - pub address: String, - pub name: String, -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct ImportWalletResponse { - pub wallet_id: String, - pub address: String, - pub name: String, -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct DeleteWalletResponse { - pub success: bool, - pub wallet_id: String, - pub message: String, -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct GetWalletInfoResponse { - pub wallet_id: String, - pub address: String, - pub name: String, - pub chain_id: ChainId, - pub is_locked: bool, -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct GetBalanceResponse { - pub balance: Balance, - pub wallet_id: String, - pub chain_id: ChainId, -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct SendEthResponse { - pub tx_hash: String, - pub from_address: String, - pub to_address: String, - pub amount: String, - pub chain_id: ChainId, -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct SendTokenResponse { - pub tx_hash: String, - pub from_address: String, - pub to_address: String, - pub token_address: String, - pub amount: String, - pub chain_id: ChainId, -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct CreateNoteResponse { - pub note_id: String, - pub content_hash: String, - pub created_at: u64, -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct ExecuteViaTbaResponse { - pub tx_hash: String, - pub tba_address: String, - pub target_address: String, - pub success: bool, -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct CheckTbaOwnershipResponse { - pub tba_address: String, - pub owner_address: String, - pub is_owned: bool, -} - -/// Unified response type that preserves type safety for all hyperwallet operations -/// This replaces serde_json::Value in the message dispatcher -#[derive(Debug, Clone, Serialize, Deserialize)] -#[serde(tag = "operation_type")] -pub enum HyperwalletResponseData { - // Session Management - Handshake(HandshakeStep), - UnlockWallet(UnlockWalletResponse), - - // Wallet Lifecycle - CreateWallet(CreateWalletResponse), - ImportWallet(ImportWalletResponse), - DeleteWallet(DeleteWalletResponse), - ExportWallet(ExportWalletResponse), - - // Wallet Queries - ListWallets(ListWalletsResponse), - GetWalletInfo(GetWalletInfoResponse), - GetBalance(GetBalanceResponse), - GetTokenBalance(GetTokenBalanceResponse), - - // Transactions - SendEth(SendEthResponse), - SendToken(SendTokenResponse), - - // ERC4337 Account Abstraction - BuildAndSignUserOperationForPayment(BuildAndSignUserOperationResponse), - SubmitUserOperation(SubmitUserOperationResponse), - GetUserOperationReceipt(UserOperationReceiptResponse), - - // Hypermap - CreateNote(CreateNoteResponse), - - // Token Bound Accounts - ExecuteViaTba(ExecuteViaTbaResponse), - CheckTbaOwnership(CheckTbaOwnershipResponse), -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct Wallet { - pub address: WalletAddress, - pub name: Option, - pub chain_id: ChainId, - pub encrypted: bool, - pub created_at: Option, - pub last_used: Option, - pub spending_limits: Option, -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct WalletSpendingLimits { - pub max_per_call: Option, - pub max_total: Option, - pub currency: String, - pub total_spent: String, - pub set_at: Option, - pub updated_at: Option, -} - -#[derive(Debug, Serialize, Deserialize)] +// Legacy type that doesn't exist in WIT - kept for compatibility +#[derive(Debug, serde::Serialize, serde::Deserialize)] pub struct TxReceipt { pub hash: String, pub details: serde_json::Value, } - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct Balance { - pub formatted: String, - pub raw: String, -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct BuildAndSignUserOperationResponse { - pub signed_user_operation: serde_json::Value, - pub entry_point: String, - pub ready_to_submit: bool, -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct SubmitUserOperationResponse { - pub user_op_hash: String, -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct ExportWalletResponse { - pub address: String, - pub private_key: String, -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct ListWalletsResponse { - pub process: String, - pub wallets: Vec, - pub total: usize, -} - -// === NEW RESPONSE STRUCTS FOR TYPE SAFETY === - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct GetTokenBalanceResponse { - pub balance: String, - pub formatted: Option, - pub decimals: Option, -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct UserOperationReceiptResponse { - pub receipt: Option, - pub user_op_hash: String, - pub status: String, -} - -#[derive(Debug, Serialize, Deserialize)] -pub struct CallContractResponse { - pub result: String, - pub gas_used: Option, -} - -#[derive(Debug, Serialize, Deserialize)] -pub struct SignTransactionResponse { - pub signed_transaction: String, - pub transaction_hash: String, -} - -#[derive(Debug, Serialize, Deserialize)] -pub struct SignMessageResponse { - pub signature: String, - pub message_hash: Option, -} - -#[derive(Debug, Serialize, Deserialize)] -pub struct EstimateGasResponse { - pub gas_estimate: String, - pub gas_price: Option, -} - -#[derive(Debug, Serialize, Deserialize)] -pub struct GetGasPriceResponse { - pub gas_price: String, - pub fast_gas_price: Option, - pub standard_gas_price: Option, - pub safe_gas_price: Option, -} - -#[derive(Debug, Serialize, Deserialize)] -pub struct TransactionHistoryResponse { - pub transactions: Vec, - pub total: usize, - pub page: Option, -} - -#[derive(Debug, Serialize, Deserialize)] -pub struct TransactionHistoryItem { - pub hash: String, - pub from: String, - pub to: String, - pub value: String, - pub gas_used: Option, - pub timestamp: u64, - pub status: String, -} - -#[derive(Debug, Serialize, Deserialize)] -pub struct ResolveIdentityResponse { - pub address: Option, - pub entry_name: String, - pub found: bool, -} diff --git a/src/lib.rs b/src/lib.rs index be83181..baca934 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -56,6 +56,7 @@ pub mod logging; pub mod net; pub mod sign; /// Low-level Ethereum signing operations and key management. +#[cfg(feature = "hyperwallet")] pub mod signer; /// Interact with the sqlite module /// @@ -72,11 +73,13 @@ pub mod timer; /// `vfs:distro:sys` to use this module. pub mod vfs; /// Ethereum wallet management with transaction preparation and submission. +#[cfg(feature = "hyperwallet")] pub mod wallet; /// A set of types and macros for writing "script" processes. pub mod scripting; +#[cfg(feature = "hyperwallet")] pub mod hyperwallet_client; mod types; diff --git a/src/signer.rs b/src/signer.rs index 3af90f4..21895d5 100644 --- a/src/signer.rs +++ b/src/signer.rs @@ -143,6 +143,7 @@ impl<'de> Deserialize<'de> for LocalSigner { { #[derive(Deserialize)] struct LocalSignerData { + #[allow(dead_code)] address: EthAddress, chain_id: u64, private_key_hex: String, diff --git a/src/wallet.rs b/src/wallet.rs index ddeb181..0f7e6ad 100644 --- a/src/wallet.rs +++ b/src/wallet.rs @@ -2345,7 +2345,7 @@ impl UserOperationBuilder { } /// Calculate the UserOp hash according to ERC-4337 spec - fn get_user_op_hash( + fn _get_user_op_hash( &self, user_op: &UserOperation, entry_point: EthAddress, @@ -2354,7 +2354,7 @@ impl UserOperationBuilder { use sha3::{Digest, Keccak256}; // Pack the UserOp for hashing (without signature) - let packed = self.pack_user_op_for_hash(user_op); + let packed = self._pack_user_op_for_hash(user_op); let user_op_hash = Keccak256::digest(&packed); // Create the final hash with entry point and chain ID @@ -2389,7 +2389,7 @@ impl UserOperationBuilder { } /// Pack UserOp fields for hashing (ERC-4337 specification) - fn pack_user_op_for_hash(&self, user_op: &UserOperation) -> Vec { + fn _pack_user_op_for_hash(&self, user_op: &UserOperation) -> Vec { use sha3::{Digest, Keccak256}; let mut packed = Vec::new(); @@ -2473,11 +2473,11 @@ impl UserOperationBuilder { pub fn paymaster_with_permit( &mut self, paymaster: EthAddress, - token_address: EthAddress, - max_cost: U256, - tba_address: EthAddress, - signer: &S, - provider: &Provider, + _token_address: EthAddress, + _max_cost: U256, + _tba_address: EthAddress, + _signer: &S, + _provider: &Provider, ) -> Result<(), WalletError> { // Use simple Circle format - no permit signature needed // The TBA has already approved the paymaster to spend USDC @@ -2741,8 +2741,8 @@ pub fn encode_usdc_paymaster_data_with_signer( /// This version uses a dummy signature and will fail with AA33 pub fn encode_usdc_paymaster_data( paymaster: EthAddress, - token_address: EthAddress, - max_cost: U256, + _token_address: EthAddress, + _max_cost: U256, ) -> Vec { // Use the new Circle format with default gas limits encode_circle_paymaster_data(paymaster, 500_000, 300_000) @@ -2826,13 +2826,13 @@ pub fn create_erc20_permit_calldata( /// Creates a multicall calldata that combines permit + another operation /// This is useful for TBAs to approve and use tokens in a single transaction pub fn create_multicall_permit_and_execute( - token_address: EthAddress, + _token_address: EthAddress, permit_spender: EthAddress, permit_amount: U256, permit_deadline: U256, - execute_target: EthAddress, - execute_calldata: Vec, - execute_value: U256, + _execute_target: EthAddress, + _execute_calldata: Vec, + _execute_value: U256, ) -> Vec { // Create permit calldata let permit_calldata = create_erc20_permit_calldata(