From a232036793a534e92cf741c765b1eba5040c2cac Mon Sep 17 00:00:00 2001 From: Hallmane Date: Wed, 26 Mar 2025 22:23:26 +0100 Subject: [PATCH 01/96] cleaned up commits --- Cargo.lock | 196 +++++++++++++------- Cargo.toml | 8 +- src/eth_utils.rs | 298 ++++++++++++++++++++++++++++++ src/hypermap.rs | 2 +- src/lib.rs | 14 +- src/signer.rs | 432 ++++++++++++++++++++++++++++++++++++++++++++ src/tba_explorer.rs | 391 +++++++++++++++++++++++++++++++++++++++ src/wallet.rs | 265 +++++++++++++++++++++++++++ 8 files changed, 1530 insertions(+), 76 deletions(-) create mode 100644 src/eth_utils.rs create mode 100644 src/signer.rs create mode 100644 src/tba_explorer.rs create mode 100644 src/wallet.rs diff --git a/Cargo.lock b/Cargo.lock index be6c367..c5b220d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -56,10 +56,13 @@ dependencies = [ "alloy-eips", "alloy-genesis", "alloy-json-rpc", + "alloy-network", "alloy-provider", "alloy-rpc-client", "alloy-rpc-types", "alloy-serde", + "alloy-signer", + "alloy-signer-local", "alloy-transport", "alloy-transport-http", ] @@ -88,7 +91,7 @@ dependencies = [ "alloy-trie", "auto_impl", "c-kzg", - "derive_more", + "derive_more 1.0.0", "serde", ] @@ -133,7 +136,7 @@ dependencies = [ "itoa", "serde", "serde_json", - "winnow", + "winnow 0.6.20", ] [[package]] @@ -155,7 +158,7 @@ checksum = "4c986539255fb839d1533c128e190e557e52ff652c9ef62939e233a81dd93f7e" dependencies = [ "alloy-primitives", "alloy-rlp", - "derive_more", + "derive_more 1.0.0", "serde", ] @@ -171,7 +174,7 @@ dependencies = [ "alloy-rlp", "alloy-serde", "c-kzg", - "derive_more", + "derive_more 1.0.0", "once_cell", "serde", "sha2", @@ -191,9 +194,9 @@ dependencies = [ [[package]] name = "alloy-json-abi" -version = "0.8.15" +version = "0.8.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c357da577dfb56998d01f574d81ad7a1958d248740a7981b205d69d65a7da404" +checksum = "fe6beff64ad0aa6ad1019a3db26fef565aefeb011736150ab73ed3366c3cfd1b" dependencies = [ "alloy-primitives", "alloy-sol-type-parser", @@ -255,18 +258,17 @@ dependencies = [ [[package]] name = "alloy-primitives" -version = "0.8.15" +version = "0.8.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6259a506ab13e1d658796c31e6e39d2e2ee89243bcc505ddc613b35732e0a430" +checksum = "8c77490fe91a0ce933a1f219029521f20fc28c2c0ca95d53fa4da9c00b8d9d4e" dependencies = [ "alloy-rlp", "bytes", "cfg-if", "const-hex", - "derive_more", + "derive_more 2.0.1", "foldhash", "hashbrown 0.15.2", - "hex-literal", "indexmap", "itoa", "k256", @@ -337,7 +339,7 @@ checksum = "5a833d97bf8a5f0f878daf2c8451fff7de7f9de38baa5a45d936ec718d81255a" dependencies = [ "proc-macro2", "quote", - "syn 2.0.91", + "syn 2.0.100", ] [[package]] @@ -400,7 +402,7 @@ dependencies = [ "alloy-rlp", "alloy-serde", "alloy-sol-types", - "derive_more", + "derive_more 1.0.0", "itertools 0.13.0", "serde", "serde_json", @@ -431,25 +433,41 @@ dependencies = [ "thiserror 2.0.9", ] +[[package]] +name = "alloy-signer-local" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47fababf5a745133490cde927d48e50267f97d3d1209b9fc9f1d1d666964d172" +dependencies = [ + "alloy-consensus", + "alloy-network", + "alloy-primitives", + "alloy-signer", + "async-trait", + "k256", + "rand", + "thiserror 2.0.9", +] + [[package]] name = "alloy-sol-macro" -version = "0.8.15" +version = "0.8.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9d64f851d95619233f74b310f12bcf16e0cbc27ee3762b6115c14a84809280a" +checksum = "e10ae8e9a91d328ae954c22542415303919aabe976fe7a92eb06db1b68fd59f2" dependencies = [ "alloy-sol-macro-expander", "alloy-sol-macro-input", "proc-macro-error2", "proc-macro2", "quote", - "syn 2.0.91", + "syn 2.0.100", ] [[package]] name = "alloy-sol-macro-expander" -version = "0.8.15" +version = "0.8.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bf7ed1574b699f48bf17caab4e6e54c6d12bc3c006ab33d58b1e227c1c3559f" +checksum = "83ad5da86c127751bc607c174d6c9fe9b85ef0889a9ca0c641735d77d4f98f26" dependencies = [ "alloy-sol-macro-input", "const-hex", @@ -458,41 +476,42 @@ dependencies = [ "proc-macro-error2", "proc-macro2", "quote", - "syn 2.0.91", + "syn 2.0.100", "syn-solidity", "tiny-keccak", ] [[package]] name = "alloy-sol-macro-input" -version = "0.8.15" +version = "0.8.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c02997ccef5f34f9c099277d4145f183b422938ed5322dc57a089fe9b9ad9ee" +checksum = "ba3d30f0d3f9ba3b7686f3ff1de9ee312647aac705604417a2f40c604f409a9e" dependencies = [ "const-hex", "dunce", "heck", + "macro-string", "proc-macro2", "quote", - "syn 2.0.91", + "syn 2.0.100", "syn-solidity", ] [[package]] name = "alloy-sol-type-parser" -version = "0.8.15" +version = "0.8.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce13ff37285b0870d0a0746992a4ae48efaf34b766ae4c2640fa15e5305f8e73" +checksum = "6d162f8524adfdfb0e4bd0505c734c985f3e2474eb022af32eef0d52a4f3935c" dependencies = [ "serde", - "winnow", + "winnow 0.7.4", ] [[package]] name = "alloy-sol-types" -version = "0.8.15" +version = "0.8.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1174cafd6c6d810711b4e00383037bdb458efc4fe3dbafafa16567e0320c54d8" +checksum = "d43d5e60466a440230c07761aa67671d4719d46f43be8ea6e7ed334d8db4a9ab" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -545,7 +564,7 @@ dependencies = [ "alloy-primitives", "alloy-rlp", "arrayvec", - "derive_more", + "derive_more 1.0.0", "nybbles", "serde", "smallvec", @@ -710,7 +729,7 @@ checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.91", + "syn 2.0.100", ] [[package]] @@ -721,7 +740,7 @@ checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd" dependencies = [ "proc-macro2", "quote", - "syn 2.0.91", + "syn 2.0.100", ] [[package]] @@ -732,7 +751,7 @@ checksum = "3c87f3f15e7794432337fc718554eaa4dc8f04c9677a950ffe366f20a162ae42" dependencies = [ "proc-macro2", "quote", - "syn 2.0.91", + "syn 2.0.100", ] [[package]] @@ -1040,7 +1059,16 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4a9b99b9cbbe49445b21764dc0625032a89b145a2642e67603e1c936f5458d05" dependencies = [ - "derive_more-impl", + "derive_more-impl 1.0.0", +] + +[[package]] +name = "derive_more" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "093242cf7570c207c83073cf82f79706fe7b8317e98620a47d5be7c3d8497678" +dependencies = [ + "derive_more-impl 2.0.1", ] [[package]] @@ -1051,7 +1079,19 @@ checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22" dependencies = [ "proc-macro2", "quote", - "syn 2.0.91", + "syn 2.0.100", + "unicode-xid", +] + +[[package]] +name = "derive_more-impl" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bda628edc44c4bb645fbe0f758797143e4e07926f7ebf4e9bdfbd3d2ce621df3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.100", "unicode-xid", ] @@ -1084,7 +1124,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.91", + "syn 2.0.100", ] [[package]] @@ -1306,7 +1346,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.91", + "syn 2.0.100", ] [[package]] @@ -1438,12 +1478,6 @@ dependencies = [ "serde", ] -[[package]] -name = "hex-literal" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" - [[package]] name = "hmac" version = "0.12.1" @@ -1549,7 +1583,7 @@ dependencies = [ [[package]] name = "hyperware_process_lib" -version = "1.0.5" +version = "1.0.4" dependencies = [ "alloy", "alloy-primitives", @@ -1558,6 +1592,7 @@ dependencies = [ "anyhow", "bincode", "color-eyre", + "hex", "http", "mime_guess", "rand", @@ -1565,6 +1600,7 @@ dependencies = [ "rmp-serde", "serde", "serde_json", + "sha3", "thiserror 1.0.69", "tracing", "tracing-error", @@ -1688,7 +1724,7 @@ checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.91", + "syn 2.0.100", ] [[package]] @@ -1735,7 +1771,7 @@ checksum = "a0eb5a3343abf848c0984fe4604b2b105da9539376e24fc0a3b0007411ae4fd9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.91", + "syn 2.0.100", ] [[package]] @@ -1888,6 +1924,17 @@ dependencies = [ "hashbrown 0.15.2", ] +[[package]] +name = "macro-string" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b27834086c65ec3f9387b096d66e99f221cf081c2b738042aa252bcd41204e3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.100", +] + [[package]] name = "matchers" version = "0.1.0" @@ -2022,7 +2069,7 @@ checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56" dependencies = [ "proc-macro2", "quote", - "syn 2.0.91", + "syn 2.0.100", ] [[package]] @@ -2076,7 +2123,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.91", + "syn 2.0.100", ] [[package]] @@ -2198,7 +2245,7 @@ checksum = "3c0f5fad0874fc7abcd4d750e76917eaebbecaa2c20bde22e1dbeeba8beb758c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.91", + "syn 2.0.100", ] [[package]] @@ -2245,7 +2292,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "64d1ec885c64d0457d564db4ec299b2dae3f9c02808b8ad9c3a089c591b18033" dependencies = [ "proc-macro2", - "syn 2.0.91", + "syn 2.0.100", ] [[package]] @@ -2287,7 +2334,7 @@ dependencies = [ "proc-macro-error-attr2", "proc-macro2", "quote", - "syn 2.0.91", + "syn 2.0.100", ] [[package]] @@ -2738,7 +2785,7 @@ checksum = "46f859dbbf73865c6627ed570e78961cd3ac92407a2d117204c49232485da55e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.91", + "syn 2.0.100", ] [[package]] @@ -2899,7 +2946,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.91", + "syn 2.0.100", ] [[package]] @@ -2921,9 +2968,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.91" +version = "2.0.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d53cbcb5a243bd33b7858b1d7f4aca2153490815872d86d955d6ea29f743c035" +checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0" dependencies = [ "proc-macro2", "quote", @@ -2932,14 +2979,14 @@ dependencies = [ [[package]] name = "syn-solidity" -version = "0.8.15" +version = "0.8.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "219389c1ebe89f8333df8bdfb871f6631c552ff399c23cac02480b6088aad8f0" +checksum = "4560533fbd6914b94a8fb5cc803ed6801c3455668db3b810702c57612bac9412" dependencies = [ "paste", "proc-macro2", "quote", - "syn 2.0.91", + "syn 2.0.100", ] [[package]] @@ -2959,7 +3006,7 @@ checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" dependencies = [ "proc-macro2", "quote", - "syn 2.0.91", + "syn 2.0.100", ] [[package]] @@ -3007,7 +3054,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.91", + "syn 2.0.100", ] [[package]] @@ -3018,7 +3065,7 @@ checksum = "7b50fa271071aae2e6ee85f842e2e28ba8cd2c5fb67f11fcb1fd70b276f9e7d4" dependencies = [ "proc-macro2", "quote", - "syn 2.0.91", + "syn 2.0.100", ] [[package]] @@ -3083,7 +3130,7 @@ checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" dependencies = [ "proc-macro2", "quote", - "syn 2.0.91", + "syn 2.0.100", ] [[package]] @@ -3135,7 +3182,7 @@ checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" dependencies = [ "indexmap", "toml_datetime", - "winnow", + "winnow 0.6.20", ] [[package]] @@ -3183,7 +3230,7 @@ checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.91", + "syn 2.0.100", ] [[package]] @@ -3388,7 +3435,7 @@ dependencies = [ "log", "proc-macro2", "quote", - "syn 2.0.91", + "syn 2.0.100", "wasm-bindgen-shared", ] @@ -3423,7 +3470,7 @@ checksum = "30d7a95b763d3c45903ed6c81f156801839e5ee968bb07e534c44df0fcd330c2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.91", + "syn 2.0.100", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -3640,6 +3687,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "winnow" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e97b544156e9bebe1a0ffbc03484fc1ffe3100cbce3ffb17eac35f7cdd7ab36" +dependencies = [ + "memchr", +] + [[package]] name = "wit-bindgen" version = "0.36.0" @@ -3680,7 +3736,7 @@ dependencies = [ "heck", "indexmap", "prettyplease", - "syn 2.0.91", + "syn 2.0.100", "wasm-metadata", "wit-bindgen-core", "wit-component", @@ -3696,7 +3752,7 @@ dependencies = [ "prettyplease", "proc-macro2", "quote", - "syn 2.0.91", + "syn 2.0.100", "wit-bindgen-core", "wit-bindgen-rust", ] @@ -3779,7 +3835,7 @@ checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" dependencies = [ "proc-macro2", "quote", - "syn 2.0.91", + "syn 2.0.100", "synstructure", ] @@ -3801,7 +3857,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.91", + "syn 2.0.100", ] [[package]] @@ -3821,7 +3877,7 @@ checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808" dependencies = [ "proc-macro2", "quote", - "syn 2.0.91", + "syn 2.0.100", "synstructure", ] @@ -3842,7 +3898,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.91", + "syn 2.0.100", ] [[package]] @@ -3864,5 +3920,5 @@ checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.91", + "syn 2.0.100", ] diff --git a/Cargo.toml b/Cargo.toml index 4fd1c75..0c364c7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "hyperware_process_lib" authors = ["Sybil Technologies AG"] -version = "1.0.5" +version = "1.0.4" edition = "2021" description = "A library for writing Hyperware processes in Rust." homepage = "https://hyperware.ai" @@ -19,7 +19,13 @@ alloy = { version = "0.8.1", features = [ "json-rpc", "rpc-client", "rpc-types", + "signers", + "signer-local", + "consensus", + "network", ] } +hex = "0.4.3" +sha3 = "0.10.8" anyhow = "1.0" bincode = "1.3.3" color-eyre = { version = "0.6", features = ["capture-spantrace"], optional = true } diff --git a/src/eth_utils.rs b/src/eth_utils.rs new file mode 100644 index 0000000..4a9e08c --- /dev/null +++ b/src/eth_utils.rs @@ -0,0 +1,298 @@ +////! Higher-level Ethereum utilities for common operations in Hyperware. +////! +////! This module provides utility functions for common Ethereum operations in the Hyperware +////! ecosystem, particularly focusing on integrating with Hypermap for name resolution. +////! +////! The main goals of this module are: +////! 1. Provide simple, developer-friendly functions for common operations +////! 2. Abstract away the complexity of blockchain interactions +////! 3. Integrate with Hypermap for human-readable addressing +////! +////! ## Usage Examples +////! +////! ```rust +////! use hyperware_process_lib::eth_utils; +////! +////! // Send ETH to a Hypermap name +////! let tx_hash = eth_utils::send_eth("alice.hypr", 1.3)?; +////! +////! // Check if a wallet owns an NFT +////! let has_token = eth_utils::has_nft(contract_address, token_id, wallet_address)?; +////! +////! // Get a token balance +////! let balance = eth_utils::get_token_balance(token_address, wallet_address)?; +////! +////! // Send tokens to a Hypermap name +////! let tx_hash = eth_utils::send_token_to_name(token_address, "bob.hypr", amount)?; +////! ``` +// +//use crate::eth::{ +// Address, +// EthError, +// TxHash, +// U256 +//}; +//use crate::hypermap::{Hypermap, HYPERMAP_ADDRESS}; +//use crate::wallet::{Wallet, WalletError}; +//use std::str::FromStr; +//use thiserror::Error; +//use sha3::Digest; +// +///// Default chain ID to use for operations if not specified. +///// Currently set to Base (Coinbase L2). +//pub const DEFAULT_CHAIN_ID: u64 = crate::hypermap::HYPERMAP_CHAIN_ID; +// +///// Default timeout (in milliseconds) for Ethereum RPC operations. +//pub const DEFAULT_TIMEOUT_MS: u64 = 60_000; // 60 seconds +// +///// Errors that can occur in Ethereum utility operations +//#[derive(Debug, Error)] +//pub enum EthUtilsError { +// #[error("Ethereum RPC error: {0}")] +// Eth(#[from] EthError), +// +// #[error("Wallet error: {0}")] +// Wallet(#[from] WalletError), +// +// #[error("Name resolution error: {0}")] +// NameResolution(String), +// +// #[error("Transaction error: {0}")] +// Transaction(String), +//} +// +///// Send Ether to an address +///// +///// This function creates, signs, and sends a transaction to send ETH to an address. +///// +///// # Parameters +///// - `provider`: The Ethereum provider to use +///// - `wallet`: The wallet to send from +///// - `to`: The recipient address +///// - `amount_wei`: The amount to send in wei +///// - `gas_limit`: Optional gas limit (defaults to 21000) +///// - `gas_price`: Optional gas price (defaults to auto-estimation) +///// +///// # Returns +///// A `Result` representing the transaction hash if successful +///// +///// # Example +///// ```rust +///// use hyperware_process_lib::{eth_utils, wallet, eth}; +///// use alloy_primitives::{Address, U256}; +///// use std::str::FromStr; +///// +///// // Create wallet and provider +///// let wallet = wallet::Wallet::from_private_key( +///// "0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef", +///// 8453 // Base chain ID +///// )?; +///// let provider = eth::Provider::new(8453, 60000); +///// let to = Address::from_str("0x7E5F4552091A69125d5DfCb7b8C2659029395Bdf")?; +///// let amount = U256::from(1000000000000000000u64); // 1 ETH +///// +///// // Send ETH +///// let tx_hash = eth_utils::send_eth(&provider, &wallet, to, amount, None, None)?; +///// println!("Transaction hash: {}", tx_hash); +///// ``` +//pub fn send_eth( +// provider: &crate::eth::Provider, +// wallet: &Wallet, +// to: Address, +// amount_wei: U256, +// gas_limit: Option, +// gas_price: Option +//) -> Result { +// // Create RLP-encoded transaction +// let nonce = provider.get_transaction_count(wallet.address(), None)?; +// let nonce_u64 = u64::try_from(nonce).unwrap_or(0); +// +// // Get gas price if not provided +// let gas_price_value = if let Some(price) = gas_price { +// price +// } else { +// let current_gas_price = provider.get_gas_price()?; +// u128::try_from(current_gas_price).unwrap_or(20000000000) +// }; +// +// // Get gas limit +// let gas_limit_value = gas_limit.unwrap_or(21000); +// +// // Create and sign a transaction manually +// // First, construct the RLP-encoded transaction +// let mut rlp_data = Vec::new(); +// rlp_data.extend_from_slice(to.as_slice()); +// rlp_data.extend_from_slice(&amount_wei.to_be_bytes::<32>()); +// rlp_data.extend_from_slice(&nonce_u64.to_be_bytes()); +// rlp_data.extend_from_slice(&gas_limit_value.to_be_bytes()); +// rlp_data.extend_from_slice(&gas_price_value.to_be_bytes()); +// +// // Hash the transaction data with keccak256 +// let mut hasher = sha3::Keccak256::new(); +// hasher.update(&rlp_data); +// let tx_hash = hasher.finalize(); +// +// // Sign the transaction hash +// let signed_tx = wallet.sign_transaction_hash(&tx_hash)?; +// +// // Send raw transaction +// let tx_hash = provider.send_raw_transaction(signed_tx)?; +// +// Ok(tx_hash) +//} +// +///// Sends Ether to the owner of the specified Hypermap name. +///// +///// This function first resolves the name to its owner address using Hypermap, +///// then sends the specified amount of Ether to that address. +///// +///// # Parameters +///// - `provider`: The Ethereum provider to use +///// - `wallet`: The wallet to send from +///// - `name`: The Hypermap name (e.g., "alice.hypr") +///// - `amount_eth`: The amount of Ether to send (as a f64) +///// +///// # Returns +///// A `Result` representing the transaction hash if successful +///// +///// # Example +///// ```rust +///// use hyperware_process_lib::{eth_utils, wallet, eth}; +///// +///// // Create wallet and provider +///// let wallet = wallet::Wallet::from_private_key( +///// "0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef", +///// 8453 // Base chain ID +///// )?; +///// let provider = eth::Provider::new(8453, 60000); +///// +///// // Send 1.3 ETH to alice.hypr +///// let tx_hash = eth_utils::send_eth_to_name(&provider, &wallet, "alice.hypr", 1.3)?; +///// println!("Transaction hash: {}", tx_hash); +///// ``` +//pub fn send_eth_to_name( +// provider: &crate::eth::Provider, +// wallet: &Wallet, +// name: &str, +// amount_eth: f64 +//) -> Result { +// // Get Hypermap instance using our provider +// let hypermap = Hypermap::new(provider.clone(), +// Address::from_str(HYPERMAP_ADDRESS).unwrap()); +// +// // Format the name if needed (add .hypr if missing) +// let formatted_name = format_hypermap_name(name); +// +// // Resolve name to owner address +// let (_, owner, _) = hypermap.get(&formatted_name) +// .map_err(|e| EthUtilsError::NameResolution(format!("Failed to resolve name '{}': {}", formatted_name, e)))?; +// +// // Convert amount to wei (1 ETH = 10^18 wei) +// let amount_wei = (amount_eth * 1e18) as u128; +// let amount_in_wei = U256::from(amount_wei); +// +// // Send ETH to the resolved address +// send_eth(provider, wallet, owner, amount_in_wei, None, None) +//} +// +///// Format a name for Hypermap resolution +///// +///// If the name already contains a dot (.), it's returned as is. +///// Otherwise, ".hypr" is appended to the name. +///// +///// # Parameters +///// - `name`: The name to format +///// +///// # Returns +///// A formatted name suitable for Hypermap resolution +//fn format_hypermap_name(name: &str) -> String { +// // If name already has a domain extension, return as is +// if name.contains('.') { +// return name.to_string(); +// } +// +// // Otherwise, add the default .hypr extension +// format!("{}.hypr", name) +//} +// +///// Resolve a Hypermap name to its owner's Ethereum address +///// +///// # Parameters +///// - `name`: The Hypermap name to resolve +///// - `chain_id`: Optional chain ID to use (defaults to Base chain) +///// - `timeout_ms`: Optional timeout in milliseconds (defaults to 60 seconds) +///// +///// # Returns +///// A `Result` representing the owner's Ethereum address +///// +///// # Example +///// ```rust +///// use hyperware_process_lib::eth_utils; +///// +///// let owner = eth_utils::resolve_name("alice.hypr", None, None)?; +///// println!("Owner address: {}", owner); +///// ``` +//pub fn resolve_name( +// name: &str, +// chain_id: Option, +// timeout_ms: Option +//) -> Result { +// // Use provided chain ID or default +// let chain_id = chain_id.unwrap_or(DEFAULT_CHAIN_ID); +// let timeout = timeout_ms.unwrap_or(DEFAULT_TIMEOUT_MS); +// +// // Create provider +// let provider = crate::eth::Provider::new(chain_id, timeout); +// +// // Get Hypermap instance using our provider +// let hypermap = Hypermap::new(provider, +// Address::from_str(HYPERMAP_ADDRESS).unwrap()); +// +// // Format the name if needed (add .hypr if missing) +// let formatted_name = format_hypermap_name(name); +// +// // Resolve name to owner address +// let (_, owner, _) = hypermap.get(&formatted_name)?; +// +// Ok(owner) +//} +// +//#[cfg(test)] +//mod tests { +// use super::*; +// use alloy_primitives::{Address, U256}; +// use std::str::FromStr; +// +// #[test] +// fn test_format_hypermap_name() { +// // Test with name that already has dot +// let name_with_dot = "test.hypr"; +// assert_eq!(format_hypermap_name(name_with_dot), name_with_dot); +// +// // Test with name that doesn't have dot +// let name_without_dot = "test"; +// assert_eq!(format_hypermap_name(name_without_dot), "test.hypr"); +// } +// +// // Note: These tests would need real providers and wallets to run +// // We'll implement placeholders that describe what should be tested +// +// #[test] +// #[ignore] // Ignore this test since it requires network connectivity +// fn test_resolve_name() { +// // This would test name resolution with real provider and chain +// // let name = "test.hypr"; +// // let result = resolve_name(name, Some(DEFAULT_CHAIN_ID), Some(DEFAULT_TIMEOUT_MS)); +// // assert!(result.is_ok()); +// } +// +// #[test] +// #[ignore] // Ignore this test since it requires network connectivity +// fn test_send_eth_to_name() { +// // This would test ETH sending with real provider and wallet +// // let wallet = Wallet::new_random(DEFAULT_CHAIN_ID).unwrap(); +// // let provider = crate::eth::Provider::new(DEFAULT_CHAIN_ID, DEFAULT_TIMEOUT_MS); +// // let result = send_eth_to_name(&provider, &wallet, "test.hypr", 0.001); +// // assert!(result.is_ok()); +// } +//} \ No newline at end of file diff --git a/src/hypermap.rs b/src/hypermap.rs index 7ad150a..3d87b71 100644 --- a/src/hypermap.rs +++ b/src/hypermap.rs @@ -16,7 +16,7 @@ pub const HYPERMAP_ADDRESS: &'static str = "0x000000000044C6B8Cb4d8f0F889a3E4766 /// base chain id pub const HYPERMAP_CHAIN_ID: u64 = 8453; /// first block (minus one) of hypermap deployment on base -pub const HYPERMAP_FIRST_BLOCK: u64 = 27_270_411; +pub const HYPERMAP_FIRST_BLOCK: u64 = 25_346_377; /// the root hash of hypermap, empty bytes32 pub const HYPERMAP_ROOT_HASH: &'static str = "0x0000000000000000000000000000000000000000000000000000000000000000"; diff --git a/src/lib.rs b/src/lib.rs index 6a30e1c..dd1f1d1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -24,8 +24,12 @@ wit_bindgen::generate!({ /// Interact with the eth provider module. pub mod eth; -/// Interact with the system homepage. -/// +/// High-level Ethereum utilities for common operations. +pub mod eth_utils; +/// Ethereum wallet management with transaction preparation and submission. +pub mod wallet; +/// Low-level Ethereum signing operations and key management. +pub mod signer; /// Your process must have the [`Capability`] to message /// `homepage:homepage:sys` to use this module. pub mod homepage; @@ -35,12 +39,14 @@ pub mod homepage; /// Your process must have the [`Capability`] to message and receive messages from /// `http-server:distro:sys` and/or `http-client:distro:sys` to use this module. pub mod http; -/// Interact with hypermap, the onchain namespace -pub mod hypermap; /// The types that the kernel itself uses -- warning -- these will /// be incompatible with WIT types in some cases, leading to annoying errors. /// Use only to interact with the kernel or runtime in certain ways. pub mod kernel_types; +/// Interact with hypermap, the onchain namespace +pub mod hypermap; +/// Tools for exploring and working with Token-Bound Accounts (TBAs) in Hypermap +//pub mod tba_explorer; /// Interact with the key_value module /// /// Your process must have the [`Capability`] to message and receive messages from diff --git a/src/signer.rs b/src/signer.rs new file mode 100644 index 0000000..45fffcb --- /dev/null +++ b/src/signer.rs @@ -0,0 +1,432 @@ +//! Ethereum signer functionality for Hyperware. +//! +//! This module provides low-level cryptographic signing operations for Ethereum, +//! including private key management, message signing, and transaction signing. +//! It separates the cryptographic concerns from the higher-level wallet functionality. + +use crate::eth::EthError; +use thiserror::Error; +use hex; +use sha3::{Sha3_256, Digest}; +use rand::{thread_rng, RngCore}; +use serde::{Serialize, Deserialize}; + +use alloy_primitives::{ + Address as EthAddress, + U256, + B256, +}; +use alloy::{ + signers::{ + local::PrivateKeySigner, + SignerSync, + }, + consensus::{ + SignableTransaction, + TxEip1559, + TxEnvelope + }, + network::eip2718::Encodable2718, + primitives::TxKind, + network::TxSignerSync, +}; +use std::str::FromStr; + +// For encryption/decryption +const SALT_SIZE: usize = 16; +const NONCE_SIZE: usize = 12; +const KEY_SIZE: usize = 32; +const TAG_SIZE: usize = 16; + +/// Transaction data structure used for signing transactions +#[derive(Debug, Clone)] +pub struct TransactionData { + /// The recipient address + pub to: EthAddress, + /// The amount to send in wei + pub value: U256, + /// Optional transaction data (for contract interactions) + pub data: Option>, + /// The transaction nonce + pub nonce: u64, + /// The maximum gas for the transaction + pub gas_limit: u64, + /// The gas price in wei + pub gas_price: u128, + /// Optional max priority fee (for EIP-1559 transactions) + pub max_priority_fee: Option, + /// The chain ID for the transaction + pub chain_id: u64, +} + +/// Errors that can occur during signing operations +#[derive(Debug, Error)] +pub enum SignerError { + #[error("failed to generate random bytes: {0}")] + RandomGenerationError(String), + + #[error("invalid private key format: {0}")] + InvalidPrivateKey(String), + + #[error("chain ID mismatch: expected {expected}, got {actual}")] + ChainIdMismatch { expected: u64, actual: u64 }, + + #[error("failed to sign transaction or message: {0}")] + SigningError(String), + + #[error("ethereum error: {0}")] + EthError(#[from] EthError), + + #[error("encryption error: {0}")] + EncryptionError(String), + + #[error("decryption error: {0}")] + DecryptionError(String), +} + +/// The storage format for encrypted signers +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct EncryptedSignerData { + /// The encrypted private key data + pub encrypted_data: Vec, + /// The Ethereum address (for quick reference without decryption) + pub address: String, + /// The chain ID this signer is for + pub chain_id: u64, +} + +/// The Signer trait defines the interface for all signing implementations +pub trait Signer { + /// Get the Ethereum address associated with this signer + fn address(&self) -> EthAddress; + + /// Get the chain ID this signer is configured for + fn chain_id(&self) -> u64; + + /// Sign a transaction with the private key + fn sign_transaction(&self, tx_data: &TransactionData) -> Result, SignerError>; + + /// Sign a message following Ethereum's personal_sign format + fn sign_message(&self, message: &[u8]) -> Result, SignerError>; +} + +/// Local signer implementation using a private key stored in memory +#[derive(Debug, Clone)] +pub struct LocalSigner { + /// The underlying private key signer from alloy + pub inner: PrivateKeySigner, + + /// The Ethereum address derived from the private key + pub address: EthAddress, + + /// The chain ID this signer is configured for + pub chain_id: u64, + + /// The private key as a hex string + pub private_key_hex: String, +} + +impl LocalSigner { + /// Create a new signer with a randomly generated private key + pub fn new_random(chain_id: u64) -> Result { + // Generate a secure random private key + let inner = PrivateKeySigner::random(); + + let address = inner.address(); + + // Extract the private key for storage + let private_key_hex = Self::extract_private_key(&inner)?; + + Ok(Self { + inner, + address, + chain_id, + private_key_hex, + }) + } + + /// Create a signer from a private key in hexadecimal string format + pub fn from_private_key(private_key: &str, chain_id: u64) -> Result { + // Remove 0x prefix if present + let clean_key = private_key.trim_start_matches("0x"); + + // Parse hex string into bytes + if clean_key.len() != 64 { + return Err(SignerError::InvalidPrivateKey( + "Private key must be 32 bytes (64 hex characters)".to_string() + )); + } + + let key_bytes = hex::decode(clean_key) + .map_err(|e| SignerError::InvalidPrivateKey(e.to_string()))?; + + Self::from_bytes(&key_bytes, chain_id, format!("0x{}", clean_key)) + } + + /// Create a signer from raw private key bytes + fn from_bytes(bytes: &[u8], chain_id: u64, private_key_hex: String) -> Result { + if bytes.len() != 32 { + return Err(SignerError::InvalidPrivateKey( + "Private key must be exactly 32 bytes".to_string() + )); + } + + // Convert to B256 (fixed bytes) + let key = B256::from_slice(bytes); + + // Create the PrivateKeySigner + let inner = match PrivateKeySigner::from_bytes(&key) { + Ok(wallet) => wallet, + Err(e) => return Err(SignerError::InvalidPrivateKey(e.to_string())), + }; + + let address = inner.address(); + + Ok(Self { + inner, + address, + chain_id, + private_key_hex, + }) + } + + // jank + /// Extract the private key hex string from an Alloy PrivateKeySigner + fn extract_private_key(wallet: &PrivateKeySigner) -> Result { + // This is a placeholder implementation until we find a better way to extract + // the private key from PrivateKeySigner + let debug_str = format!("{:?}", wallet); + + // Parse the private key from the debug output + let start_marker = "private_key: PrivateKey("; + let end_marker = ")"; + + if let Some(start_idx) = debug_str.find(start_marker) { + let key_start = start_idx + start_marker.len(); + if let Some(end_idx) = debug_str[key_start..].find(end_marker) { + let key_hex = &debug_str[key_start..key_start + end_idx]; + return Ok(format!("0x{}", key_hex.trim_start_matches("0x"))); + } + } + + Err(SignerError::InvalidPrivateKey("Failed to extract private key".into())) + } + + /// Encrypt this signer using a password + pub fn encrypt(&self, password: &str) -> Result { + // Extract the private key hex (without 0x prefix) + let clean_key = self.private_key_hex.trim_start_matches("0x"); + + // Convert to bytes + let key_bytes = hex::decode(clean_key) + .map_err(|e| SignerError::EncryptionError(e.to_string()))?; + + // Encrypt the private key + let encrypted_data = encrypt_data(&key_bytes, password) + .map_err(|e| SignerError::EncryptionError(e))?; + + // Create encrypted data structure + Ok(EncryptedSignerData { + encrypted_data, + address: self.address.to_string(), + chain_id: self.chain_id, + }) + } + + /// Decrypt an encrypted signer + pub fn decrypt(encrypted: &EncryptedSignerData, password: &str) -> Result { + let decrypted_bytes = decrypt_data(&encrypted.encrypted_data, password) + .map_err(|e| SignerError::DecryptionError(e))?; + + // Convert bytes back to hex string + let private_key_hex = format!("0x{}", hex::encode(&decrypted_bytes)); + + // Create a new signer with the specified chain ID + Self::from_bytes(&decrypted_bytes, encrypted.chain_id, private_key_hex) + } + + /// Export the private key as a hexadecimal string + pub fn export_private_key(&self) -> String { + self.private_key_hex.clone() + } +} + +impl Signer for LocalSigner { + fn address(&self) -> EthAddress { + self.address + } + + fn chain_id(&self) -> u64 { + self.chain_id + } + + fn sign_transaction(&self, tx_data: &TransactionData) -> Result, SignerError> { + // Verify chain ID matches the signer's chain ID + if tx_data.chain_id != self.chain_id { + return Err(SignerError::ChainIdMismatch { + expected: self.chain_id, + actual: tx_data.chain_id, + }); + } + + + // Convert hyperware types to alloy types + let to_str = tx_data.to.to_string(); + let to = alloy_primitives::Address::from_str(&to_str) + .map_err(|e| SignerError::SigningError(format!("Invalid contract address: {}", e)))?; + + let mut tx = TxEip1559 { + chain_id: tx_data.chain_id, + nonce: tx_data.nonce, + to: TxKind::Call(to), + gas_limit: tx_data.gas_limit, + max_fee_per_gas: tx_data.gas_price, + max_priority_fee_per_gas: tx_data.max_priority_fee.unwrap_or(tx_data.gas_price / 10), + input: tx_data.data.clone().unwrap_or_default().into(), + value: tx_data.value, + ..Default::default() + }; + + // Sign the transaction with the wallet + let sig = match self.inner.sign_transaction_sync(&mut tx) { + Ok(sig) => sig, + Err(e) => return Err(SignerError::SigningError(e.to_string())), + }; + + // Create signed transaction envelope + let signed = TxEnvelope::from(tx.into_signed(sig)); + + // Encode the transaction + let mut buf = vec![]; + signed.encode_2718(&mut buf); + + Ok(buf) + } + + fn sign_message(&self, message: &[u8]) -> Result, SignerError> { + // Create the Ethereum signed message prefixed hash + let prefix = format!("\x19Ethereum Signed Message:\n{}", message.len()); + let prefixed_message = [prefix.as_bytes(), message].concat(); + + // Hash the message + let hash = sha3::Keccak256::digest(&prefixed_message); + let hash_bytes = B256::from_slice(hash.as_slice()); + + // Sign the hash + match self.inner.sign_hash_sync(&hash_bytes) { + Ok(signature) => Ok(signature.as_bytes().to_vec()), + Err(e) => Err(SignerError::SigningError(e.to_string())), + } + } +} + +/// Encrypt a private key using a password +pub fn encrypt_data(data: &[u8], password: &str) -> Result, String> { + let mut rng = thread_rng(); + + // Generate salt + let mut salt = [0u8; SALT_SIZE]; + rng.fill_bytes(&mut salt); + + // Generate nonce + let mut nonce = [0u8; NONCE_SIZE]; + rng.fill_bytes(&mut nonce); + + // Derive key using SHA3 + let key = derive_key(password.as_bytes(), &salt); + + // Encrypt data using XOR + let encrypted_data = encrypt_with_key(data, &key, &nonce); + + // Generate authentication tag + let tag = compute_tag(&salt, &nonce, &encrypted_data, &key); + + // Combine all components + Ok([ + salt.as_ref(), + nonce.as_ref(), + encrypted_data.as_ref(), + tag.as_ref(), + ].concat()) +} + +/// Decrypt an encrypted private key +pub fn decrypt_data(encrypted_data: &[u8], password: &str) -> Result, String> { + // Check if data is long enough to contain all components + if encrypted_data.len() < SALT_SIZE + NONCE_SIZE + TAG_SIZE { + return Err("Encrypted data is too short".into()); + } + + // Extract components + let salt = &encrypted_data[..SALT_SIZE]; + let nonce = &encrypted_data[SALT_SIZE..SALT_SIZE + NONCE_SIZE]; + let tag = &encrypted_data[encrypted_data.len() - TAG_SIZE..]; + let ciphertext = &encrypted_data[SALT_SIZE + NONCE_SIZE..encrypted_data.len() - TAG_SIZE]; + + // Derive key + let key = derive_key(password.as_bytes(), salt); + + // Verify the authentication tag + let expected_tag = compute_tag(salt, nonce, ciphertext, &key); + if tag != expected_tag { + return Err("Decryption failed: Authentication tag mismatch".into()); + } + + // Decrypt data + let plaintext = decrypt_with_key(ciphertext, &key, nonce); + + Ok(plaintext) +} + +/// Derive a key from a password and salt using SHA3 +fn derive_key(password: &[u8], salt: &[u8]) -> [u8; KEY_SIZE] { + // Initial hash + let mut hasher = Sha3_256::new(); + hasher.update(salt); + hasher.update(password); + let mut key = hasher.finalize().into(); + + // Multiple iterations for stronger key derivation + for _ in 0..10000 { + let mut hasher = Sha3_256::new(); + hasher.update(key); + hasher.update(salt); + key = hasher.finalize().into(); + } + + key +} + +/// Encrypt data with a key and nonce using XOR +fn encrypt_with_key(data: &[u8], key: &[u8; KEY_SIZE], nonce: &[u8]) -> Vec { + let mut result = Vec::with_capacity(data.len()); + + for (i, &byte) in data.iter().enumerate() { + // Create a unique keystream byte for each position + let key_byte = key[i % key.len()]; + let nonce_byte = nonce[i % nonce.len()]; + let keystream = key_byte ^ nonce_byte ^ (i as u8); + + // XOR with data + result.push(byte ^ keystream); + } + + result +} + +/// Decrypt data (same as encrypt since XOR is symmetric) +fn decrypt_with_key(data: &[u8], key: &[u8; KEY_SIZE], nonce: &[u8]) -> Vec { + encrypt_with_key(data, key, nonce) +} + +/// Compute an authentication tag using SHA3 +fn compute_tag(salt: &[u8], nonce: &[u8], data: &[u8], key: &[u8]) -> Vec { + let mut hasher = Sha3_256::new(); + hasher.update(salt); + hasher.update(nonce); + hasher.update(data); + hasher.update(key); + + let hash = hasher.finalize(); + hash[..TAG_SIZE].to_vec() +} \ No newline at end of file diff --git a/src/tba_explorer.rs b/src/tba_explorer.rs new file mode 100644 index 0000000..3eac6ce --- /dev/null +++ b/src/tba_explorer.rs @@ -0,0 +1,391 @@ +/// not ready for thought + + +use crate::{ + eth::{ + self, + Bytes, + Provider, + Address as EthAddress + }, + hypermap::{ + self, + Hypermap + }, + println as kiprintln, +}; +use alloy_primitives::{ + B256, + FixedBytes +}; +use alloy::rpc_types::request::{ + TransactionInput, + TransactionRequest +}; +use alloy_sol_types::SolValue; +use std::str::FromStr; +use hex; +use serde::{ + Serialize, + Deserialize +}; + +/// ERC-6551 Account Interface for interacting with Token-Bound Accounts +pub struct Erc6551Account { + address: EthAddress, + provider: Provider, +} + +impl Erc6551Account { + pub fn new(address: EthAddress, provider: Provider) -> Self { + Self { address, provider } + } + + /// Get token information from the TBA + pub fn token(&self) -> Result<(u64, EthAddress, u64)> { + // Function selector for token() + let selector = [0x45, 0xc3, 0x11, 0x87]; // token() + + let tx_req = TransactionRequest::default() + .input(TransactionInput::new(Bytes::from(selector.to_vec()))) + .to(self.address); + + let res_bytes = self.provider.call(tx_req, None)?; + + // Parse the response (chainId, tokenContract, tokenId) + if res_bytes.len() < 96 { + return Err(anyhow!("Invalid response length for token() call")); + } + + // Extract chainId (first 32 bytes) + let chain_id = u64::from_be_bytes([ + res_bytes[24], res_bytes[25], res_bytes[26], res_bytes[27], + res_bytes[28], res_bytes[29], res_bytes[30], res_bytes[31], + ]); + + // Extract tokenContract (next 32 bytes, but we only need 20 bytes) + let token_contract = EthAddress::from_slice(&res_bytes[44..64]); + + // Extract tokenId (last 32 bytes) + let token_id = u64::from_be_bytes([ + res_bytes[88], res_bytes[89], res_bytes[90], res_bytes[91], + res_bytes[92], res_bytes[93], res_bytes[94], res_bytes[95], + ]); + + Ok((chain_id, token_contract, token_id)) + } + + /// Check if a signer is valid for this account + pub fn is_valid_signer(&self, signer: &EthAddress) -> Result { + // Function selector for isValidSigner(address,bytes) + let selector = [0x52, 0x65, 0x78, 0x4c, 0x3c]; + + // Encode the signer address (padded to 32 bytes) + let mut call_data = Vec::with_capacity(68); + call_data.extend_from_slice(&selector); + + // Add the address parameter (padded to 32 bytes) + call_data.extend_from_slice(&[0; 12]); // 12 bytes padding + call_data.extend_from_slice(signer.as_slice()); + + // Add offset to empty bytes array (32) and length (0) + call_data.extend_from_slice(&[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32]); + call_data.extend_from_slice(&[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]); + + let tx_req = TransactionRequest::default() + .input(TransactionInput::new(Bytes::from(call_data))) + .to(self.address); + + match self.provider.call(tx_req, None) { + Ok(res) => { + // Expect a boolean response (32 bytes with last byte being 0 or 1) + if res.len() >= 32 && (res[31] == 1) { + Ok(true) + } else { + Ok(false) + } + }, + Err(e) => { + kiprintln!("isValidSigner call failed: {:?}", e); + Ok(false) + } + } + } + + /// Get the implementation address of this TBA + pub fn get_implementation(&self) -> Result { + // EIP-1967 implementation slot + let impl_slot = B256::from_str("0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc")?; + + match self.provider.get_storage_at(self.address, impl_slot, None) { + Ok(value) => { + // Extract address from storage value (last 20 bytes) + let bytes = value.as_ref(); + if bytes.len() >= 20 { + let impl_addr = EthAddress::from_slice(&bytes[bytes.len()-20..]); + Ok(impl_addr) + } else { + Err(anyhow!("Invalid implementation address format")) + } + }, + Err(e) => Err(anyhow!("Failed to get implementation: {:?}", e)), + } + } + + /// Check if an auth-key is set on the TBA + pub fn has_auth_key(&self) -> Result { + // Storage slot for auth keys (depends on implementation) + // This would need to match the exact implementation's storage layout + let auth_key_slot = B256::from_str("0x0000000000000000000000000000000000000000000000000000000000000001")?; + + match self.provider.get_storage_at(self.address, auth_key_slot, None) { + Ok(value) => { + // Check if the slot has a non-zero value + let bytes = value.as_ref(); + Ok(!bytes.iter().all(|&b| b == 0)) + }, + Err(e) => { + kiprintln!("Failed to check auth key: {:?}", e); + Ok(false) + } + } + } +} + +/// Complete TBA information structure +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct TbaInfo { + pub node_name: String, + pub tba_address: EthAddress, + pub owner_address: EthAddress, + pub implementation: EthAddress, + pub chain_id: u64, + pub token_contract: EthAddress, + pub token_id: u64, + pub auth_signers: Vec, +} + +/// Extended Hypermap functionality specifically for TBA exploration +pub struct TbaExplorer { + hypermap: Hypermap, +} + +impl TbaExplorer { + pub fn new(timeout: u64) -> Self { + Self { + hypermap: Hypermap::default(timeout), + } + } + + /// Format a node name to ensure it has proper extension + pub fn format_node_name(&self, node_name: &str) -> String { + if !node_name.contains('.') { + format!("{}.hypr", node_name) + } else { + node_name.to_string() + } + } + + /// Get information about a TBA by node name + pub fn get_tba_info(&self, node_name: &str) -> Result { + // Format node name properly + let name = self.format_node_name(node_name); + + // Get TBA and owner from Hypermap + let (tba_address, owner_address, _) = self.hypermap.get(&name)?; + + // Create ERC-6551 account wrapper + let account = Erc6551Account::new(tba_address, self.hypermap.provider.clone()); + + // Get token info + let (chain_id, token_contract, token_id) = match account.token() { + Ok(info) => info, + Err(e) => { + kiprintln!("Failed to get token info: {:?}", e); + // Provide defaults + (0, EthAddress::default(), 0) + } + }; + + // Get implementation + let implementation = match account.get_implementation() { + Ok(impl_addr) => impl_addr, + Err(e) => { + kiprintln!("Failed to get implementation: {:?}", e); + EthAddress::default() + } + }; + + // Get custom auth signers from ~auth-signers note if it exists + let auth_signers = self.get_auth_signers(&name)?; + + Ok(TbaInfo { + node_name: name, + tba_address, + owner_address, + implementation, + chain_id, + token_contract, + token_id, + auth_signers, + }) + } + + /// Get authorized signers from a node's notes + fn get_auth_signers(&self, node_name: &str) -> Result> { + let mut auth_signers = Vec::new(); + + // Get the namehash + let namehash = hypermap::namehash(node_name); + + // Create filter for ~auth-signers note + let note_filter = self.hypermap.notes_filter(&["~auth-signers"]) + .topic1(vec![FixedBytes::<32>::from_str(&namehash)?]); + + // Get logs + let logs = self.hypermap.provider.get_logs(¬e_filter)?; + + if logs.is_empty() { + return Ok(Vec::new()); + } + + // Process the latest version of the note + if let Some(latest_log) = logs.last() { + if let Ok(note) = hypermap::decode_note_log(latest_log) { + // Try to parse as JSON list of addresses + if let Ok(json) = serde_json::from_slice::(¬e.data) { + if let Some(addresses) = json.as_array() { + for addr in addresses { + if let Some(addr_str) = addr.as_str() { + if let Ok(address) = EthAddress::from_str(addr_str) { + auth_signers.push(address); + } + } + } + } + } else { + // If not JSON, try to parse as comma-separated list + let data_str = String::from_utf8_lossy(¬e.data); + for addr_str in data_str.split(',') { + let trimmed = addr_str.trim(); + if let Ok(address) = EthAddress::from_str(trimmed) { + auth_signers.push(address); + } + } + } + } + } + + Ok(auth_signers) + } + + /// Check if an address is authorized to sign for a node's TBA + pub fn is_authorized_signer(&self, node_name: &str, signer: &EthAddress) -> Result { + // Get TBA info + let tba_info = self.get_tba_info(node_name)?; + + // Check if signer is owner (always authorized) + if &tba_info.owner_address == signer { + return Ok(true); + } + + // Check if signer is in auth_signers list from note + if tba_info.auth_signers.contains(signer) { + return Ok(true); + } + + // Check using isValidSigner directly + let account = Erc6551Account::new(tba_info.tba_address, self.hypermap.provider.clone()); + account.is_valid_signer(signer) + } + + /// Get all ~net-key and any custom signing keys from a node + pub fn get_signing_keys(&self, node_name: &str) -> Result>> { + let mut keys = Vec::new(); + + // Format node name + let name = self.format_node_name(node_name); + + // Get the namehash + let namehash = hypermap::namehash(&name); + + // Create filter for ~net-key and ~signing-key notes + let keys_filter = self.hypermap.notes_filter(&["~net-key", "~signing-key"]) + .topic1(vec![FixedBytes::<32>::from_str(&namehash)?]); + + // Get logs + let logs = self.hypermap.provider.get_logs(&keys_filter)?; + + for log in logs { + if let Ok(note) = hypermap::decode_note_log(&log) { + // Add the key data + keys.push(note.data.to_vec()); + } + } + + Ok(keys) + } + + /// Check if a TBA supports the custom auth signer mechanism + pub fn supports_auth_signers(&self, node_name: &str) -> Result { + // Get TBA info + let tba_info = self.get_tba_info(node_name)?; + + // Check if the implementation is the customized TBA + // This would require knowing the specific implementation address + // For now, we'll just check if the implementation isn't the default + if tba_info.implementation == EthAddress::default() { + return Ok(false); + } + + // We could also check if there's already an auth_signers note + if !tba_info.auth_signers.is_empty() { + return Ok(true); + } + + // Try calling isValidSigner with a test address to see if it works + let account = Erc6551Account::new(tba_info.tba_address, self.hypermap.provider.clone()); + + // Check if auth key slot is used + account.has_auth_key() + } + + /// Format key data for storage in Hypermap + pub fn format_auth_signers(&self, signers: &[EthAddress]) -> Result { + // Format as JSON array of address strings + let signer_strings: Vec = signers.iter() + .map(|addr| addr.to_string()) + .collect(); + + let json = serde_json::to_string(&signer_strings)?; + + Ok(Bytes::copy_from_slice(json.as_bytes())) + } +} + +/// Helper function to convert a hex string to an Address +pub fn hex_to_address(hex_str: &str) -> Result { + let cleaned = hex_str.trim_start_matches("0x"); + if cleaned.len() != 40 { + return Err(anyhow!("Invalid address length")); + } + + let bytes = hex::decode(cleaned)?; + Ok(EthAddress::from_slice(&bytes)) +} + +/// Helper function to convert bytes to a human-readable format +pub fn format_bytes(bytes: &[u8]) -> String { + if bytes.len() <= 64 { + // For small data, show full hex + format!("0x{}", hex::encode(bytes)) + } else { + // For larger data, truncate + format!("0x{}...{} ({} bytes)", + hex::encode(&bytes[..8]), + hex::encode(&bytes[bytes.len() - 8..]), + bytes.len()) + } +} \ No newline at end of file diff --git a/src/wallet.rs b/src/wallet.rs new file mode 100644 index 0000000..c01afaf --- /dev/null +++ b/src/wallet.rs @@ -0,0 +1,265 @@ +//! Ethereum wallet functionality for Hyperware. +//! +//! This module provides higher-level wallet functionality, building on top of +//! the cryptographic operations in the signer module. It handles transaction +//! construction, name resolution, and account management. +//! +//! wallet module: +//! 1. Provides convenient transaction creation and submission +//! 2. Handles Hypermap name resolution +//! 3. Manages account state and balances +//! 4. Offers a simpler interface for common ETH operations (more to do here) + +use crate::eth::{ + Provider, + EthError +}; +use crate::signer::{ + Signer, + LocalSigner, + TransactionData, + SignerError, + EncryptedSignerData +}; +use crate::hypermap; + +use thiserror::Error; +use alloy_primitives::{Address as EthAddress, TxHash, U256}; +use std::str::FromStr; + +#[derive(Debug, Error)] +pub enum WalletError { + #[error("signing error: {0}")] + SignerError(#[from] SignerError), + + #[error("ethereum error: {0}")] + EthError(#[from] EthError), + + #[error("name resolution error: {0}")] + NameResolutionError(String), + + #[error("invalid amount: {0}")] + InvalidAmount(String), + + #[error("transaction error: {0}")] + TransactionError(String), +} + +/// Represents the storage state of a wallet's private key +#[derive(Debug, Clone)] +pub enum KeyStorage { + /// An unencrypted wallet with a signer + Decrypted(LocalSigner), + + /// An encrypted wallet - contains all the necessary data + Encrypted(EncryptedSignerData), +} + +impl KeyStorage { + /// Get the encrypted data if this is an encrypted key storage + pub fn get_encrypted_data(&self) -> Option> { + match self { + KeyStorage::Encrypted(data) => Some(data.encrypted_data.clone()), + KeyStorage::Decrypted(_) => None, + } + } + + /// Get the address associated with this wallet + pub fn get_address(&self) -> String { + match self { + KeyStorage::Decrypted(signer) => signer.address().to_string(), + KeyStorage::Encrypted(data) => data.address.clone(), + } + } + + /// Get the chain ID associated with this wallet + pub fn get_chain_id(&self) -> u64 { + match self { + KeyStorage::Decrypted(signer) => signer.chain_id(), + KeyStorage::Encrypted(data) => data.chain_id, + } + } +} + +/// Represents an amount of ETH with proper formatting +#[derive(Debug, Clone)] +pub struct EthAmount { + /// Value in wei + wei_value: U256, +} + +impl EthAmount { + /// Create a new amount from ETH value + pub fn from_eth(eth_value: f64) -> Self { + // Convert ETH to wei (1 ETH = 10^18 wei) + let wei = (eth_value * 1_000_000_000_000_000_000.0) as u128; + Self { + wei_value: U256::from(wei), + } + } + + /// Create from a string like "0.1 ETH" or "10 wei" + pub fn from_string(amount_str: &str) -> Result { + let parts: Vec<&str> = amount_str.trim().split_whitespace().collect(); + + if parts.is_empty() { + return Err(WalletError::InvalidAmount( + "Empty amount string".to_string() + )); + } + + let value_str = parts[0]; + let unit = parts.get(1).map(|s| s.to_lowercase()).unwrap_or_else(|| "eth".to_string()); + + let value = value_str.parse::() + .map_err(|_| WalletError::InvalidAmount(format!("Invalid numeric value: {}", value_str)))?; + + match unit.as_str() { + "eth" => Ok(Self::from_eth(value)), + "wei" => Ok(Self { + wei_value: U256::from(value as u128), + }), + _ => Err(WalletError::InvalidAmount(format!("Unknown unit: {}", unit))), + } + } + + /// Get the value in wei + pub fn as_wei(&self) -> U256 { + self.wei_value + } + + /// Get a human-readable string representation + pub fn to_string(&self) -> String { + // For values over 0.0001 ETH, show in ETH, otherwise in wei + if self.wei_value >= U256::from(100_000_000_000_000u128) { + // Convert to u128 first (safe since ETH total supply fits in u128) then to f64 + let wei_u128 = self.wei_value.to::(); + let eth_value = wei_u128 as f64 / 1_000_000_000_000_000_000.0; + format!("{:.6} ETH", eth_value) + } else { + format!("{} wei", self.wei_value) + } + } +} + +/// Transaction receipt returned after sending +#[derive(Debug, Clone)] +pub struct TxReceipt { + /// Transaction hash + pub hash: TxHash, + /// Transaction details + pub details: String, +} + +// The checks here aren't solid, but it works for now. Will also expand with full hypermap support +/// Resolve a .hypr name to an Ethereum address using Hypermap. +pub fn resolve_name(name: &str, _chain_id: u64) -> Result { + // If it's already an address, just parse it + if name.starts_with("0x") && name.len() == 42 { + return EthAddress::from_str(name) + .map_err(|_| WalletError::NameResolutionError(format!("Invalid address format: {}", name))); + } + + // Format the name properly if it doesn't contain dots + let formatted_name = if !name.contains('.') { + format!("{}.hypr", name) + } else { + name.to_string() + }; + + // Use hypermap resolution + let hypermap = hypermap::Hypermap::default(60); + match hypermap.get(&formatted_name) { + Ok((_tba, owner, _)) => { + Ok(owner) + }, + Err(e) => { + Err(WalletError::NameResolutionError( + format!("Failed to resolve name '{}': {}", name, e) + )) + } + } +} + +/// Send ETH to an address or name +pub fn send_eth( + to: &str, + amount: EthAmount, + provider: Provider, + signer: &S, +) -> Result { + // Special handling for Anvil (31337) or other test networks + let chain_id = signer.chain_id(); + // temp + let is_test_network = chain_id == 31337 || chain_id == 1337; + + // Resolve the name to an address + let to_address = resolve_name(to, chain_id)?; + + // Get the current nonce for the signer's address + let from_address = signer.address(); + let nonce = provider.get_transaction_count(from_address, None)? + .to::(); + + // Get gas pricing based on network + let (gas_price, priority_fee) = if is_test_network { + // For test networks like Anvil, use a fixed gas price that's known to work + // These specific values work reliably with Anvil + (2_000_000_000, 100_000_000) // 2 gwei, 0.1 gwei priority fee + } else { + // For real networks, get current gas price + let base_fee = provider.get_gas_price()? + .to::(); + + // Increase by 20% to ensure transaction goes through + let adjusted_fee = (base_fee * 120) / 100; + + // Priority fee at 10% of gas price + (adjusted_fee, adjusted_fee / 10) + }; + + // Standard gas limit for ETH transfer + let gas_limit = 21000; + + // Prepare transaction data + let tx_data = TransactionData { + to: to_address, + value: amount.as_wei(), + data: None, // No data for simple ETH transfer + nonce, + gas_limit, + gas_price, + max_priority_fee: Some(priority_fee), + chain_id, + }; + + // Sign the transaction + let signed_tx = signer.sign_transaction(&tx_data)?; + + // Send the transaction + let tx_hash = provider.send_raw_transaction(signed_tx.into())?; + + // Return the receipt with transaction details + Ok(TxReceipt { + hash: tx_hash, + details: format!("Sent {} to {}", amount.to_string(), to), + }) +} + +/// Get the ETH balance for an address or name +pub fn get_balance( + address_or_name: &str, + chain_id: u64, + provider: Provider, +) -> Result { + // Resolve name to address + let address = resolve_name(address_or_name, chain_id)?; + + // Query balance + let balance = provider.get_balance(address, None)?; + + // Return formatted amount + Ok(EthAmount { + wei_value: balance, + }) +} \ No newline at end of file From 860c2b4bf8c0b04d122c99cb5ad3251fa1e76717 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 26 Mar 2025 21:24:06 +0000 Subject: [PATCH 02/96] Format Rust code using rustfmt --- src/eth_utils.rs | 86 ++++++++++++------------ src/lib.rs | 12 ++-- src/signer.rs | 169 +++++++++++++++++++++++------------------------ src/wallet.rs | 122 ++++++++++++++++------------------ 4 files changed, 189 insertions(+), 200 deletions(-) diff --git a/src/eth_utils.rs b/src/eth_utils.rs index 4a9e08c..9cc3f0e 100644 --- a/src/eth_utils.rs +++ b/src/eth_utils.rs @@ -2,7 +2,7 @@ ////! ////! This module provides utility functions for common Ethereum operations in the Hyperware ////! ecosystem, particularly focusing on integrating with Hypermap for name resolution. -////! +////! ////! The main goals of this module are: ////! 1. Provide simple, developer-friendly functions for common operations ////! 2. Abstract away the complexity of blockchain interactions @@ -12,24 +12,24 @@ ////! ////! ```rust ////! use hyperware_process_lib::eth_utils; -////! +////! ////! // Send ETH to a Hypermap name ////! let tx_hash = eth_utils::send_eth("alice.hypr", 1.3)?; -////! +////! ////! // Check if a wallet owns an NFT ////! let has_token = eth_utils::has_nft(contract_address, token_id, wallet_address)?; -////! +////! ////! // Get a token balance ////! let balance = eth_utils::get_token_balance(token_address, wallet_address)?; -////! +////! ////! // Send tokens to a Hypermap name ////! let tx_hash = eth_utils::send_token_to_name(token_address, "bob.hypr", amount)?; ////! ``` // //use crate::eth::{ -// Address, -// EthError, -// TxHash, +// Address, +// EthError, +// TxHash, // U256 //}; //use crate::hypermap::{Hypermap, HYPERMAP_ADDRESS}; @@ -56,13 +56,13 @@ // // #[error("Name resolution error: {0}")] // NameResolution(String), -// +// // #[error("Transaction error: {0}")] // Transaction(String), //} // ///// Send Ether to an address -///// +///// ///// This function creates, signs, and sends a transaction to send ETH to an address. ///// ///// # Parameters @@ -75,13 +75,13 @@ ///// ///// # Returns ///// A `Result` representing the transaction hash if successful -///// +///// ///// # Example ///// ```rust ///// use hyperware_process_lib::{eth_utils, wallet, eth}; ///// use alloy_primitives::{Address, U256}; ///// use std::str::FromStr; -///// +///// ///// // Create wallet and provider ///// let wallet = wallet::Wallet::from_private_key( ///// "0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef", @@ -90,7 +90,7 @@ ///// let provider = eth::Provider::new(8453, 60000); ///// let to = Address::from_str("0x7E5F4552091A69125d5DfCb7b8C2659029395Bdf")?; ///// let amount = U256::from(1000000000000000000u64); // 1 ETH -///// +///// ///// // Send ETH ///// let tx_hash = eth_utils::send_eth(&provider, &wallet, to, amount, None, None)?; ///// println!("Transaction hash: {}", tx_hash); @@ -106,7 +106,7 @@ // // Create RLP-encoded transaction // let nonce = provider.get_transaction_count(wallet.address(), None)?; // let nonce_u64 = u64::try_from(nonce).unwrap_or(0); -// +// // // Get gas price if not provided // let gas_price_value = if let Some(price) = gas_price { // price @@ -114,10 +114,10 @@ // let current_gas_price = provider.get_gas_price()?; // u128::try_from(current_gas_price).unwrap_or(20000000000) // }; -// +// // // Get gas limit // let gas_limit_value = gas_limit.unwrap_or(21000); -// +// // // Create and sign a transaction manually // // First, construct the RLP-encoded transaction // let mut rlp_data = Vec::new(); @@ -126,23 +126,23 @@ // rlp_data.extend_from_slice(&nonce_u64.to_be_bytes()); // rlp_data.extend_from_slice(&gas_limit_value.to_be_bytes()); // rlp_data.extend_from_slice(&gas_price_value.to_be_bytes()); -// +// // // Hash the transaction data with keccak256 // let mut hasher = sha3::Keccak256::new(); // hasher.update(&rlp_data); // let tx_hash = hasher.finalize(); -// +// // // Sign the transaction hash // let signed_tx = wallet.sign_transaction_hash(&tx_hash)?; -// +// // // Send raw transaction // let tx_hash = provider.send_raw_transaction(signed_tx)?; -// +// // Ok(tx_hash) //} // ///// Sends Ether to the owner of the specified Hypermap name. -///// +///// ///// This function first resolves the name to its owner address using Hypermap, ///// then sends the specified amount of Ether to that address. ///// @@ -154,18 +154,18 @@ ///// ///// # Returns ///// A `Result` representing the transaction hash if successful -///// +///// ///// # Example ///// ```rust ///// use hyperware_process_lib::{eth_utils, wallet, eth}; -///// +///// ///// // Create wallet and provider ///// let wallet = wallet::Wallet::from_private_key( ///// "0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef", ///// 8453 // Base chain ID ///// )?; ///// let provider = eth::Provider::new(8453, 60000); -///// +///// ///// // Send 1.3 ETH to alice.hypr ///// let tx_hash = eth_utils::send_eth_to_name(&provider, &wallet, "alice.hypr", 1.3)?; ///// println!("Transaction hash: {}", tx_hash); @@ -173,11 +173,11 @@ //pub fn send_eth_to_name( // provider: &crate::eth::Provider, // wallet: &Wallet, -// name: &str, +// name: &str, // amount_eth: f64 //) -> Result { // // Get Hypermap instance using our provider -// let hypermap = Hypermap::new(provider.clone(), +// let hypermap = Hypermap::new(provider.clone(), // Address::from_str(HYPERMAP_ADDRESS).unwrap()); // // // Format the name if needed (add .hypr if missing) @@ -186,23 +186,23 @@ // // Resolve name to owner address // let (_, owner, _) = hypermap.get(&formatted_name) // .map_err(|e| EthUtilsError::NameResolution(format!("Failed to resolve name '{}': {}", formatted_name, e)))?; -// +// // // Convert amount to wei (1 ETH = 10^18 wei) // let amount_wei = (amount_eth * 1e18) as u128; // let amount_in_wei = U256::from(amount_wei); -// +// // // Send ETH to the resolved address // send_eth(provider, wallet, owner, amount_in_wei, None, None) //} // ///// Format a name for Hypermap resolution -///// +///// ///// If the name already contains a dot (.), it's returned as is. ///// Otherwise, ".hypr" is appended to the name. -///// +///// ///// # Parameters ///// - `name`: The name to format -///// +///// ///// # Returns ///// A formatted name suitable for Hypermap resolution //fn format_hypermap_name(name: &str) -> String { @@ -210,25 +210,25 @@ // if name.contains('.') { // return name.to_string(); // } -// +// // // Otherwise, add the default .hypr extension // format!("{}.hypr", name) //} // ///// Resolve a Hypermap name to its owner's Ethereum address -///// +///// ///// # Parameters ///// - `name`: The Hypermap name to resolve ///// - `chain_id`: Optional chain ID to use (defaults to Base chain) ///// - `timeout_ms`: Optional timeout in milliseconds (defaults to 60 seconds) -///// +///// ///// # Returns ///// A `Result` representing the owner's Ethereum address -///// +///// ///// # Example ///// ```rust ///// use hyperware_process_lib::eth_utils; -///// +///// ///// let owner = eth_utils::resolve_name("alice.hypr", None, None)?; ///// println!("Owner address: {}", owner); ///// ``` @@ -240,12 +240,12 @@ // // Use provided chain ID or default // let chain_id = chain_id.unwrap_or(DEFAULT_CHAIN_ID); // let timeout = timeout_ms.unwrap_or(DEFAULT_TIMEOUT_MS); -// +// // // Create provider // let provider = crate::eth::Provider::new(chain_id, timeout); -// +// // // Get Hypermap instance using our provider -// let hypermap = Hypermap::new(provider, +// let hypermap = Hypermap::new(provider, // Address::from_str(HYPERMAP_ADDRESS).unwrap()); // // // Format the name if needed (add .hypr if missing) @@ -253,7 +253,7 @@ // // // Resolve name to owner address // let (_, owner, _) = hypermap.get(&formatted_name)?; -// +// // Ok(owner) //} // @@ -268,7 +268,7 @@ // // Test with name that already has dot // let name_with_dot = "test.hypr"; // assert_eq!(format_hypermap_name(name_with_dot), name_with_dot); -// +// // // Test with name that doesn't have dot // let name_without_dot = "test"; // assert_eq!(format_hypermap_name(name_without_dot), "test.hypr"); @@ -276,7 +276,7 @@ // // // Note: These tests would need real providers and wallets to run // // We'll implement placeholders that describe what should be tested -// +// // #[test] // #[ignore] // Ignore this test since it requires network connectivity // fn test_resolve_name() { @@ -295,4 +295,4 @@ // // let result = send_eth_to_name(&provider, &wallet, "test.hypr", 0.001); // // assert!(result.is_ok()); // } -//} \ No newline at end of file +//} diff --git a/src/lib.rs b/src/lib.rs index dd1f1d1..031b6a2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -26,10 +26,6 @@ wit_bindgen::generate!({ pub mod eth; /// High-level Ethereum utilities for common operations. pub mod eth_utils; -/// Ethereum wallet management with transaction preparation and submission. -pub mod wallet; -/// Low-level Ethereum signing operations and key management. -pub mod signer; /// Your process must have the [`Capability`] to message /// `homepage:homepage:sys` to use this module. pub mod homepage; @@ -39,12 +35,12 @@ pub mod homepage; /// Your process must have the [`Capability`] to message and receive messages from /// `http-server:distro:sys` and/or `http-client:distro:sys` to use this module. pub mod http; +/// Interact with hypermap, the onchain namespace +pub mod hypermap; /// The types that the kernel itself uses -- warning -- these will /// be incompatible with WIT types in some cases, leading to annoying errors. /// Use only to interact with the kernel or runtime in certain ways. pub mod kernel_types; -/// Interact with hypermap, the onchain namespace -pub mod hypermap; /// Tools for exploring and working with Token-Bound Accounts (TBAs) in Hypermap //pub mod tba_explorer; /// Interact with the key_value module @@ -60,6 +56,8 @@ pub mod logging; /// Your process must have the [`Capability`] to message and receive messages from /// `net:distro:sys` to use this module. pub mod net; +/// Low-level Ethereum signing operations and key management. +pub mod signer; /// Interact with the sqlite module /// /// Your process must have the [`Capability] to message and receive messages from @@ -74,6 +72,8 @@ pub mod timer; /// Your process must have the [`Capability`] to message and receive messages from /// `vfs:distro:sys` to use this module. pub mod vfs; +/// Ethereum wallet management with transaction preparation and submission. +pub mod wallet; /// A set of types and macros for writing "script" processes. pub mod scripting; diff --git a/src/signer.rs b/src/signer.rs index 45fffcb..6349257 100644 --- a/src/signer.rs +++ b/src/signer.rs @@ -5,31 +5,20 @@ //! It separates the cryptographic concerns from the higher-level wallet functionality. use crate::eth::EthError; -use thiserror::Error; use hex; -use sha3::{Sha3_256, Digest}; use rand::{thread_rng, RngCore}; -use serde::{Serialize, Deserialize}; +use serde::{Deserialize, Serialize}; +use sha3::{Digest, Sha3_256}; +use thiserror::Error; -use alloy_primitives::{ - Address as EthAddress, - U256, - B256, -}; use alloy::{ - signers::{ - local::PrivateKeySigner, - SignerSync, - }, - consensus::{ - SignableTransaction, - TxEip1559, - TxEnvelope - }, + consensus::{SignableTransaction, TxEip1559, TxEnvelope}, network::eip2718::Encodable2718, - primitives::TxKind, network::TxSignerSync, + primitives::TxKind, + signers::{local::PrivateKeySigner, SignerSync}, }; +use alloy_primitives::{Address as EthAddress, B256, U256}; use std::str::FromStr; // For encryption/decryption @@ -64,22 +53,22 @@ pub struct TransactionData { pub enum SignerError { #[error("failed to generate random bytes: {0}")] RandomGenerationError(String), - + #[error("invalid private key format: {0}")] InvalidPrivateKey(String), - + #[error("chain ID mismatch: expected {expected}, got {actual}")] ChainIdMismatch { expected: u64, actual: u64 }, - + #[error("failed to sign transaction or message: {0}")] SigningError(String), - + #[error("ethereum error: {0}")] EthError(#[from] EthError), - + #[error("encryption error: {0}")] EncryptionError(String), - + #[error("decryption error: {0}")] DecryptionError(String), } @@ -99,13 +88,13 @@ pub struct EncryptedSignerData { pub trait Signer { /// Get the Ethereum address associated with this signer fn address(&self) -> EthAddress; - + /// Get the chain ID this signer is configured for fn chain_id(&self) -> u64; - + /// Sign a transaction with the private key fn sign_transaction(&self, tx_data: &TransactionData) -> Result, SignerError>; - + /// Sign a message following Ethereum's personal_sign format fn sign_message(&self, message: &[u8]) -> Result, SignerError>; } @@ -115,13 +104,13 @@ pub trait Signer { pub struct LocalSigner { /// The underlying private key signer from alloy pub inner: PrivateKeySigner, - + /// The Ethereum address derived from the private key pub address: EthAddress, - + /// The chain ID this signer is configured for pub chain_id: u64, - + /// The private key as a hex string pub private_key_hex: String, } @@ -131,12 +120,12 @@ impl LocalSigner { pub fn new_random(chain_id: u64) -> Result { // Generate a secure random private key let inner = PrivateKeySigner::random(); - + let address = inner.address(); - + // Extract the private key for storage let private_key_hex = Self::extract_private_key(&inner)?; - + Ok(Self { inner, address, @@ -144,44 +133,48 @@ impl LocalSigner { private_key_hex, }) } - + /// Create a signer from a private key in hexadecimal string format pub fn from_private_key(private_key: &str, chain_id: u64) -> Result { // Remove 0x prefix if present let clean_key = private_key.trim_start_matches("0x"); - + // Parse hex string into bytes if clean_key.len() != 64 { return Err(SignerError::InvalidPrivateKey( - "Private key must be 32 bytes (64 hex characters)".to_string() + "Private key must be 32 bytes (64 hex characters)".to_string(), )); } - - let key_bytes = hex::decode(clean_key) - .map_err(|e| SignerError::InvalidPrivateKey(e.to_string()))?; - + + let key_bytes = + hex::decode(clean_key).map_err(|e| SignerError::InvalidPrivateKey(e.to_string()))?; + Self::from_bytes(&key_bytes, chain_id, format!("0x{}", clean_key)) } - + /// Create a signer from raw private key bytes - fn from_bytes(bytes: &[u8], chain_id: u64, private_key_hex: String) -> Result { + fn from_bytes( + bytes: &[u8], + chain_id: u64, + private_key_hex: String, + ) -> Result { if bytes.len() != 32 { return Err(SignerError::InvalidPrivateKey( - "Private key must be exactly 32 bytes".to_string() + "Private key must be exactly 32 bytes".to_string(), )); } - + // Convert to B256 (fixed bytes) let key = B256::from_slice(bytes); - + // Create the PrivateKeySigner let inner = match PrivateKeySigner::from_bytes(&key) { Ok(wallet) => wallet, Err(e) => return Err(SignerError::InvalidPrivateKey(e.to_string())), }; - + let address = inner.address(); - + Ok(Self { inner, address, @@ -189,18 +182,18 @@ impl LocalSigner { private_key_hex, }) } - + // jank - /// Extract the private key hex string from an Alloy PrivateKeySigner + /// Extract the private key hex string from an Alloy PrivateKeySigner fn extract_private_key(wallet: &PrivateKeySigner) -> Result { // This is a placeholder implementation until we find a better way to extract // the private key from PrivateKeySigner let debug_str = format!("{:?}", wallet); - + // Parse the private key from the debug output let start_marker = "private_key: PrivateKey("; let end_marker = ")"; - + if let Some(start_idx) = debug_str.find(start_marker) { let key_start = start_idx + start_marker.len(); if let Some(end_idx) = debug_str[key_start..].find(end_marker) { @@ -208,23 +201,25 @@ impl LocalSigner { return Ok(format!("0x{}", key_hex.trim_start_matches("0x"))); } } - - Err(SignerError::InvalidPrivateKey("Failed to extract private key".into())) + + Err(SignerError::InvalidPrivateKey( + "Failed to extract private key".into(), + )) } - + /// Encrypt this signer using a password pub fn encrypt(&self, password: &str) -> Result { // Extract the private key hex (without 0x prefix) let clean_key = self.private_key_hex.trim_start_matches("0x"); - + // Convert to bytes - let key_bytes = hex::decode(clean_key) - .map_err(|e| SignerError::EncryptionError(e.to_string()))?; - + let key_bytes = + hex::decode(clean_key).map_err(|e| SignerError::EncryptionError(e.to_string()))?; + // Encrypt the private key - let encrypted_data = encrypt_data(&key_bytes, password) - .map_err(|e| SignerError::EncryptionError(e))?; - + let encrypted_data = + encrypt_data(&key_bytes, password).map_err(|e| SignerError::EncryptionError(e))?; + // Create encrypted data structure Ok(EncryptedSignerData { encrypted_data, @@ -232,19 +227,19 @@ impl LocalSigner { chain_id: self.chain_id, }) } - + /// Decrypt an encrypted signer pub fn decrypt(encrypted: &EncryptedSignerData, password: &str) -> Result { let decrypted_bytes = decrypt_data(&encrypted.encrypted_data, password) .map_err(|e| SignerError::DecryptionError(e))?; - + // Convert bytes back to hex string let private_key_hex = format!("0x{}", hex::encode(&decrypted_bytes)); - + // Create a new signer with the specified chain ID Self::from_bytes(&decrypted_bytes, encrypted.chain_id, private_key_hex) } - + /// Export the private key as a hexadecimal string pub fn export_private_key(&self) -> String { self.private_key_hex.clone() @@ -255,7 +250,7 @@ impl Signer for LocalSigner { fn address(&self) -> EthAddress { self.address } - + fn chain_id(&self) -> u64 { self.chain_id } @@ -268,13 +263,12 @@ impl Signer for LocalSigner { actual: tx_data.chain_id, }); } - - - // Convert hyperware types to alloy types + + // Convert hyperware types to alloy types let to_str = tx_data.to.to_string(); let to = alloy_primitives::Address::from_str(&to_str) .map_err(|e| SignerError::SigningError(format!("Invalid contract address: {}", e)))?; - + let mut tx = TxEip1559 { chain_id: tx_data.chain_id, nonce: tx_data.nonce, @@ -286,32 +280,32 @@ impl Signer for LocalSigner { value: tx_data.value, ..Default::default() }; - + // Sign the transaction with the wallet let sig = match self.inner.sign_transaction_sync(&mut tx) { Ok(sig) => sig, Err(e) => return Err(SignerError::SigningError(e.to_string())), }; - - // Create signed transaction envelope + + // Create signed transaction envelope let signed = TxEnvelope::from(tx.into_signed(sig)); - + // Encode the transaction let mut buf = vec![]; signed.encode_2718(&mut buf); - + Ok(buf) } - + fn sign_message(&self, message: &[u8]) -> Result, SignerError> { // Create the Ethereum signed message prefixed hash let prefix = format!("\x19Ethereum Signed Message:\n{}", message.len()); let prefixed_message = [prefix.as_bytes(), message].concat(); - + // Hash the message let hash = sha3::Keccak256::digest(&prefixed_message); let hash_bytes = B256::from_slice(hash.as_slice()); - + // Sign the hash match self.inner.sign_hash_sync(&hash_bytes) { Ok(signature) => Ok(signature.as_bytes().to_vec()), @@ -347,7 +341,8 @@ pub fn encrypt_data(data: &[u8], password: &str) -> Result, String> { nonce.as_ref(), encrypted_data.as_ref(), tag.as_ref(), - ].concat()) + ] + .concat()) } /// Decrypt an encrypted private key @@ -374,7 +369,7 @@ pub fn decrypt_data(encrypted_data: &[u8], password: &str) -> Result, St // Decrypt data let plaintext = decrypt_with_key(ciphertext, &key, nonce); - + Ok(plaintext) } @@ -385,7 +380,7 @@ fn derive_key(password: &[u8], salt: &[u8]) -> [u8; KEY_SIZE] { hasher.update(salt); hasher.update(password); let mut key = hasher.finalize().into(); - + // Multiple iterations for stronger key derivation for _ in 0..10000 { let mut hasher = Sha3_256::new(); @@ -393,24 +388,24 @@ fn derive_key(password: &[u8], salt: &[u8]) -> [u8; KEY_SIZE] { hasher.update(salt); key = hasher.finalize().into(); } - + key } /// Encrypt data with a key and nonce using XOR fn encrypt_with_key(data: &[u8], key: &[u8; KEY_SIZE], nonce: &[u8]) -> Vec { let mut result = Vec::with_capacity(data.len()); - + for (i, &byte) in data.iter().enumerate() { // Create a unique keystream byte for each position let key_byte = key[i % key.len()]; let nonce_byte = nonce[i % nonce.len()]; let keystream = key_byte ^ nonce_byte ^ (i as u8); - + // XOR with data result.push(byte ^ keystream); } - + result } @@ -426,7 +421,7 @@ fn compute_tag(salt: &[u8], nonce: &[u8], data: &[u8], key: &[u8]) -> Vec { hasher.update(nonce); hasher.update(data); hasher.update(key); - + let hash = hasher.finalize(); hash[..TAG_SIZE].to_vec() -} \ No newline at end of file +} diff --git a/src/wallet.rs b/src/wallet.rs index c01afaf..762fb6b 100644 --- a/src/wallet.rs +++ b/src/wallet.rs @@ -1,7 +1,7 @@ //! Ethereum wallet functionality for Hyperware. //! //! This module provides higher-level wallet functionality, building on top of -//! the cryptographic operations in the signer module. It handles transaction +//! the cryptographic operations in the signer module. It handles transaction //! construction, name resolution, and account management. //! //! wallet module: @@ -10,37 +10,28 @@ //! 3. Manages account state and balances //! 4. Offers a simpler interface for common ETH operations (more to do here) -use crate::eth::{ - Provider, - EthError -}; -use crate::signer::{ - Signer, - LocalSigner, - TransactionData, - SignerError, - EncryptedSignerData -}; +use crate::eth::{EthError, Provider}; use crate::hypermap; +use crate::signer::{EncryptedSignerData, LocalSigner, Signer, SignerError, TransactionData}; -use thiserror::Error; use alloy_primitives::{Address as EthAddress, TxHash, U256}; use std::str::FromStr; +use thiserror::Error; #[derive(Debug, Error)] pub enum WalletError { #[error("signing error: {0}")] SignerError(#[from] SignerError), - + #[error("ethereum error: {0}")] EthError(#[from] EthError), - + #[error("name resolution error: {0}")] NameResolutionError(String), - + #[error("invalid amount: {0}")] InvalidAmount(String), - + #[error("transaction error: {0}")] TransactionError(String), } @@ -50,7 +41,7 @@ pub enum WalletError { pub enum KeyStorage { /// An unencrypted wallet with a signer Decrypted(LocalSigner), - + /// An encrypted wallet - contains all the necessary data Encrypted(EncryptedSignerData), } @@ -63,7 +54,7 @@ impl KeyStorage { KeyStorage::Decrypted(_) => None, } } - + /// Get the address associated with this wallet pub fn get_address(&self) -> String { match self { @@ -71,7 +62,7 @@ impl KeyStorage { KeyStorage::Encrypted(data) => data.address.clone(), } } - + /// Get the chain ID associated with this wallet pub fn get_chain_id(&self) -> u64 { match self { @@ -97,37 +88,44 @@ impl EthAmount { wei_value: U256::from(wei), } } - + /// Create from a string like "0.1 ETH" or "10 wei" pub fn from_string(amount_str: &str) -> Result { let parts: Vec<&str> = amount_str.trim().split_whitespace().collect(); - + if parts.is_empty() { return Err(WalletError::InvalidAmount( - "Empty amount string".to_string() + "Empty amount string".to_string(), )); } - + let value_str = parts[0]; - let unit = parts.get(1).map(|s| s.to_lowercase()).unwrap_or_else(|| "eth".to_string()); - - let value = value_str.parse::() - .map_err(|_| WalletError::InvalidAmount(format!("Invalid numeric value: {}", value_str)))?; - + let unit = parts + .get(1) + .map(|s| s.to_lowercase()) + .unwrap_or_else(|| "eth".to_string()); + + let value = value_str.parse::().map_err(|_| { + WalletError::InvalidAmount(format!("Invalid numeric value: {}", value_str)) + })?; + match unit.as_str() { "eth" => Ok(Self::from_eth(value)), "wei" => Ok(Self { wei_value: U256::from(value as u128), }), - _ => Err(WalletError::InvalidAmount(format!("Unknown unit: {}", unit))), + _ => Err(WalletError::InvalidAmount(format!( + "Unknown unit: {}", + unit + ))), } } - + /// Get the value in wei pub fn as_wei(&self) -> U256 { self.wei_value } - + /// Get a human-readable string representation pub fn to_string(&self) -> String { // For values over 0.0001 ETH, show in ETH, otherwise in wei @@ -152,32 +150,30 @@ pub struct TxReceipt { } // The checks here aren't solid, but it works for now. Will also expand with full hypermap support -/// Resolve a .hypr name to an Ethereum address using Hypermap. +/// Resolve a .hypr name to an Ethereum address using Hypermap. pub fn resolve_name(name: &str, _chain_id: u64) -> Result { // If it's already an address, just parse it if name.starts_with("0x") && name.len() == 42 { - return EthAddress::from_str(name) - .map_err(|_| WalletError::NameResolutionError(format!("Invalid address format: {}", name))); + return EthAddress::from_str(name).map_err(|_| { + WalletError::NameResolutionError(format!("Invalid address format: {}", name)) + }); } - + // Format the name properly if it doesn't contain dots let formatted_name = if !name.contains('.') { format!("{}.hypr", name) } else { name.to_string() }; - + // Use hypermap resolution let hypermap = hypermap::Hypermap::default(60); match hypermap.get(&formatted_name) { - Ok((_tba, owner, _)) => { - Ok(owner) - }, - Err(e) => { - Err(WalletError::NameResolutionError( - format!("Failed to resolve name '{}': {}", name, e) - )) - } + Ok((_tba, owner, _)) => Ok(owner), + Err(e) => Err(WalletError::NameResolutionError(format!( + "Failed to resolve name '{}': {}", + name, e + ))), } } @@ -192,15 +188,16 @@ pub fn send_eth( let chain_id = signer.chain_id(); // temp let is_test_network = chain_id == 31337 || chain_id == 1337; - + // Resolve the name to an address let to_address = resolve_name(to, chain_id)?; - + // Get the current nonce for the signer's address let from_address = signer.address(); - let nonce = provider.get_transaction_count(from_address, None)? + let nonce = provider + .get_transaction_count(from_address, None)? .to::(); - + // Get gas pricing based on network let (gas_price, priority_fee) = if is_test_network { // For test networks like Anvil, use a fixed gas price that's known to work @@ -208,19 +205,18 @@ pub fn send_eth( (2_000_000_000, 100_000_000) // 2 gwei, 0.1 gwei priority fee } else { // For real networks, get current gas price - let base_fee = provider.get_gas_price()? - .to::(); - + let base_fee = provider.get_gas_price()?.to::(); + // Increase by 20% to ensure transaction goes through let adjusted_fee = (base_fee * 120) / 100; - + // Priority fee at 10% of gas price (adjusted_fee, adjusted_fee / 10) }; - + // Standard gas limit for ETH transfer let gas_limit = 21000; - + // Prepare transaction data let tx_data = TransactionData { to: to_address, @@ -232,13 +228,13 @@ pub fn send_eth( max_priority_fee: Some(priority_fee), chain_id, }; - + // Sign the transaction let signed_tx = signer.sign_transaction(&tx_data)?; - + // Send the transaction let tx_hash = provider.send_raw_transaction(signed_tx.into())?; - + // Return the receipt with transaction details Ok(TxReceipt { hash: tx_hash, @@ -254,12 +250,10 @@ pub fn get_balance( ) -> Result { // Resolve name to address let address = resolve_name(address_or_name, chain_id)?; - + // Query balance let balance = provider.get_balance(address, None)?; - + // Return formatted amount - Ok(EthAmount { - wei_value: balance, - }) -} \ No newline at end of file + Ok(EthAmount { wei_value: balance }) +} From ba0d7c8b98a8c245ab8fe48f77d76cb7e9f61744 Mon Sep 17 00:00:00 2001 From: Hallmane Date: Fri, 28 Mar 2025 16:12:53 +0100 Subject: [PATCH 03/96] base tx works --- src/eth.rs | 7 ++ src/signer.rs | 57 +++++++++++++- src/wallet.rs | 211 ++++++++++++++++++++++++++++++++++++++++++++------ 3 files changed, 250 insertions(+), 25 deletions(-) diff --git a/src/eth.rs b/src/eth.rs index 488e19b..b85941f 100644 --- a/src/eth.rs +++ b/src/eth.rs @@ -14,6 +14,9 @@ use std::collections::{HashMap, HashSet}; use std::error::Error; use std::fmt; +// TODO: remove this +use crate::kiprintln; + /// Subscription kind. Pulled directly from alloy (https://github.com/alloy-rs/alloy). /// Why? Because alloy is not yet 1.0 and the types in this interface must be stable. /// If alloy SubscriptionKind changes, we can implement a transition function in runtime @@ -335,6 +338,8 @@ impl Provider { .unwrap() .map_err(|_| EthError::RpcTimeout)?; + kiprintln!("PROCESS_LIB::send_request_and_parse_response resp: {:#?}", resp); + match resp { Message::Response { body, .. } => match serde_json::from_slice::(&body) { Ok(EthResponse::Response(value)) => { @@ -699,6 +704,8 @@ impl Provider { // NOTE: tx must be encased by a tuple to be serialized correctly params: serde_json::to_value((tx,)).unwrap(), }; + //TODO: remove + kiprintln!("PROCESS_LIB::send_raw_transaction action: {:#?}", action); self.send_request_and_parse_response::(action) } diff --git a/src/signer.rs b/src/signer.rs index 45fffcb..9a7deb0 100644 --- a/src/signer.rs +++ b/src/signer.rs @@ -269,19 +269,30 @@ impl Signer for LocalSigner { }); } - // Convert hyperware types to alloy types let to_str = tx_data.to.to_string(); let to = alloy_primitives::Address::from_str(&to_str) .map_err(|e| SignerError::SigningError(format!("Invalid contract address: {}", e)))?; + // Create transaction based on chain type + // Both Ethereum mainnet and Base use EIP-1559 transactions let mut tx = TxEip1559 { chain_id: tx_data.chain_id, nonce: tx_data.nonce, to: TxKind::Call(to), gas_limit: tx_data.gas_limit, max_fee_per_gas: tx_data.gas_price, - max_priority_fee_per_gas: tx_data.max_priority_fee.unwrap_or(tx_data.gas_price / 10), + // Use provided priority fee or calculate a reasonable default based on the chain + max_priority_fee_per_gas: tx_data.max_priority_fee.unwrap_or_else(|| + match tx_data.chain_id { + // Ethereum mainnet (1) + 1 => tx_data.gas_price / 10, + // Base (8453) - typically accepts lower priority fees + 8453 => tx_data.gas_price / 5, + // Default fallback for other networks + _ => tx_data.gas_price / 10, + } + ), input: tx_data.data.clone().unwrap_or_default().into(), value: tx_data.value, ..Default::default() @@ -302,6 +313,48 @@ impl Signer for LocalSigner { Ok(buf) } + //fn sign_transaction(&self, tx_data: &TransactionData) -> Result, SignerError> { + // // Verify chain ID matches the signer's chain ID + // if tx_data.chain_id != self.chain_id { + // return Err(SignerError::ChainIdMismatch { + // expected: self.chain_id, + // actual: tx_data.chain_id, + // }); + // } + // + // + // // Convert hyperware types to alloy types + // let to_str = tx_data.to.to_string(); + // let to = alloy_primitives::Address::from_str(&to_str) + // .map_err(|e| SignerError::SigningError(format!("Invalid contract address: {}", e)))?; + // + // let mut tx = TxEip1559 { + // chain_id: tx_data.chain_id, + // nonce: tx_data.nonce, + // to: TxKind::Call(to), + // gas_limit: tx_data.gas_limit, + // max_fee_per_gas: tx_data.gas_price, + // max_priority_fee_per_gas: tx_data.max_priority_fee.unwrap_or(tx_data.gas_price / 10), + // input: tx_data.data.clone().unwrap_or_default().into(), + // value: tx_data.value, + // ..Default::default() + // }; + // + // // Sign the transaction with the wallet + // let sig = match self.inner.sign_transaction_sync(&mut tx) { + // Ok(sig) => sig, + // Err(e) => return Err(SignerError::SigningError(e.to_string())), + // }; + // + // // Create signed transaction envelope + // let signed = TxEnvelope::from(tx.into_signed(sig)); + // + // // Encode the transaction + // let mut buf = vec![]; + // signed.encode_2718(&mut buf); + // + // Ok(buf) + //} fn sign_message(&self, message: &[u8]) -> Result, SignerError> { // Create the Ethereum signed message prefixed hash diff --git a/src/wallet.rs b/src/wallet.rs index c01afaf..014ad40 100644 --- a/src/wallet.rs +++ b/src/wallet.rs @@ -12,7 +12,8 @@ use crate::eth::{ Provider, - EthError + EthError, + BlockNumberOrTag }; use crate::signer::{ Signer, @@ -21,10 +22,11 @@ use crate::signer::{ SignerError, EncryptedSignerData }; -use crate::hypermap; +use crate::{hypermap, kiprintln}; use thiserror::Error; use alloy_primitives::{Address as EthAddress, TxHash, U256}; +use alloy::rpc::types::TransactionReceipt; use std::str::FromStr; #[derive(Debug, Error)] @@ -43,6 +45,21 @@ pub enum WalletError { #[error("transaction error: {0}")] TransactionError(String), + + #[error("gas estimation error: {0}")] + GasEstimationError(String), + + #[error("insufficient funds: {0}")] + InsufficientFunds(String), + + #[error("network congestion: {0}")] + NetworkCongestion(String), + + #[error("transaction underpriced")] + TransactionUnderpriced, + + #[error("transaction nonce too low")] + TransactionNonceTooLow, } /// Represents the storage state of a wallet's private key @@ -188,36 +205,52 @@ pub fn send_eth( provider: Provider, signer: &S, ) -> Result { - // Special handling for Anvil (31337) or other test networks + + kiprintln!("PROCESS_LIB::send_eth provider: {:#?}", provider); + + // Current chain-specific handling let chain_id = signer.chain_id(); - // temp + kiprintln!("PROCESS_LIB::send_eth chain_id: {}", chain_id); + + // This part needs improvement - detect network type more robustly let is_test_network = chain_id == 31337 || chain_id == 1337; + // Use network-specific gas strategies + let (gas_price, priority_fee) = match chain_id { + + // just rough calculations for now + 1 => calculate_eth_mainnet_gas(&provider)?, // mainnet + 8453 => calculate_base_gas(&provider)?, // Base + 10 => calculate_optimism_gas(&provider)?, // Optimism + + // Test networks - keep your current approach + _ if is_test_network => (2_000_000_000, 100_000_000), + + // 30% + _ => { + kiprintln!("PROCESS_LIB::send_eth getting gas price"); + let base_fee = provider.get_gas_price()?.to::(); + kiprintln!("PROCESS_LIB::send_eth base_fee: {}", base_fee); + let adjusted_fee = (base_fee * 130) / 100; + kiprintln!("PROCESS_LIB::send_eth adjusted_fee: {}", adjusted_fee); + (adjusted_fee, adjusted_fee / 10) + } + }; + + kiprintln!("PROCESS_LIB::send_eth gas_price: {}", gas_price); + // Resolve the name to an address let to_address = resolve_name(to, chain_id)?; - + + kiprintln!("PROCESS_LIB::send_eth to_address: {}", to_address); + // Get the current nonce for the signer's address let from_address = signer.address(); let nonce = provider.get_transaction_count(from_address, None)? .to::(); - - // Get gas pricing based on network - let (gas_price, priority_fee) = if is_test_network { - // For test networks like Anvil, use a fixed gas price that's known to work - // These specific values work reliably with Anvil - (2_000_000_000, 100_000_000) // 2 gwei, 0.1 gwei priority fee - } else { - // For real networks, get current gas price - let base_fee = provider.get_gas_price()? - .to::(); - - // Increase by 20% to ensure transaction goes through - let adjusted_fee = (base_fee * 120) / 100; - - // Priority fee at 10% of gas price - (adjusted_fee, adjusted_fee / 10) - }; - + + kiprintln!("PROCESS_LIB::send_eth nonce: {}", nonce); + // Standard gas limit for ETH transfer let gas_limit = 21000; @@ -232,12 +265,18 @@ pub fn send_eth( max_priority_fee: Some(priority_fee), chain_id, }; + + kiprintln!("PROCESS_LIB::send_eth tx_data: {:#?}", tx_data); // Sign the transaction let signed_tx = signer.sign_transaction(&tx_data)?; + + kiprintln!("PROCESS_LIB::send_eth signed_tx: {:?}", signed_tx); // Send the transaction let tx_hash = provider.send_raw_transaction(signed_tx.into())?; + + kiprintln!("lol PROCESS_LIB::send_eth tx_hash: {}", tx_hash); // Return the receipt with transaction details Ok(TxReceipt { @@ -246,6 +285,78 @@ pub fn send_eth( }) } +// Helper function to calculate EIP-1559 gas parameters with network-specific values +fn calculate_eip1559_gas( + provider: &Provider, + buffer_fraction: u128, + priority_fee: u128 +) -> Result<(u128, u128), WalletError> { + kiprintln!("PROCESS_LIB::calculate_eip1559_gas provider\n", ); + // Get latest block + let latest_block = provider.get_block_by_number(BlockNumberOrTag::Latest, false)? + .ok_or_else(|| WalletError::TransactionError("Failed to get latest block".into()))?; + + kiprintln!("PROCESS_LIB::calculate_eip1559_gas latest_block: {:#?}", latest_block); + + // Get base fee + let base_fee = latest_block.header.inner.base_fee_per_gas + .ok_or_else(|| WalletError::TransactionError("No base fee in block".into()))? + as u128; + + kiprintln!("PROCESS_LIB::calculate_eip1559_gas base_fee: {}", base_fee); + + // Calculate max fee with the provided buffer fraction + let max_fee = base_fee + (base_fee / buffer_fraction); + + kiprintln!("PROCESS_LIB::calculate_eip1559_gas max_fee: {}", max_fee); + + Ok((max_fee, priority_fee)) +} + +// Network-specific gas calculation for Ethereum mainnet +fn calculate_eth_mainnet_gas(provider: &Provider) -> Result<(u128, u128), WalletError> { + // For mainnet: 50% buffer and 1.5 gwei priority fee + calculate_eip1559_gas(provider, 2, 1_500_000_000u128) +} + +fn calculate_base_gas(provider: &Provider) -> Result<(u128, u128), WalletError> { + // Get the latest block to determine current gas conditions + let latest_block = provider.get_block_by_number(BlockNumberOrTag::Latest, false)? + .ok_or_else(|| WalletError::TransactionError("Failed to get latest block".into()))?; + + // Get base fee from the block + let base_fee = latest_block.header.inner.base_fee_per_gas + .ok_or_else(|| WalletError::TransactionError("No base fee in block".into()))? + as u128; + + // Calculate max fee with a 33% buffer + let max_fee = base_fee + (base_fee / 3); + + // Dynamic priority fee - 10% of base fee, but with a minimum and a maximum + // Low minimum for Base which has very low gas prices + let min_priority_fee = 100_000u128; // 0.0001 gwei minimum + let max_priority_fee = max_fee / 2; // Never more than half the max fee + + let priority_fee = std::cmp::max( + min_priority_fee, + std::cmp::min(base_fee / 10, max_priority_fee) + ); + + Ok((max_fee, priority_fee)) +} + +//// Gas calculation for Base network +//fn calculate_base_gas(provider: &Provider) -> Result<(u128, u128), WalletError> { +// // For Base: 33% buffer and 0.5 gwei priority fee +// calculate_eip1559_gas(provider, 3, 500_000_000u128) +//} + +// Gas calculation for Optimism network +fn calculate_optimism_gas(provider: &Provider) -> Result<(u128, u128), WalletError> { + // For Optimism: 25% buffer and 0.3 gwei priority fee + calculate_eip1559_gas(provider, 4, 300_000_000u128) +} + /// Get the ETH balance for an address or name pub fn get_balance( address_or_name: &str, @@ -262,4 +373,58 @@ pub fn get_balance( Ok(EthAmount { wei_value: balance, }) +} + +pub fn wait_for_transaction( + tx_hash: TxHash, + provider: Provider, + confirmations: u64, + timeout_secs: u64 +) -> Result { + let start_time = std::time::Instant::now(); + let timeout = std::time::Duration::from_secs(timeout_secs); + + loop { + // Check if we've exceeded the timeout + if start_time.elapsed() > timeout { + return Err(WalletError::TransactionError( + format!("Transaction confirmation timeout after {} seconds", timeout_secs) + )); + } + + // Try to get the receipt + if let Ok(Some(receipt)) = provider.get_transaction_receipt(tx_hash) { + // Check if we have enough confirmations + let latest_block = provider.get_block_number()?; + let receipt_block = receipt.block_number.unwrap_or(0) as u64; + + if latest_block >= receipt_block + confirmations { + return Ok(receipt); + } + } + + // Wait a bit before checking again + std::thread::sleep(std::time::Duration::from_secs(2)); + } +} + +// Extract error information from RPC errors +fn extract_rpc_error(error: &EthError) -> WalletError { + match error { + EthError::RpcError(value) => { + // Try to parse the error message + if let Some(message) = value.get("message").and_then(|m| m.as_str()) { + if message.contains("insufficient funds") { + return WalletError::InsufficientFunds(message.to_string()); + } else if message.contains("underpriced") { + return WalletError::TransactionUnderpriced; + } else if message.contains("nonce too low") { + return WalletError::TransactionNonceTooLow; + } + // Add more error patterns as needed + } + WalletError::TransactionError(format!("RPC error: {:?}", value)) + }, + _ => WalletError::TransactionError(format!("Ethereum error: {:?}", error)) + } } \ No newline at end of file From 397a77c2f3829a33e9f1387d99252af0090f2c14 Mon Sep 17 00:00:00 2001 From: Hallmane Date: Tue, 1 Apr 2025 11:16:45 +0200 Subject: [PATCH 04/96] f5 --- src/eth.rs | 7 +- src/signer.rs | 42 --- src/wallet.rs | 815 ++++++++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 791 insertions(+), 73 deletions(-) diff --git a/src/eth.rs b/src/eth.rs index b85941f..a5c72df 100644 --- a/src/eth.rs +++ b/src/eth.rs @@ -309,8 +309,8 @@ impl<'de> Deserialize<'de> for NodeOrRpcUrl { /// for that chain. #[derive(Clone, Debug, Deserialize, Serialize)] pub struct Provider { - chain_id: u64, - request_timeout: u64, + pub chain_id: u64, + pub request_timeout: u64, } impl Provider { @@ -338,7 +338,8 @@ impl Provider { .unwrap() .map_err(|_| EthError::RpcTimeout)?; - kiprintln!("PROCESS_LIB::send_request_and_parse_response resp: {:#?}", resp); + //TODO: remove + //kiprintln!("PROCESS_LIB::send_request_and_parse_response resp: {:#?}", resp); match resp { Message::Response { body, .. } => match serde_json::from_slice::(&body) { diff --git a/src/signer.rs b/src/signer.rs index 9a7deb0..c146383 100644 --- a/src/signer.rs +++ b/src/signer.rs @@ -313,48 +313,6 @@ impl Signer for LocalSigner { Ok(buf) } - //fn sign_transaction(&self, tx_data: &TransactionData) -> Result, SignerError> { - // // Verify chain ID matches the signer's chain ID - // if tx_data.chain_id != self.chain_id { - // return Err(SignerError::ChainIdMismatch { - // expected: self.chain_id, - // actual: tx_data.chain_id, - // }); - // } - // - // - // // Convert hyperware types to alloy types - // let to_str = tx_data.to.to_string(); - // let to = alloy_primitives::Address::from_str(&to_str) - // .map_err(|e| SignerError::SigningError(format!("Invalid contract address: {}", e)))?; - // - // let mut tx = TxEip1559 { - // chain_id: tx_data.chain_id, - // nonce: tx_data.nonce, - // to: TxKind::Call(to), - // gas_limit: tx_data.gas_limit, - // max_fee_per_gas: tx_data.gas_price, - // max_priority_fee_per_gas: tx_data.max_priority_fee.unwrap_or(tx_data.gas_price / 10), - // input: tx_data.data.clone().unwrap_or_default().into(), - // value: tx_data.value, - // ..Default::default() - // }; - // - // // Sign the transaction with the wallet - // let sig = match self.inner.sign_transaction_sync(&mut tx) { - // Ok(sig) => sig, - // Err(e) => return Err(SignerError::SigningError(e.to_string())), - // }; - // - // // Create signed transaction envelope - // let signed = TxEnvelope::from(tx.into_signed(sig)); - // - // // Encode the transaction - // let mut buf = vec![]; - // signed.encode_2718(&mut buf); - // - // Ok(buf) - //} fn sign_message(&self, message: &[u8]) -> Result, SignerError> { // Create the Ethereum signed message prefixed hash diff --git a/src/wallet.rs b/src/wallet.rs index 014ad40..795dafc 100644 --- a/src/wallet.rs +++ b/src/wallet.rs @@ -22,12 +22,28 @@ use crate::signer::{ SignerError, EncryptedSignerData }; +use crate::hypermap::{ + namehash, + valid_note, + valid_fact, + valid_name, +}; use crate::{hypermap, kiprintln}; use thiserror::Error; -use alloy_primitives::{Address as EthAddress, TxHash, U256}; -use alloy::rpc::types::TransactionReceipt; +use alloy_primitives::{ + Address as EthAddress, + TxHash, + U256, + Bytes +}; +use alloy::rpc::types::{ + TransactionReceipt, + TransactionRequest +}; +use alloy_primitives::TxKind; use std::str::FromStr; +use alloy_sol_types::SolCall; #[derive(Debug, Error)] pub enum WalletError { @@ -60,6 +76,9 @@ pub enum WalletError { #[error("transaction nonce too low")] TransactionNonceTooLow, + + #[error("permission denied: {0}")] + PermissionDenied(String), } /// Represents the storage state of a wallet's private key @@ -177,10 +196,11 @@ pub fn resolve_name(name: &str, _chain_id: u64) -> Result Result<(u128, u128), WalletError> Ok((max_fee, priority_fee)) } -//// Gas calculation for Base network -//fn calculate_base_gas(provider: &Provider) -> Result<(u128, u128), WalletError> { -// // For Base: 33% buffer and 0.5 gwei priority fee -// calculate_eip1559_gas(provider, 3, 500_000_000u128) -//} - // Gas calculation for Optimism network fn calculate_optimism_gas(provider: &Provider) -> Result<(u128, u128), WalletError> { // For Optimism: 25% buffer and 0.3 gwei priority fee @@ -408,23 +422,768 @@ pub fn wait_for_transaction( } } -// Extract error information from RPC errors -fn extract_rpc_error(error: &EthError) -> WalletError { - match error { - EthError::RpcError(value) => { - // Try to parse the error message - if let Some(message) = value.get("message").and_then(|m| m.as_str()) { - if message.contains("insufficient funds") { - return WalletError::InsufficientFunds(message.to_string()); - } else if message.contains("underpriced") { - return WalletError::TransactionUnderpriced; - } else if message.contains("nonce too low") { - return WalletError::TransactionNonceTooLow; - } - // Add more error patterns as needed - } - WalletError::TransactionError(format!("RPC error: {:?}", value)) - }, - _ => WalletError::TransactionError(format!("Ethereum error: {:?}", error)) +//// Extract error information from RPC errors +//fn extract_rpc_error(error: &EthError) -> WalletError { +// match error { +// EthError::RpcError(value) => { +// // Try to parse the error message +// if let Some(message) = value.get("message").and_then(|m| m.as_str()) { +// if message.contains("insufficient funds") { +// return WalletError::InsufficientFunds(message.to_string()); +// } else if message.contains("underpriced") { +// return WalletError::TransactionUnderpriced; +// } else if message.contains("nonce too low") { +// return WalletError::TransactionNonceTooLow; +// } +// // Add more error patterns as needed +// } +// WalletError::TransactionError(format!("RPC error: {:?}", value)) +// }, +// _ => WalletError::TransactionError(format!("Ethereum error: {:?}", error)) +// } +//} + +// ERC-20 Read Function Selectors (_ = NOT CHECKED) +const ERC20_BALANCE_OF: [u8; 4] = [0x70, 0xa0, 0x82, 0x31]; // balanceOf(address) +const ERC20_DECIMALS: [u8; 4] = [0x31, 0x3c, 0xe5, 0x67]; // decimals() +const _ERC20_ALLOWANCE: [u8; 4] = [0xdd, 0x62, 0xed, 0x3e]; // allowance(address,address) +const _ERC20_TOTAL_SUPPLY: [u8; 4] = [0x18, 0x16, 0x0d, 0xdd]; // totalSupply() +const _ERC20_SYMBOL: [u8; 4] = [0x95, 0xd8, 0x9b, 0x41]; // symbol() +const _ERC20_NAME: [u8; 4] = [0x06, 0xfd, 0xde, 0x03]; // name() + +// ERC-20 Write Function Selectors +const _ERC20_TRANSFER: [u8; 4] = [0xa9, 0x05, 0x9c, 0xbb]; // transfer(address,uint256) +const _ERC20_TRANSFER_FROM: [u8; 4] = [0x23, 0xb8, 0x72, 0xdd]; // transferFrom(address,address,uint256) +const _ERC20_APPROVE: [u8; 4] = [0x09, 0x5e, 0xa7, 0xb3]; // approve(address,uint256) + +/// Get the balance of ERC20 tokens for an address +pub fn erc20_balance_of( + token_address: &str, + owner_address: &str, + provider: Provider +) -> Result { + kiprintln!("PROCESS_LIB::erc20_balance_of token_address: {}", token_address); + // Resolve addresses + let token: EthAddress = resolve_name(&token_address, provider.chain_id)?; + kiprintln!("PROCESS_LIB::erc20_balance_of token: {}", token); + let owner = resolve_name(&owner_address, provider.chain_id)?; + kiprintln!("PROCESS_LIB::erc20_balance_of owner: {}", owner); + + // The ERC20 balanceOf function selector: keccak256("balanceOf(address)")[0:4] + //let selector = [0xa9, 0x05, 0x9c, 0xbb]; + let selector = ERC20_BALANCE_OF; + kiprintln!("PROCESS_LIB::erc20_balance_of selector: {:?}", selector); + + // Encode the owner address parameter (padded to 32 bytes) + let mut call_data = Vec::with_capacity(4 + 32); + call_data.extend_from_slice(&selector); + call_data.extend_from_slice(&[0u8; 12]); // 12 bytes of padding + call_data.extend_from_slice(owner.as_slice()); + kiprintln!("PROCESS_LIB::erc20_balance_of call_data: {:?}", call_data); + + // Create the transaction request for eth_call + let tx = TransactionRequest { + to: Some(TxKind::Call(token)), + input: call_data.into(), + ..Default::default() + }; + + kiprintln!("PROCESS_LIB::erc20_balance_of tx: {:#?}", tx); + + // Call the contract + let result = provider.call(tx, None)?; + + kiprintln!("PROCESS_LIB::erc20_balance_of result: {:?}", result); + + // Parse the result (a uint256 value) + if result.len() < 32 { + kiprintln!("PROCESS_LIB::erc20_balance_of Invalid result length"); + return Err(WalletError::TransactionError("Invalid result length".into())); + } + + // Convert the bytes to a U256 + let balance = U256::from_be_bytes::<32>(result[0..32].try_into().unwrap()); + + // TODO: This should be based on the contract's decimals, fix later with a LUT + //Ok(EthAmount { wei_value: balance }) + + let decimals = erc20_decimals(token, &provider)?; + let balance_u128 = balance.to::(); + let balance_float = balance_u128 as f64 / 10f64.powi(decimals as i32); + Ok(balance_float) // Returns balance in full tokens (e.g., 390.159112 USDC) + +} + +pub fn erc20_decimals(token_address: EthAddress, provider: &Provider) -> Result { + kiprintln!("PROCESS_LIB::erc20_decimals token_address: {}", token_address); + let token = token_address; + kiprintln!("PROCESS_LIB::erc20_decimals token: {}", token); + let selector = ERC20_DECIMALS; + let call_data = selector.to_vec(); // ✅ Ensure it's exactly 4 bytes + + kiprintln!("PROCESS_LIB::erc20_decimals selector: {:?}", selector); + + let tx = TransactionRequest { + to: Some(TxKind::Call(token)), + input: call_data.into(), + ..Default::default() + }; + + let result = provider.call(tx, None)?; + kiprintln!("PROCESS_LIB::erc20_decimals result: {:?}", result); + + if result.len() < 32 { + return Err(WalletError::TransactionError("Invalid decimals response".into())); + } + kiprintln!("PROCESS_LIB::erc20_decimals done", ); + + Ok(result[31]) // Decimals are stored in the last byte of the 32-byte response +} + + +///// Transfer ERC20 tokens to another address +//pub fn erc20_transfer( +// token_address: &str, +// to_address: &str, +// amount: EthAmount, +// provider: Provider, +// signer: &S +//) -> Result { +// // Resolve addresses +// let token = resolve_name(token_address, provider.chain_id())?; +// let to = resolve_name(to_address, provider.chain_id())?; +// +// // The ERC20 transfer function selector: keccak256("transfer(address,uint256)")[0:4] +// let selector = [0xa9, 0x05, 0x9c, 0xbb]; +// +// // Encode the parameters: address recipient, uint256 amount +// let mut call_data = Vec::with_capacity(4 + 32 + 32); +// call_data.extend_from_slice(&selector); +// +// // Recipient address (padded to 32 bytes) +// call_data.extend_from_slice(&[0u8; 12]); // 12 bytes of padding +// call_data.extend_from_slice(to.as_slice()); +// +// // Amount (padded to 32 bytes) +// let amount_bytes = amount.as_wei().to_be_bytes::<32>(); +// call_data.extend_from_slice(&amount_bytes); +// +// // Get the current nonce +// let from_address = signer.address(); +// let nonce = provider.get_transaction_count(from_address, None)?.to::(); +// +// // Estimate gas for the token transfer (usually around 60k for ERC20 transfers) +// let tx_req = TransactionRequest { +// from: Some(from_address), +// to: Some(token), +// data: Some(call_data.clone().into()), +// ..Default::default() +// }; +// +// let gas_limit = provider.estimate_gas(tx_req, None)? +// .to::() +// .saturating_mul(12).saturating_div(10); // Add 20% buffer +// +// // Calculate gas price based on the chain +// let (gas_price, priority_fee) = match signer.chain_id() { +// // Use your existing gas calculation functions +// 1 => calculate_eth_mainnet_gas(&provider)?, +// 8453 => calculate_base_gas(&provider)?, +// _ => { +// let base_fee = provider.get_gas_price()?.to::(); +// let adjusted_fee = (base_fee * 130) / 100; +// (adjusted_fee, adjusted_fee / 10) +// } +// }; +// +// // Create transaction data +// let tx_data = TransactionData { +// to: token, +// value: U256::ZERO, // No ETH sent with token transfers +// data: Some(call_data), +// nonce, +// gas_limit, +// gas_price, +// max_priority_fee: Some(priority_fee), +// chain_id: signer.chain_id(), +// }; +// +// // Sign and send transaction +// let signed_tx = signer.sign_transaction(&tx_data)?; +// let tx_hash = provider.send_raw_transaction(signed_tx.into())?; +// +// Ok(TxReceipt { +// hash: tx_hash, +// details: format!("Sent {} tokens to {}", amount.to_string(), to_address), +// }) +//} + +// THE HYPERMAP stuff + +/// Result type for Hypermap transactions +#[derive(Debug, Clone)] +pub struct HypermapTxReceipt { + /// Transaction hash + pub hash: TxHash, + /// Description of the operation + pub description: String, +} + +/// Create a note (mutable data) on a Hypermap namespace entry +/// +/// # Parameters +/// - `parent_entry`: The namespace entry (e.g. "mynode.hypr") where the note will be created +/// - `note_key`: The note key to create (must start with '~') +/// - `data`: The data to store in the note +/// - `provider`: The Ethereum provider to use +/// - `signer`: The signer to use for signing the transaction +/// +/// # Returns +/// A result containing a HypermapTxReceipt or a WalletError +pub fn create_note( + parent_entry: &str, + note_key: &str, + data: Vec, + provider: Provider, + signer: &S, +) -> Result { + // Verify the note key is valid + if !valid_note(note_key) { + return Err(WalletError::NameResolutionError( + format!("Invalid note key '{}'. Must start with '~' and contain only lowercase letters, numbers, and hyphens", note_key) + )); + } + + // Get the parent TBA address + let hypermap = provider.hypermap(); + let parent_hash_str = namehash(parent_entry); + + println!("Parent entry: {}", parent_entry); + println!("Parent hash: {}", parent_hash_str); + + let (tba, owner, _) = hypermap.get_hash(&parent_hash_str)?; + + println!("TBA address (parent): {}", tba); + println!("Owner address: {}", owner); + + // Check that the signer is the owner of the parent entry + let signer_address = signer.address(); + println!("Signer address: {}", signer_address); + + if signer_address != owner { + return Err(WalletError::PermissionDenied( + format!("Signer address {} does not own the parent entry {}", signer_address, parent_entry) + )); + } + + // Get the hypermap contract address + let hypermap_address = *hypermap.address(); + println!("Hypermap contract address: {}", hypermap_address); + + // Create the note function call data + let note_function = hypermap::contract::noteCall { + note: Bytes::from(note_key.as_bytes().to_vec()), + data: Bytes::from(data), + }; + let note_call_data = note_function.abi_encode(); + + // ?? Bytes::from(note_call_data) or note_call_data? + // Now create an ERC-6551 execute call to send from the wallet to the TBA + let execute_call_data = create_execute_calldata( + hypermap_address, + U256::ZERO, + Bytes::from(note_call_data), + 0 // CALL operation + ); + + // Send the transaction from the wallet to the TBA + let (tx_hash, tx_data) = send_transaction( + tba, + execute_call_data, + U256::ZERO, + provider, + signer + )?; + + // Return the receipt with transaction details + Ok(HypermapTxReceipt { + hash: tx_hash, + description: format!("Created note '{}' on '{}'", note_key, parent_entry), + }) +} + +/// Helper function to create calldata for the TBA's execute function +/// +/// The ERC-6551 execute function has this signature: +/// function execute(address to, uint256 value, bytes calldata data, uint8 operation) +/// +/// Parameters: +/// - to: The target contract to call (the Hypermap contract) +/// - value: Amount of ETH to send (usually 0) +/// - data: The calldata for the target function +/// - operation: The type of operation (0 = CALL, 1 = DELEGATECALL, etc.) +fn create_execute_calldata( + to: EthAddress, + value: U256, + data: Bytes, + operation: u8 +) -> Bytes { + // Function selector for execute(address,uint256,bytes,uint8) + // keccak256("execute(address,uint256,bytes,uint8)")[0:4] + let selector = [0x44, 0xc0, 0x28, 0xfe]; + + // Encode to address (padded to 32 bytes) + let mut to_bytes = vec![0u8; 32]; + to_bytes[12..32].copy_from_slice(to.as_slice()); + + // Encode value (uint256) + let value_bytes = value.to_be_bytes::<32>(); + + // Calculate offset for the dynamic bytes data + // This is the offset in 32-byte words to where the bytes data starts + // 3 fixed params (address, uint256, uint256 offset) + 1 more fixed param after = 4 * 32 = 128 bytes + let offset = U256::from(128); + let offset_bytes = offset.to_be_bytes::<32>(); + + // Encode operation (padded to 32 bytes) + let mut operation_bytes = vec![0u8; 32]; + operation_bytes[31] = operation; + + // Encode bytes length + let data_len = U256::from(data.len()); + let data_len_bytes = data_len.to_be_bytes::<32>(); + + // Encode bytes data (with padding to 32-byte boundary) + let mut padded_data = data.to_vec(); + if padded_data.len() % 32 != 0 { + let padding = vec![0u8; 32 - (padded_data.len() % 32)]; + padded_data.extend_from_slice(&padding); } -} \ No newline at end of file + + // Combine everything into final calldata + let mut result = Vec::new(); + result.extend_from_slice(&selector); // Function selector (4 bytes) + result.extend_from_slice(&to_bytes); // To address (32 bytes) + result.extend_from_slice(&value_bytes); // Value (32 bytes) + result.extend_from_slice(&offset_bytes); // Data offset (32 bytes) + result.extend_from_slice(&operation_bytes); // Operation (32 bytes) + result.extend_from_slice(&data_len_bytes); // Data length (32 bytes) + result.extend_from_slice(&padded_data); // Data (padded) + + Bytes::from(result) +} + +/// Send a transaction to the token-bound account +fn send_transaction( + to: EthAddress, + data: Bytes, + value: U256, + provider: Provider, + signer: &S, +) -> Result<(TxHash, Vec), WalletError> { + let chain_id = signer.chain_id(); + + kiprintln!("PROCESS_LIB::send_transaction starting"); + kiprintln!("PROCESS_LIB::send_transaction chain_id: {}", chain_id); + + // Get gas estimates - use 50% buffer for Base to ensure acceptance + let base_fee = provider.get_gas_price()?.to::(); + let gas_price = (base_fee * 150) / 100; // 50% buffer + let priority_fee = gas_price / 5; // 20% of gas price + + kiprintln!("PROCESS_LIB::send_transaction base_fee: {}, priority_fee: {}", base_fee, priority_fee); + kiprintln!("PROCESS_LIB::send_transaction gas_price: {}", gas_price); + + // Get the current nonce for the signer's address + let from_address = signer.address(); + let nonce = provider.get_transaction_count(from_address, None)? + .to::(); + + kiprintln!("PROCESS_LIB::send_transaction nonce: {}", nonce); + + // For ERC-6551 account operations, use a higher gas limit + // The ERC-6551 execute function is complex and gas-intensive + let estimated_gas = 500_000; // Start high for ERC-6551 + + // Add 50% buffer to estimated gas since this is a complex operation + let gas_limit = (estimated_gas * 150) / 100; + + kiprintln!("PROCESS_LIB::send_transaction estimated_gas: {}", estimated_gas); + kiprintln!("PROCESS_LIB::send_transaction gas_limit: {}", gas_limit); + + // Prepare transaction data + let tx_data = TransactionData { + to, + value, + data: Some(data.to_vec()), + nonce, + gas_limit, + gas_price, + max_priority_fee: Some(priority_fee), + chain_id, + }; + + // Sign the transaction + let signed_tx = signer.sign_transaction(&tx_data)?; + kiprintln!("PROCESS_LIB::send_transaction signed"); + + // Send the transaction + let tx_hash = provider.send_raw_transaction(signed_tx.clone().into())?; + kiprintln!("PROCESS_LIB::send_transaction tx_hash: {}", tx_hash); + + // Return both the hash and the raw transaction data + Ok((tx_hash, signed_tx)) +} + +///// A simple test function to create a note +//pub fn test_create_note( +// parent_entry: &str, // e.g., "lazybonesitis.os" +// provider: Provider, +// signer: &S, +//) -> Result<(), WalletError> { +// println!("=== TESTING NOTE CREATION ==="); +// println!("Parent entry: {}", parent_entry); +// +// // Simple test note +// let note_key = "~test-note"; +// let data = "This is a test note created at ".to_string() + &chrono::Utc::now().to_rfc3339(); +// +// println!("Creating note: {}", note_key); +// println!("Data: {}", data); +// +// match create_note( +// parent_entry, +// note_key, +// data.as_bytes().to_vec(), +// provider, +// signer +// ) { +// Ok(receipt) => { +// println!("Success! Transaction hash: {}", receipt.hash); +// println!("Description: {}", receipt.description); +// Ok(()) +// }, +// Err(e) => { +// println!("Error creating note: {:?}", e); +// Err(e) +// } +// } +//} + +///// Create a note (mutable data) on a Hypermap namespace entry +///// +///// # Parameters +///// - `parent_entry`: The namespace entry (e.g. "mynode.hypr") where the note will be created +///// - `note_key`: The note key to create (must start with '~') +///// - `data`: The data to store in the note +///// - `provider`: The Ethereum provider to use +///// - `signer`: The signer to use for signing the transaction +///// +///// # Returns +///// A result containing a HypermapTxReceipt or a WalletError +//pub fn create_note( +// parent_entry: &str, +// note_key: &str, +// data: Vec, +// provider: Provider, +// signer: &S, +//) -> Result { +// // Verify the note key is valid +// if !valid_note(note_key) { +// return Err(WalletError::NameResolutionError( +// format!("Invalid note key '{}'. Must start with '~' and contain only lowercase letters, numbers, and hyphens", note_key) +// )); +// } +// +// // Get the parent TBA address +// let hypermap = provider.hypermap(); +// let parent_hash_str = namehash(parent_entry); +// let (tba, owner, _) = hypermap.get_hash(&parent_hash_str)?; +// +// // Check that the signer is the owner of the parent entry +// let signer_address = signer.address(); +// if signer_address != owner { +// return Err(WalletError::PermissionDenied( +// format!("Signer address {} does not own the parent entry {}", signer_address, parent_entry) +// )); +// } +// +// // Create the note call data +// let note_function = hypermap::contract::noteCall { +// note: Bytes::from(note_key.as_bytes().to_vec()), +// data: Bytes::from(data), +// }; +// let call_data = note_function.abi_encode(); +// +// // Prepare and send the transaction +// send_tba_transaction( +// tba, +// call_data.into(), +// U256::ZERO, // No ETH value to send +// provider, +// signer, +// format!("Created note '{}' on '{}'", note_key, parent_entry), +// ) +//} + +/// Create a fact (immutable data) on a Hypermap namespace entry +/// +/// # Parameters +/// - `parent_entry`: The namespace entry (e.g. "mynode.hypr") where the fact will be created +/// - `fact_key`: The fact key to create (must start with '!') +/// - `data`: The data to store in the fact +/// - `provider`: The Ethereum provider to use +/// - `signer`: The signer to use for signing the transaction +/// +/// # Returns +/// A result containing a HypermapTxReceipt or a WalletError +pub fn create_fact( + parent_entry: &str, + fact_key: &str, + data: Vec, + provider: Provider, + signer: &S, +) -> Result { + // Verify the fact key is valid + if !valid_fact(fact_key) { + return Err(WalletError::NameResolutionError( + format!("Invalid fact key '{}'. Must start with '!' and contain only lowercase letters, numbers, and hyphens", fact_key) + )); + } + + // Get the parent TBA address + let hypermap = provider.hypermap(); + let parent_hash_str = namehash(parent_entry); + let (tba, owner, _) = hypermap.get_hash(&parent_hash_str)?; + + // Check that the signer is the owner of the parent entry + let signer_address = signer.address(); + if signer_address != owner { + return Err(WalletError::PermissionDenied( + format!("Signer address {} does not own the parent entry {}", signer_address, parent_entry) + )); + } + + // Create the fact call data + let fact_function = hypermap::contract::factCall { + fact: Bytes::from(fact_key.as_bytes().to_vec()), + data: Bytes::from(data), + }; + let call_data = fact_function.abi_encode(); + + // Prepare and send the transaction + send_tba_transaction( + tba, + call_data.into(), + U256::ZERO, // No ETH value to send + provider, + signer, + format!("Created fact '{}' on '{}'", fact_key, parent_entry), + ) +} + +/// Mint a new namespace entry under a parent entry +/// +/// # Parameters +/// - `parent_entry`: The parent namespace entry (e.g. "mynode.hypr") +/// - `label`: The new label to mint (without prefix or parent) +/// - `recipient`: The address that will own the new entry +/// - `implementation`: The address of the token-bound account implementation +/// - `provider`: The Ethereum provider to use +/// - `signer`: The signer to use for signing the transaction +/// +/// # Returns +/// A result containing a HypermapTxReceipt or a WalletError +pub fn mint_entry( + parent_entry: &str, + label: &str, + recipient: &str, + implementation: &str, + provider: Provider, + signer: &S, +) -> Result { + // Verify the label is valid + if !valid_name(label) { + return Err(WalletError::NameResolutionError( + format!("Invalid label '{}'. Must contain only lowercase letters, numbers, and hyphens", label) + )); + } + + // Get the parent TBA address + let hypermap = provider.hypermap(); + let parent_hash_str = namehash(parent_entry); + kiprintln!("PROCESS_LIB::mint_entry parent_hash_str: {}", parent_hash_str); + let (tba, owner, _) = hypermap.get_hash(&parent_hash_str)?; + + // Check that the signer is the owner of the parent entry + let signer_address = signer.address(); + if signer_address != owner { + return Err(WalletError::PermissionDenied( + format!("Signer address {} does not own the parent entry {}", signer_address, parent_entry) + )); + } + + // Resolve recipient address + let recipient_address = resolve_name(recipient, provider.chain_id)?; + + // Resolve implementation address + let implementation_address = resolve_name(implementation, provider.chain_id)?; + + // Create the mint call data + let mint_function = hypermap::contract::mintCall { + who: recipient_address, + label: Bytes::from(label.as_bytes().to_vec()), + initialization: Bytes::default(), // No initialization data + erc721Data: Bytes::default(), // No ERC721 data + implementation: implementation_address, + }; + let call_data = mint_function.abi_encode(); + + kiprintln!("Parent entry: {}", parent_entry); + kiprintln!("Parent hash: {}", parent_hash_str); + kiprintln!("TBA address: {}", tba); + kiprintln!("Owner address: {}", owner); + kiprintln!("Signer address: {}", signer_address); + + // Prepare and send the transaction + send_tba_transaction( + tba, + call_data.into(), + U256::ZERO, // No ETH value to send + provider, + signer, + format!("Minted new entry '{}' under '{}'", label, parent_entry), + ) +} + +/// Set the gene for a namespace entry +/// +/// # Parameters +/// - `entry`: The namespace entry (e.g. "mynode.hypr") to set the gene for +/// - `gene_implementation`: The address of the token-bound account implementation to use as the gene +/// - `provider`: The Ethereum provider to use +/// - `signer`: The signer to use for signing the transaction +/// +/// # Returns +/// A result containing a HypermapTxReceipt or a WalletError +pub fn set_gene( + entry: &str, + gene_implementation: &str, + provider: Provider, + signer: &S, +) -> Result { + // Get the entry's TBA address + let hypermap = provider.hypermap(); + let entry_hash_str = namehash(entry); + let (tba, owner, _) = hypermap.get_hash(&entry_hash_str)?; + + // Check that the signer is the owner of the entry + let signer_address = signer.address(); + if signer_address != owner { + return Err(WalletError::PermissionDenied( + format!("Signer address {} does not own the entry {}", signer_address, entry) + )); + } + + // Resolve gene implementation address + let gene_address = resolve_name(gene_implementation, provider.chain_id)?; + + // Create the gene call data + let gene_function = hypermap::contract::geneCall { + _gene: gene_address, + }; + let call_data = gene_function.abi_encode(); + + // Prepare and send the transaction + send_tba_transaction( + tba, + call_data.into(), + U256::ZERO, // No ETH value to send + provider, + signer, + format!("Set gene for '{}' to '{}'", entry, gene_implementation), + ) +} + +/// Helper function to send a transaction to a token-bound account +fn send_tba_transaction( + tba: EthAddress, + data: Bytes, + value: U256, + provider: Provider, + signer: &S, + description: String, +) -> Result { + kiprintln!("PROCESS_LIB::send_tba_transaction starting"); + + let chain_id = signer.chain_id(); + kiprintln!("PROCESS_LIB::send_tba_transaction chain_id: {}", chain_id); + + // TODO: change from hardcoded base to dynamic + let (base_fee, priority_fee) = calculate_base_gas(&provider)?; + kiprintln!("PROCESS_LIB::send_tba_transaction base_fee: {}, priority_fee: {}", base_fee, priority_fee); + + let gas_price = (base_fee * 180) / 100; + kiprintln!("PROCESS_LIB::send_tba_transaction gas_price: {}", gas_price); + + // Get the current nonce for the signer's address + let from_address = signer.address(); + let nonce = provider.get_transaction_count(from_address, None)? + .to::(); + kiprintln!("PROCESS_LIB::send_tba_transaction nonce: {}", nonce); + + // Estimate gas limit for the transaction + let tx_req = TransactionRequest::default() + .from(from_address) + .to(tba) + .input(data.clone().into()) + .value(value); + + let estimated_gas = match provider.estimate_gas(tx_req, None) { + Ok(gas) => gas.to::(), + Err(_) => { + // If estimation fails, use a conservative gas limit + // This might happen for new contracts or complex interactions + 300_000 + } + }; + kiprintln!("PROCESS_LIB::send_tba_transaction estimated_gas: {}", estimated_gas); + + // Add 120% buffer to estimated gas + let gas_limit = (estimated_gas * 220) / 100; + kiprintln!("PROCESS_LIB::send_tba_transaction gas_limit: {}", gas_limit); + + // Prepare transaction data + let tx_data = TransactionData { + to: tba, + value, + data: Some(data.to_vec()), + nonce, + gas_limit, + gas_price, + max_priority_fee: Some(priority_fee), + chain_id, + }; + //kiprintln!("PROCESS_LIB::send_tba_transaction tx_data: {:#?}", tx_data); + + // Sign the transaction + let signed_tx = signer.sign_transaction(&tx_data)?; + kiprintln!("PROCESS_LIB::send_tba_transaction signed"); + + // Send the transaction + let tx_hash = provider.send_raw_transaction(signed_tx.into())?; + kiprintln!("PROCESS_LIB::send_tba_transaction tx_hash: {}", tx_hash); + + // Return the receipt with transaction details + Ok(HypermapTxReceipt { + hash: tx_hash, + description, + }) +} + + + + + + + From e4fd3e6b5e83d40d8e7c42e4f64ac489a373278e Mon Sep 17 00:00:00 2001 From: Hallmane Date: Tue, 1 Apr 2025 14:20:20 +0200 Subject: [PATCH 05/96] er20_X works --- src/wallet.rs | 4795 +++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 4008 insertions(+), 787 deletions(-) diff --git a/src/wallet.rs b/src/wallet.rs index 795dafc..04068b7 100644 --- a/src/wallet.rs +++ b/src/wallet.rs @@ -1,15 +1,3 @@ -//! Ethereum wallet functionality for Hyperware. -//! -//! This module provides higher-level wallet functionality, building on top of -//! the cryptographic operations in the signer module. It handles transaction -//! construction, name resolution, and account management. -//! -//! wallet module: -//! 1. Provides convenient transaction creation and submission -//! 2. Handles Hypermap name resolution -//! 3. Manages account state and balances -//! 4. Offers a simpler interface for common ETH operations (more to do here) - use crate::eth::{ Provider, EthError, @@ -28,7 +16,10 @@ use crate::hypermap::{ valid_fact, valid_name, }; -use crate::{hypermap, kiprintln}; +use crate::hypermap; +use crate::{ + kiprintln +}; use thiserror::Error; use alloy_primitives::{ @@ -43,7 +34,46 @@ use alloy::rpc::types::{ }; use alloy_primitives::TxKind; use std::str::FromStr; -use alloy_sol_types::SolCall; +use alloy_sol_types::{sol, SolCall}; + +// Define token standards using sol! macro +sol! { + interface IERC20 { + function balanceOf(address who) external view returns (uint256); + function transfer(address to, uint256 value) external returns (bool); + function approve(address spender, uint256 value) external returns (bool); + function transferFrom(address from, address to, uint256 value) external returns (bool); + function allowance(address owner, address spender) external view returns (uint256); + function totalSupply() external view returns (uint256); + function decimals() external view returns (uint8); + function symbol() external view returns (string); + function name() external view returns (string); + } + + interface IERC721 { + function balanceOf(address owner) external view returns (uint256); + function ownerOf(uint256 tokenId) external view returns (address); + function safeTransferFrom(address from, address to, uint256 tokenId) external; + function transferFrom(address from, address to, uint256 tokenId) external; + function approve(address to, uint256 tokenId) external; + function setApprovalForAll(address operator, bool approved) external; + function getApproved(uint256 tokenId) external view returns (address); + function isApprovedForAll(address owner, address operator) external view returns (bool); + } + + interface IERC1155 { + function balanceOf(address account, uint256 id) external view returns (uint256); + function balanceOfBatch(address[] accounts, uint256[] ids) external view returns (uint256[]); + function setApprovalForAll(address operator, bool approved) external; + function isApprovedForAll(address account, address operator) external view returns (bool); + function safeTransferFrom(address from, address to, uint256 id, uint256 amount, bytes data) external; + function safeBatchTransferFrom(address from, address to, uint256[] ids, uint256[] amounts, bytes data) external; + } + + interface IERC6551Account { + function execute(address to, uint256 value, bytes calldata data, uint8 operation) external returns (bytes); + } +} #[derive(Debug, Error)] pub enum WalletError { @@ -187,192 +217,319 @@ pub struct TxReceipt { pub details: String, } -// The checks here aren't solid, but it works for now. Will also expand with full hypermap support -/// Resolve a .hypr name to an Ethereum address using Hypermap. -pub fn resolve_name(name: &str, _chain_id: u64) -> Result { - // If it's already an address, just parse it - if name.starts_with("0x") && name.len() == 42 { - return EthAddress::from_str(name) - .map_err(|_| WalletError::NameResolutionError(format!("Invalid address format: {}", name))); - } - - // hardcoded to .hypr for now - let formatted_name = if !name.contains('.') { - format!("{}.hypr", name) - } else { - kiprintln!("PROCESS_LIB::resolve_name name: {}", name); - name.to_string() - }; - - // Use hypermap resolution - let hypermap = hypermap::Hypermap::default(60); - match hypermap.get(&formatted_name) { - Ok((_tba, owner, _)) => { - Ok(owner) - }, - Err(e) => { - Err(WalletError::NameResolutionError( - format!("Failed to resolve name '{}': {}", name, e) - )) - } - } +/// Result type for Hypermap transactions +#[derive(Debug, Clone)] +pub struct HypermapTxReceipt { + /// Transaction hash + pub hash: TxHash, + /// Description of the operation + pub description: String, } -/// Send ETH to an address or name -pub fn send_eth( - to: &str, - amount: EthAmount, - provider: Provider, - signer: &S, -) -> Result { - - kiprintln!("PROCESS_LIB::send_eth provider: {:#?}", provider); +// +// HELPER FUNCTIONS +// - // Current chain-specific handling - let chain_id = signer.chain_id(); - kiprintln!("PROCESS_LIB::send_eth chain_id: {}", chain_id); +/// Helper for making contract view function calls +fn call_view_function( + contract: EthAddress, + call: T, + provider: &Provider +) -> Result { + let call_data = call.abi_encode(); + let tx = TransactionRequest { + to: Some(TxKind::Call(contract)), + input: call_data.into(), + ..Default::default() + }; - // This part needs improvement - detect network type more robustly - let is_test_network = chain_id == 31337 || chain_id == 1337; + let result = provider.call(tx, None)?; + + if result.is_empty() { + return Err(WalletError::TransactionError("Empty result from call".into())); + } - // Use network-specific gas strategies - let (gas_price, priority_fee) = match chain_id { + match T::abi_decode_returns(&result, true) { + Ok(decoded) => Ok(decoded), + Err(e) => Err(WalletError::TransactionError( + format!("Failed to decode result: {}", e) + )) + } +} - // just rough calculations for now - 1 => calculate_eth_mainnet_gas(&provider)?, // mainnet - 8453 => calculate_base_gas(&provider)?, // Base - 10 => calculate_optimism_gas(&provider)?, // Optimism +/// Calculate gas parameters based on network type +fn calculate_gas_params(provider: &Provider, chain_id: u64) -> Result<(u128, u128), WalletError> { + kiprintln!("PL:: Calculating gas parameters for chain ID: {}", chain_id); - // Test networks - keep your current approach - _ if is_test_network => (2_000_000_000, 100_000_000), - - // 30% - _ => { - kiprintln!("PROCESS_LIB::send_eth getting gas price"); + match chain_id { + 1 => { // Mainnet: 50% buffer and 1.5 gwei priority fee + let latest_block = provider.get_block_by_number(BlockNumberOrTag::Latest, false)? + .ok_or_else(|| WalletError::TransactionError("Failed to get latest block".into()))?; + + let base_fee = latest_block.header.inner.base_fee_per_gas + .ok_or_else(|| WalletError::TransactionError("No base fee in block".into()))? + as u128; + + Ok((base_fee + (base_fee / 2), 1_500_000_000u128)) + }, + 8453 => { // Base + kiprintln!("PL:: Calculating gas parameters for Base"); + let latest_block = provider.get_block_by_number(BlockNumberOrTag::Latest, false)? + .ok_or_else(|| WalletError::TransactionError("Failed to get latest block".into()))?; + + kiprintln!("PL:: Got latest block"); + let base_fee = latest_block.header.inner.base_fee_per_gas + .ok_or_else(|| WalletError::TransactionError("No base fee in block".into()))? + as u128; + + kiprintln!("PL:: Got base fee: {}", base_fee); + + let max_fee = base_fee + (base_fee / 3); + kiprintln!("PL:: max fee: {}", max_fee); + + let min_priority_fee = 100_000u128; + kiprintln!("PL:: min priority fee: {}", min_priority_fee); + + let max_priority_fee = max_fee / 2; + kiprintln!("PL:: max priority fee: {}", max_priority_fee); + + let priority_fee = std::cmp::max(min_priority_fee, std::cmp::min(base_fee / 10, max_priority_fee)); + kiprintln!("PL:: priority fee: {}", priority_fee); + + Ok((max_fee, priority_fee)) + }, + 10 => { // Optimism: 25% buffer and 0.3 gwei priority fee + let latest_block = provider.get_block_by_number(BlockNumberOrTag::Latest, false)? + .ok_or_else(|| WalletError::TransactionError("Failed to get latest block".into()))?; + + let base_fee = latest_block.header.inner.base_fee_per_gas + .ok_or_else(|| WalletError::TransactionError("No base fee in block".into()))? + as u128; + + Ok((base_fee + (base_fee / 4), 300_000_000u128)) + }, + 31337 | 1337 => { // Test networks + Ok((2_000_000_000, 100_000_000)) + }, + _ => { // Default: 30% buffer let base_fee = provider.get_gas_price()?.to::(); - kiprintln!("PROCESS_LIB::send_eth base_fee: {}", base_fee); let adjusted_fee = (base_fee * 130) / 100; - kiprintln!("PROCESS_LIB::send_eth adjusted_fee: {}", adjusted_fee); - (adjusted_fee, adjusted_fee / 10) + Ok((adjusted_fee, adjusted_fee / 10)) } - }; - - kiprintln!("PROCESS_LIB::send_eth gas_price: {}", gas_price); - - // Resolve the name to an address - let to_address = resolve_name(to, chain_id)?; + } +} - kiprintln!("PROCESS_LIB::send_eth to_address: {}", to_address); +/// Prepare and send a transaction with common parameters +fn prepare_and_send_tx( + to: EthAddress, + call_data: Vec, + value: U256, + provider: &Provider, + signer: &S, + gas_limit: Option, + format_receipt: F +) -> Result +where F: FnOnce(TxHash) -> String { + kiprintln!("PL:: Preparing transaction..."); // Get the current nonce for the signer's address - let from_address = signer.address(); - let nonce = provider.get_transaction_count(from_address, None)? + let signer_address = signer.address(); + let nonce = provider.get_transaction_count(signer_address, None)? .to::(); - - kiprintln!("PROCESS_LIB::send_eth nonce: {}", nonce); - - // Standard gas limit for ETH transfer - let gas_limit = 21000; + + kiprintln!("PL:: Got nonce: {}", nonce); + + // Calculate gas parameters based on chain ID + let (gas_price, priority_fee) = calculate_gas_params(provider, signer.chain_id())?; + + kiprintln!("PL:: Calculated gas params - price: {}, priority fee: {}", gas_price, priority_fee); + + // Use provided gas limit or estimate it with 20% buffer + let gas_limit = match gas_limit { + Some(limit) => { + kiprintln!("PL:: Using provided gas limit: {}", limit); + limit + }, + None => { + kiprintln!("PL:: Estimating gas limit..."); + let tx_req = TransactionRequest { + from: Some(signer_address), + to: Some(TxKind::Call(to)), + input: call_data.clone().into(), + ..Default::default() + }; + + match provider.estimate_gas(tx_req, None) { + Ok(gas) => { + let limit = (gas.to::() * 120) / 100; // Add 20% buffer + kiprintln!("PL:: Estimated gas limit with buffer: {}", limit); + limit + }, + Err(_) => { + kiprintln!("PL:: Gas estimation failed, using default: 100,000"); + 100_000 // Default value if estimation fails + } + } + } + }; // Prepare transaction data let tx_data = TransactionData { - to: to_address, - value: amount.as_wei(), - data: None, // No data for simple ETH transfer + to, + value, + data: Some(call_data), nonce, gas_limit, gas_price, max_priority_fee: Some(priority_fee), - chain_id, + chain_id: signer.chain_id(), }; - - kiprintln!("PROCESS_LIB::send_eth tx_data: {:#?}", tx_data); - // Sign the transaction + kiprintln!("PL:: Signing transaction..."); + + // Sign and send transaction let signed_tx = signer.sign_transaction(&tx_data)?; - - kiprintln!("PROCESS_LIB::send_eth signed_tx: {:?}", signed_tx); - // Send the transaction + kiprintln!("PL:: Sending transaction..."); let tx_hash = provider.send_raw_transaction(signed_tx.into())?; - - kiprintln!("lol PROCESS_LIB::send_eth tx_hash: {}", tx_hash); - // Return the receipt with transaction details + kiprintln!("PL:: Transaction sent with hash: {}", tx_hash); + + // Return the receipt with formatted details Ok(TxReceipt { hash: tx_hash, - details: format!("Sent {} to {}", amount.to_string(), to), + details: format_receipt(tx_hash), }) } -// Helper function to calculate EIP-1559 gas parameters with network-specific values -fn calculate_eip1559_gas( - provider: &Provider, - buffer_fraction: u128, - priority_fee: u128 -) -> Result<(u128, u128), WalletError> { - kiprintln!("PROCESS_LIB::calculate_eip1559_gas provider\n", ); - // Get latest block - let latest_block = provider.get_block_by_number(BlockNumberOrTag::Latest, false)? - .ok_or_else(|| WalletError::TransactionError("Failed to get latest block".into()))?; - - kiprintln!("PROCESS_LIB::calculate_eip1559_gas latest_block: {:#?}", latest_block); +/// Helper for creating Hypermap transaction operations +fn create_hypermap_tx( + parent_entry: &str, + hypermap_call_data: Bytes, + description_fn: F, + provider: Provider, + signer: &S +) -> Result +where F: FnOnce() -> String { + // Get the parent TBA address and verify ownership + let hypermap = provider.hypermap(); + let parent_hash_str = namehash(parent_entry); + let (tba, owner, _) = hypermap.get_hash(&parent_hash_str)?; - // Get base fee - let base_fee = latest_block.header.inner.base_fee_per_gas - .ok_or_else(|| WalletError::TransactionError("No base fee in block".into()))? - as u128; - - kiprintln!("PROCESS_LIB::calculate_eip1559_gas base_fee: {}", base_fee); + // Check that the signer is the owner of the parent entry + let signer_address = signer.address(); + if signer_address != owner { + return Err(WalletError::PermissionDenied( + format!("Signer address {} does not own the entry {}", signer_address, parent_entry) + )); + } - // Calculate max fee with the provided buffer fraction - let max_fee = base_fee + (base_fee / buffer_fraction); - - kiprintln!("PROCESS_LIB::calculate_eip1559_gas max_fee: {}", max_fee); + // Create the ERC-6551 execute call + let execute_call = IERC6551Account::executeCall { + to: *hypermap.address(), + value: U256::ZERO, + data: hypermap_call_data, + operation: 0, // CALL operation + }; + + // Format receipt message + let description = description_fn(); + let format_receipt = move |_| description.clone(); + + // For ERC-6551 operations we need a higher gas limit + let gas_limit = Some(600_000); + + // Send the transaction + let receipt = prepare_and_send_tx( + tba, + execute_call.abi_encode(), + U256::ZERO, + &provider, + signer, + gas_limit, + format_receipt + )?; - Ok((max_fee, priority_fee)) + // Convert to Hypermap receipt + Ok(HypermapTxReceipt { + hash: receipt.hash, + description: receipt.details, + }) } -// Network-specific gas calculation for Ethereum mainnet -fn calculate_eth_mainnet_gas(provider: &Provider) -> Result<(u128, u128), WalletError> { - // For mainnet: 50% buffer and 1.5 gwei priority fee - calculate_eip1559_gas(provider, 2, 1_500_000_000u128) -} +// +// NAME RESOLUTION +// -fn calculate_base_gas(provider: &Provider) -> Result<(u128, u128), WalletError> { - // Get the latest block to determine current gas conditions - let latest_block = provider.get_block_by_number(BlockNumberOrTag::Latest, false)? - .ok_or_else(|| WalletError::TransactionError("Failed to get latest block".into()))?; +// Resolve a .hypr name to an Ethereum address using Hypermap +pub fn resolve_name(name: &str, chain_id: u64) -> Result { + // If it's already an address, just parse it + if name.starts_with("0x") && name.len() == 42 { + return EthAddress::from_str(name) + .map_err(|_| WalletError::NameResolutionError(format!("Invalid address format: {}", name))); + } + + // hardcoded to .hypr for now + let formatted_name = if !name.contains('.') { + format!("{}.hypr", name) + } else { + name.to_string() + }; - // Get base fee from the block - let base_fee = latest_block.header.inner.base_fee_per_gas - .ok_or_else(|| WalletError::TransactionError("No base fee in block".into()))? - as u128; + // Use hypermap resolution + let hypermap = hypermap::Hypermap::default(chain_id); + match hypermap.get(&formatted_name) { + Ok((_tba, owner, _)) => { + Ok(owner) + }, + Err(e) => { + Err(WalletError::NameResolutionError( + format!("Failed to resolve name '{}': {}", name, e) + )) + } + } +} + +// +// ETHEREUM FUNCTIONS +// + +/// Send ETH to an address or name +pub fn send_eth( + to: &str, + amount: EthAmount, + provider: Provider, + signer: &S, +) -> Result { + // Resolve the name to an address + let to_address = resolve_name(to, signer.chain_id())?; - // Calculate max fee with a 33% buffer - let max_fee = base_fee + (base_fee / 3); + // Standard gas limit for ETH transfer is always 21000 + let gas_limit = Some(21000); - // Dynamic priority fee - 10% of base fee, but with a minimum and a maximum - // Low minimum for Base which has very low gas prices - let min_priority_fee = 100_000u128; // 0.0001 gwei minimum - let max_priority_fee = max_fee / 2; // Never more than half the max fee + // Format receipt message + let amount_str = amount.to_string(); + let format_receipt = move |_tx_hash| { + format!("Sent {} to {}", amount_str, to) + }; - let priority_fee = std::cmp::max( - min_priority_fee, - std::cmp::min(base_fee / 10, max_priority_fee) - ); + // For ETH transfers, we have no call data + let empty_call_data = vec![]; - Ok((max_fee, priority_fee)) -} - -// Gas calculation for Optimism network -fn calculate_optimism_gas(provider: &Provider) -> Result<(u128, u128), WalletError> { - // For Optimism: 25% buffer and 0.3 gwei priority fee - calculate_eip1559_gas(provider, 4, 300_000_000u128) + // Use the helper function to prepare and send the transaction + prepare_and_send_tx( + to_address, + empty_call_data, + amount.as_wei(), + &provider, + signer, + gas_limit, + format_receipt + ) } /// Get the ETH balance for an address or name -pub fn get_balance( +pub fn get_eth_balance( address_or_name: &str, chain_id: u64, provider: Provider, @@ -389,6 +546,7 @@ pub fn get_balance( }) } +/// Wait for a transaction to be confirmed pub fn wait_for_transaction( tx_hash: TxHash, provider: Provider, @@ -422,578 +580,595 @@ pub fn wait_for_transaction( } } -//// Extract error information from RPC errors -//fn extract_rpc_error(error: &EthError) -> WalletError { -// match error { -// EthError::RpcError(value) => { -// // Try to parse the error message -// if let Some(message) = value.get("message").and_then(|m| m.as_str()) { -// if message.contains("insufficient funds") { -// return WalletError::InsufficientFunds(message.to_string()); -// } else if message.contains("underpriced") { -// return WalletError::TransactionUnderpriced; -// } else if message.contains("nonce too low") { -// return WalletError::TransactionNonceTooLow; -// } -// // Add more error patterns as needed -// } -// WalletError::TransactionError(format!("RPC error: {:?}", value)) -// }, -// _ => WalletError::TransactionError(format!("Ethereum error: {:?}", error)) -// } -//} - -// ERC-20 Read Function Selectors (_ = NOT CHECKED) -const ERC20_BALANCE_OF: [u8; 4] = [0x70, 0xa0, 0x82, 0x31]; // balanceOf(address) -const ERC20_DECIMALS: [u8; 4] = [0x31, 0x3c, 0xe5, 0x67]; // decimals() -const _ERC20_ALLOWANCE: [u8; 4] = [0xdd, 0x62, 0xed, 0x3e]; // allowance(address,address) -const _ERC20_TOTAL_SUPPLY: [u8; 4] = [0x18, 0x16, 0x0d, 0xdd]; // totalSupply() -const _ERC20_SYMBOL: [u8; 4] = [0x95, 0xd8, 0x9b, 0x41]; // symbol() -const _ERC20_NAME: [u8; 4] = [0x06, 0xfd, 0xde, 0x03]; // name() - -// ERC-20 Write Function Selectors -const _ERC20_TRANSFER: [u8; 4] = [0xa9, 0x05, 0x9c, 0xbb]; // transfer(address,uint256) -const _ERC20_TRANSFER_FROM: [u8; 4] = [0x23, 0xb8, 0x72, 0xdd]; // transferFrom(address,address,uint256) -const _ERC20_APPROVE: [u8; 4] = [0x09, 0x5e, 0xa7, 0xb3]; // approve(address,uint256) +// +// ERC-20 TOKEN FUNCTIONS +// -/// Get the balance of ERC20 tokens for an address +/// Get the ERC20 token balance of an address or name +/// Returns the balance in token units (adjusted for decimals) pub fn erc20_balance_of( token_address: &str, owner_address: &str, - provider: Provider + provider: &Provider ) -> Result { - kiprintln!("PROCESS_LIB::erc20_balance_of token_address: {}", token_address); - // Resolve addresses - let token: EthAddress = resolve_name(&token_address, provider.chain_id)?; - kiprintln!("PROCESS_LIB::erc20_balance_of token: {}", token); - let owner = resolve_name(&owner_address, provider.chain_id)?; - kiprintln!("PROCESS_LIB::erc20_balance_of owner: {}", owner); - - // The ERC20 balanceOf function selector: keccak256("balanceOf(address)")[0:4] - //let selector = [0xa9, 0x05, 0x9c, 0xbb]; - let selector = ERC20_BALANCE_OF; - kiprintln!("PROCESS_LIB::erc20_balance_of selector: {:?}", selector); - - // Encode the owner address parameter (padded to 32 bytes) - let mut call_data = Vec::with_capacity(4 + 32); - call_data.extend_from_slice(&selector); - call_data.extend_from_slice(&[0u8; 12]); // 12 bytes of padding - call_data.extend_from_slice(owner.as_slice()); - kiprintln!("PROCESS_LIB::erc20_balance_of call_data: {:?}", call_data); - - // Create the transaction request for eth_call - let tx = TransactionRequest { - to: Some(TxKind::Call(token)), - input: call_data.into(), - ..Default::default() - }; - - kiprintln!("PROCESS_LIB::erc20_balance_of tx: {:#?}", tx); - - // Call the contract - let result = provider.call(tx, None)?; - - kiprintln!("PROCESS_LIB::erc20_balance_of result: {:?}", result); + let token = resolve_name(token_address, provider.chain_id)?; + let owner = resolve_name(owner_address, provider.chain_id)?; - // Parse the result (a uint256 value) - if result.len() < 32 { - kiprintln!("PROCESS_LIB::erc20_balance_of Invalid result length"); - return Err(WalletError::TransactionError("Invalid result length".into())); - } + let call = IERC20::balanceOfCall { who: owner }; + let balance = call_view_function(token, call, provider)?; - // Convert the bytes to a U256 - let balance = U256::from_be_bytes::<32>(result[0..32].try_into().unwrap()); + let decimals = erc20_decimals(token_address, provider)?; + let balance_float = balance._0.to::() as f64 / 10f64.powi(decimals as i32); - // TODO: This should be based on the contract's decimals, fix later with a LUT - //Ok(EthAmount { wei_value: balance }) + Ok(balance_float) +} - let decimals = erc20_decimals(token, &provider)?; - let balance_u128 = balance.to::(); - let balance_float = balance_u128 as f64 / 10f64.powi(decimals as i32); - Ok(balance_float) // Returns balance in full tokens (e.g., 390.159112 USDC) +/// Get the number of decimals for an ERC20 token +pub fn erc20_decimals(token_address: &str, provider: &Provider) -> Result { + let token = resolve_name(token_address, provider.chain_id)?; + let call = IERC20::decimalsCall {}; + let decimals = call_view_function(token, call, provider)?; + Ok(decimals._0) +} +/// Get the token symbol for an ERC20 token +pub fn erc20_symbol(token_address: &str, provider: &Provider) -> Result { + let token = resolve_name(token_address, provider.chain_id)?; + let call = IERC20::symbolCall {}; + let symbol = call_view_function(token, call, provider)?; + Ok(symbol._0) } -pub fn erc20_decimals(token_address: EthAddress, provider: &Provider) -> Result { - kiprintln!("PROCESS_LIB::erc20_decimals token_address: {}", token_address); - let token = token_address; - kiprintln!("PROCESS_LIB::erc20_decimals token: {}", token); - let selector = ERC20_DECIMALS; - let call_data = selector.to_vec(); // ✅ Ensure it's exactly 4 bytes +/// Get the token name for an ERC20 token +pub fn erc20_name(token_address: &str, provider: &Provider) -> Result { + let token = resolve_name(token_address, provider.chain_id)?; + let call = IERC20::nameCall {}; + let name = call_view_function(token, call, provider)?; + Ok(name._0) +} - kiprintln!("PROCESS_LIB::erc20_decimals selector: {:?}", selector); +/// Get the total supply of an ERC20 token +pub fn erc20_total_supply(token_address: &str, provider: &Provider) -> Result { + let token = resolve_name(token_address, provider.chain_id)?; + let call = IERC20::totalSupplyCall {}; + let total_supply = call_view_function(token, call, provider)?; + Ok(total_supply._0) +} - let tx = TransactionRequest { - to: Some(TxKind::Call(token)), - input: call_data.into(), - ..Default::default() - }; +/// Get the allowance for an ERC20 token +pub fn erc20_allowance( + token_address: &str, + owner_address: &str, + spender_address: &str, + provider: &Provider +) -> Result { + let token = resolve_name(token_address, provider.chain_id)?; + let owner = resolve_name(owner_address, provider.chain_id)?; + let spender = resolve_name(spender_address, provider.chain_id)?; + + let call = IERC20::allowanceCall { owner, spender }; + let allowance = call_view_function(token, call, provider)?; + Ok(allowance._0) +} - let result = provider.call(tx, None)?; - kiprintln!("PROCESS_LIB::erc20_decimals result: {:?}", result); +/// Transfer ERC20 tokens to an address or name +pub fn erc20_transfer( + token_address: &str, + to_address: &str, + amount: U256, + provider: &Provider, + signer: &S +) -> Result { + kiprintln!("PL:: Transferring {} tokens to {} on {}", amount, to_address, provider.chain_id); - if result.len() < 32 { - return Err(WalletError::TransactionError("Invalid decimals response".into())); - } - kiprintln!("PROCESS_LIB::erc20_decimals done", ); + // Resolve addresses + let token = resolve_name(token_address, provider.chain_id)?; + let to = resolve_name(to_address, provider.chain_id)?; + + kiprintln!("PL:: Resolved token address: {}", token); + kiprintln!("PL:: Resolved recipient address: {}", to); + + // Create the call + let call = IERC20::transferCall { to, value: amount }; + let call_data = call.abi_encode(); + + // Get token details for receipt formatting + let token_symbol = erc20_symbol(token_address, provider).unwrap_or_else(|_| "tokens".to_string()); + let token_decimals = erc20_decimals(token_address, provider).unwrap_or(18); + + kiprintln!("PL:: Token symbol: {}", token_symbol); + kiprintln!("PL:: Token decimals: {}", token_decimals); + + // Format receipt message + let format_receipt = move |_| { + let amount_float = amount.to::() as f64 / 10f64.powi(token_decimals as i32); + format!("Transferred {:.6} {} to {}", amount_float, token_symbol, to_address) + }; + + kiprintln!("PL:: Sending ERC20 transfer transaction..."); + + // Prepare and send transaction + prepare_and_send_tx( + token, + call_data, + U256::ZERO, + provider, + signer, + Some(100_000), // Default gas limit for ERC20 transfers + format_receipt + ) +} - Ok(result[31]) // Decimals are stored in the last byte of the 32-byte response +/// Approve an address to spend ERC20 tokens +pub fn erc20_approve( + token_address: &str, + spender_address: &str, + amount: U256, + provider: &Provider, + signer: &S +) -> Result { + // Resolve addresses + let token = resolve_name(token_address, provider.chain_id)?; + let spender = resolve_name(spender_address, provider.chain_id)?; + + // Create the call + let call = IERC20::approveCall { spender, value: amount }; + let call_data = call.abi_encode(); + + // Get token details for receipt formatting + let token_symbol = erc20_symbol(token_address, provider).unwrap_or_else(|_| "tokens".to_string()); + let token_decimals = erc20_decimals(token_address, provider).unwrap_or(18); + + // Format receipt message + let format_receipt = move |_| { + let amount_float = amount.to::() as f64 / 10f64.powi(token_decimals as i32); + format!("Approved {:.6} {} to be spent by {}", amount_float, token_symbol, spender_address) + }; + + // Prepare and send transaction + prepare_and_send_tx( + token, + call_data, + U256::ZERO, + provider, + signer, + Some(60_000), // Default gas limit for approvals + format_receipt + ) } +/// Transfer ERC20 tokens from one address to another (requires approval) +pub fn erc20_transfer_from( + token_address: &str, + from_address: &str, + to_address: &str, + amount: U256, + provider: &Provider, + signer: &S +) -> Result { + // Resolve addresses + let token = resolve_name(token_address, provider.chain_id)?; + let from = resolve_name(from_address, provider.chain_id)?; + let to = resolve_name(to_address, provider.chain_id)?; + + // Create the call + let call = IERC20::transferFromCall { from, to, value: amount }; + let call_data = call.abi_encode(); + + // Get token details for receipt formatting + let token_symbol = erc20_symbol(token_address, provider).unwrap_or_else(|_| "tokens".to_string()); + let token_decimals = erc20_decimals(token_address, provider).unwrap_or(18); + + // Format receipt message + let format_receipt = move |_| { + let amount_float = amount.to::() as f64 / 10f64.powi(token_decimals as i32); + format!("Transferred {:.6} {} from {} to {}", + amount_float, token_symbol, from_address, to_address) + }; + + // Prepare and send transaction + prepare_and_send_tx( + token, + call_data, + U256::ZERO, + provider, + signer, + Some(100_000), // Default gas limit + format_receipt + ) +} -///// Transfer ERC20 tokens to another address -//pub fn erc20_transfer( -// token_address: &str, -// to_address: &str, -// amount: EthAmount, -// provider: Provider, -// signer: &S -//) -> Result { -// // Resolve addresses -// let token = resolve_name(token_address, provider.chain_id())?; -// let to = resolve_name(to_address, provider.chain_id())?; -// -// // The ERC20 transfer function selector: keccak256("transfer(address,uint256)")[0:4] -// let selector = [0xa9, 0x05, 0x9c, 0xbb]; -// -// // Encode the parameters: address recipient, uint256 amount -// let mut call_data = Vec::with_capacity(4 + 32 + 32); -// call_data.extend_from_slice(&selector); -// -// // Recipient address (padded to 32 bytes) -// call_data.extend_from_slice(&[0u8; 12]); // 12 bytes of padding -// call_data.extend_from_slice(to.as_slice()); -// -// // Amount (padded to 32 bytes) -// let amount_bytes = amount.as_wei().to_be_bytes::<32>(); -// call_data.extend_from_slice(&amount_bytes); -// -// // Get the current nonce -// let from_address = signer.address(); -// let nonce = provider.get_transaction_count(from_address, None)?.to::(); -// -// // Estimate gas for the token transfer (usually around 60k for ERC20 transfers) -// let tx_req = TransactionRequest { -// from: Some(from_address), -// to: Some(token), -// data: Some(call_data.clone().into()), -// ..Default::default() -// }; -// -// let gas_limit = provider.estimate_gas(tx_req, None)? -// .to::() -// .saturating_mul(12).saturating_div(10); // Add 20% buffer -// -// // Calculate gas price based on the chain -// let (gas_price, priority_fee) = match signer.chain_id() { -// // Use your existing gas calculation functions -// 1 => calculate_eth_mainnet_gas(&provider)?, -// 8453 => calculate_base_gas(&provider)?, -// _ => { -// let base_fee = provider.get_gas_price()?.to::(); -// let adjusted_fee = (base_fee * 130) / 100; -// (adjusted_fee, adjusted_fee / 10) -// } -// }; -// -// // Create transaction data -// let tx_data = TransactionData { -// to: token, -// value: U256::ZERO, // No ETH sent with token transfers -// data: Some(call_data), -// nonce, -// gas_limit, -// gas_price, -// max_priority_fee: Some(priority_fee), -// chain_id: signer.chain_id(), -// }; -// -// // Sign and send transaction -// let signed_tx = signer.sign_transaction(&tx_data)?; -// let tx_hash = provider.send_raw_transaction(signed_tx.into())?; -// -// Ok(TxReceipt { -// hash: tx_hash, -// details: format!("Sent {} tokens to {}", amount.to_string(), to_address), -// }) -//} - -// THE HYPERMAP stuff +// +// ERC-721 NFT FUNCTIONS +// -/// Result type for Hypermap transactions -#[derive(Debug, Clone)] -pub struct HypermapTxReceipt { - /// Transaction hash - pub hash: TxHash, - /// Description of the operation - pub description: String, +/// Get the NFT balance of an address +pub fn erc721_balance_of( + token_address: &str, + owner_address: &str, + provider: &Provider +) -> Result { + let token = resolve_name(token_address, provider.chain_id)?; + let owner = resolve_name(owner_address, provider.chain_id)?; + + let call = IERC721::balanceOfCall { owner }; + let balance = call_view_function(token, call, provider)?; + Ok(balance._0) } -/// Create a note (mutable data) on a Hypermap namespace entry -/// -/// # Parameters -/// - `parent_entry`: The namespace entry (e.g. "mynode.hypr") where the note will be created -/// - `note_key`: The note key to create (must start with '~') -/// - `data`: The data to store in the note -/// - `provider`: The Ethereum provider to use -/// - `signer`: The signer to use for signing the transaction -/// -/// # Returns -/// A result containing a HypermapTxReceipt or a WalletError -pub fn create_note( - parent_entry: &str, - note_key: &str, - data: Vec, - provider: Provider, - signer: &S, -) -> Result { - // Verify the note key is valid - if !valid_note(note_key) { - return Err(WalletError::NameResolutionError( - format!("Invalid note key '{}'. Must start with '~' and contain only lowercase letters, numbers, and hyphens", note_key) - )); - } +/// Get the owner of an NFT token +pub fn erc721_owner_of( + token_address: &str, + token_id: U256, + provider: &Provider +) -> Result { + let token = resolve_name(token_address, provider.chain_id)?; + let call = IERC721::ownerOfCall { tokenId: token_id }; + let owner = call_view_function(token, call, provider)?; + Ok(owner._0) +} - // Get the parent TBA address - let hypermap = provider.hypermap(); - let parent_hash_str = namehash(parent_entry); +/// Check if an operator is approved for all NFTs of an owner +pub fn erc721_is_approved_for_all( + token_address: &str, + owner_address: &str, + operator_address: &str, + provider: &Provider +) -> Result { + let token = resolve_name(token_address, provider.chain_id)?; + let owner = resolve_name(owner_address, provider.chain_id)?; + let operator = resolve_name(operator_address, provider.chain_id)?; - println!("Parent entry: {}", parent_entry); - println!("Parent hash: {}", parent_hash_str); + let call = IERC721::isApprovedForAllCall { owner, operator }; + let is_approved = call_view_function(token, call, provider)?; + Ok(is_approved._0) +} + +/// Safely transfer an NFT +pub fn erc721_safe_transfer_from( + token_address: &str, + from_address: &str, + to_address: &str, + token_id: U256, + provider: &Provider, + signer: &S +) -> Result { + // Resolve addresses + let token = resolve_name(token_address, provider.chain_id)?; + let from = resolve_name(from_address, provider.chain_id)?; + let to = resolve_name(to_address, provider.chain_id)?; - let (tba, owner, _) = hypermap.get_hash(&parent_hash_str)?; + // Create the call + let call = IERC721::safeTransferFromCall { from, to, tokenId: token_id }; + let call_data = call.abi_encode(); - println!("TBA address (parent): {}", tba); - println!("Owner address: {}", owner); + // Format receipt message + let format_receipt = move |_| { + format!("Safely transferred NFT ID {} from {} to {}", token_id, from_address, to_address) + }; - // Check that the signer is the owner of the parent entry - let signer_address = signer.address(); - println!("Signer address: {}", signer_address); + // Prepare and send transaction + prepare_and_send_tx( + token, + call_data, + U256::ZERO, + provider, + signer, + Some(200_000), // Higher gas limit for NFT transfers + format_receipt + ) +} + +/// Set approval for all tokens to an operator +pub fn erc721_set_approval_for_all( + token_address: &str, + operator_address: &str, + approved: bool, + provider: &Provider, + signer: &S +) -> Result { + // Resolve addresses + let token = resolve_name(token_address, provider.chain_id)?; + let operator = resolve_name(operator_address, provider.chain_id)?; - if signer_address != owner { - return Err(WalletError::PermissionDenied( - format!("Signer address {} does not own the parent entry {}", signer_address, parent_entry) - )); - } + // Create the call + let call = IERC721::setApprovalForAllCall { operator, approved }; + let call_data = call.abi_encode(); - // Get the hypermap contract address - let hypermap_address = *hypermap.address(); - println!("Hypermap contract address: {}", hypermap_address); - - // Create the note function call data - let note_function = hypermap::contract::noteCall { - note: Bytes::from(note_key.as_bytes().to_vec()), - data: Bytes::from(data), + // Format receipt message + let format_receipt = move |_| { + format!( + "{} operator {} to manage all of your NFTs in contract {}", + if approved { "Approved" } else { "Revoked approval for" }, + operator_address, + token_address + ) }; - let note_call_data = note_function.abi_encode(); - // ?? Bytes::from(note_call_data) or note_call_data? - // Now create an ERC-6551 execute call to send from the wallet to the TBA - let execute_call_data = create_execute_calldata( - hypermap_address, - U256::ZERO, - Bytes::from(note_call_data), - 0 // CALL operation - ); - - // Send the transaction from the wallet to the TBA - let (tx_hash, tx_data) = send_transaction( - tba, - execute_call_data, + // Prepare and send transaction + prepare_and_send_tx( + token, + call_data, U256::ZERO, provider, - signer - )?; + signer, + Some(60_000), // Default gas limit for approvals + format_receipt + ) +} + +// +// ERC-1155 MULTI-TOKEN FUNCTIONS +// + +/// Get the balance of a specific token ID for an account +pub fn erc1155_balance_of( + token_address: &str, + account_address: &str, + token_id: U256, + provider: &Provider +) -> Result { + let token = resolve_name(token_address, provider.chain_id)?; + let account = resolve_name(account_address, provider.chain_id)?; - // Return the receipt with transaction details - Ok(HypermapTxReceipt { - hash: tx_hash, - description: format!("Created note '{}' on '{}'", note_key, parent_entry), - }) + let call = IERC1155::balanceOfCall { account, id: token_id }; + let balance = call_view_function(token, call, provider)?; + Ok(balance._0) } -/// Helper function to create calldata for the TBA's execute function -/// -/// The ERC-6551 execute function has this signature: -/// function execute(address to, uint256 value, bytes calldata data, uint8 operation) -/// -/// Parameters: -/// - to: The target contract to call (the Hypermap contract) -/// - value: Amount of ETH to send (usually 0) -/// - data: The calldata for the target function -/// - operation: The type of operation (0 = CALL, 1 = DELEGATECALL, etc.) -fn create_execute_calldata( - to: EthAddress, - value: U256, - data: Bytes, - operation: u8 -) -> Bytes { - // Function selector for execute(address,uint256,bytes,uint8) - // keccak256("execute(address,uint256,bytes,uint8)")[0:4] - let selector = [0x44, 0xc0, 0x28, 0xfe]; - - // Encode to address (padded to 32 bytes) - let mut to_bytes = vec![0u8; 32]; - to_bytes[12..32].copy_from_slice(to.as_slice()); - - // Encode value (uint256) - let value_bytes = value.to_be_bytes::<32>(); - - // Calculate offset for the dynamic bytes data - // This is the offset in 32-byte words to where the bytes data starts - // 3 fixed params (address, uint256, uint256 offset) + 1 more fixed param after = 4 * 32 = 128 bytes - let offset = U256::from(128); - let offset_bytes = offset.to_be_bytes::<32>(); - - // Encode operation (padded to 32 bytes) - let mut operation_bytes = vec![0u8; 32]; - operation_bytes[31] = operation; - - // Encode bytes length - let data_len = U256::from(data.len()); - let data_len_bytes = data_len.to_be_bytes::<32>(); - - // Encode bytes data (with padding to 32-byte boundary) - let mut padded_data = data.to_vec(); - if padded_data.len() % 32 != 0 { - let padding = vec![0u8; 32 - (padded_data.len() % 32)]; - padded_data.extend_from_slice(&padding); +/// Get balances for multiple accounts and token IDs +pub fn erc1155_balance_of_batch( + token_address: &str, + account_addresses: Vec<&str>, + token_ids: Vec, + provider: &Provider +) -> Result, WalletError> { + // Check that arrays are same length + if account_addresses.len() != token_ids.len() { + return Err(WalletError::TransactionError( + "Arrays of accounts and token IDs must have the same length".into() + )); } - // Combine everything into final calldata - let mut result = Vec::new(); - result.extend_from_slice(&selector); // Function selector (4 bytes) - result.extend_from_slice(&to_bytes); // To address (32 bytes) - result.extend_from_slice(&value_bytes); // Value (32 bytes) - result.extend_from_slice(&offset_bytes); // Data offset (32 bytes) - result.extend_from_slice(&operation_bytes); // Operation (32 bytes) - result.extend_from_slice(&data_len_bytes); // Data length (32 bytes) - result.extend_from_slice(&padded_data); // Data (padded) - - Bytes::from(result) + // Resolve token address + let token = resolve_name(token_address, provider.chain_id)?; + + // Resolve all account addresses + let mut accounts = Vec::with_capacity(account_addresses.len()); + for addr in account_addresses { + accounts.push(resolve_name(addr, provider.chain_id)?); + } + + let call = IERC1155::balanceOfBatchCall { accounts, ids: token_ids }; + let balances = call_view_function(token, call, provider)?; + Ok(balances._0) } -/// Send a transaction to the token-bound account -fn send_transaction( - to: EthAddress, - data: Bytes, - value: U256, - provider: Provider, - signer: &S, -) -> Result<(TxHash, Vec), WalletError> { - let chain_id = signer.chain_id(); - - kiprintln!("PROCESS_LIB::send_transaction starting"); - kiprintln!("PROCESS_LIB::send_transaction chain_id: {}", chain_id); +/// Check if an operator is approved for all tokens of an account +pub fn erc1155_is_approved_for_all( + token_address: &str, + account_address: &str, + operator_address: &str, + provider: &Provider +) -> Result { + let token = resolve_name(token_address, provider.chain_id)?; + let account = resolve_name(account_address, provider.chain_id)?; + let operator = resolve_name(operator_address, provider.chain_id)?; - // Get gas estimates - use 50% buffer for Base to ensure acceptance - let base_fee = provider.get_gas_price()?.to::(); - let gas_price = (base_fee * 150) / 100; // 50% buffer - let priority_fee = gas_price / 5; // 20% of gas price + let call = IERC1155::isApprovedForAllCall { account, operator }; + let is_approved = call_view_function(token, call, provider)?; + Ok(is_approved._0) +} + +/// Set approval for all tokens to an operator +pub fn erc1155_set_approval_for_all( + token_address: &str, + operator_address: &str, + approved: bool, + provider: &Provider, + signer: &S +) -> Result { + // Resolve addresses + let token = resolve_name(token_address, provider.chain_id)?; + let operator = resolve_name(operator_address, provider.chain_id)?; - kiprintln!("PROCESS_LIB::send_transaction base_fee: {}, priority_fee: {}", base_fee, priority_fee); - kiprintln!("PROCESS_LIB::send_transaction gas_price: {}", gas_price); + // Create the call + let call = IERC1155::setApprovalForAllCall { operator, approved }; + let call_data = call.abi_encode(); - // Get the current nonce for the signer's address - let from_address = signer.address(); - let nonce = provider.get_transaction_count(from_address, None)? - .to::(); + // Format receipt message + let format_receipt = move |_| { + format!( + "{} operator {} to manage all of your ERC1155 tokens in contract {}", + if approved { "Approved" } else { "Revoked approval for" }, + operator_address, + token_address + ) + }; - kiprintln!("PROCESS_LIB::send_transaction nonce: {}", nonce); + // Prepare and send transaction + prepare_and_send_tx( + token, + call_data, + U256::ZERO, + provider, + signer, + Some(60_000), // Default gas limit for approvals + format_receipt + ) +} - // For ERC-6551 account operations, use a higher gas limit - // The ERC-6551 execute function is complex and gas-intensive - let estimated_gas = 500_000; // Start high for ERC-6551 +/// Transfer a single ERC1155 token +pub fn erc1155_safe_transfer_from( + token_address: &str, + from_address: &str, + to_address: &str, + token_id: U256, + amount: U256, + data: Vec, + provider: &Provider, + signer: &S +) -> Result { + // Resolve addresses + let token = resolve_name(token_address, provider.chain_id)?; + let from = resolve_name(from_address, provider.chain_id)?; + let to = resolve_name(to_address, provider.chain_id)?; - // Add 50% buffer to estimated gas since this is a complex operation - let gas_limit = (estimated_gas * 150) / 100; + // Create the call + let call = IERC1155::safeTransferFromCall { + from, + to, + id: token_id, + amount, + data: Bytes::from(data) + }; + let call_data = call.abi_encode(); - kiprintln!("PROCESS_LIB::send_transaction estimated_gas: {}", estimated_gas); - kiprintln!("PROCESS_LIB::send_transaction gas_limit: {}", gas_limit); + // Format receipt message + let format_receipt = move |_| { + format!( + "Transferred {} of token ID {} from {} to {}", + amount, token_id, from_address, to_address + ) + }; - // Prepare transaction data - let tx_data = TransactionData { - to, - value, - data: Some(data.to_vec()), - nonce, - gas_limit, - gas_price, - max_priority_fee: Some(priority_fee), - chain_id, + // Prepare and send transaction + prepare_and_send_tx( + token, + call_data, + U256::ZERO, + provider, + signer, + Some(150_000), // Default gas limit for ERC1155 transfers + format_receipt + ) +} + +/// Batch transfer multiple ERC1155 tokens +pub fn erc1155_safe_batch_transfer_from( + token_address: &str, + from_address: &str, + to_address: &str, + token_ids: Vec, + amounts: Vec, + data: Vec, + provider: &Provider, + signer: &S +) -> Result { + // Check that arrays are same length + if token_ids.len() != amounts.len() { + return Err(WalletError::TransactionError( + "Arrays of token IDs and amounts must have the same length".into() + )); + } + + // Resolve addresses + let token = resolve_name(token_address, provider.chain_id)?; + let from = resolve_name(from_address, provider.chain_id)?; + let to = resolve_name(to_address, provider.chain_id)?; + + // Create the call + let call = IERC1155::safeBatchTransferFromCall { + from, + to, + ids: token_ids.clone(), + amounts: amounts.clone(), + data: Bytes::from(data) }; + let call_data = call.abi_encode(); - // Sign the transaction - let signed_tx = signer.sign_transaction(&tx_data)?; - kiprintln!("PROCESS_LIB::send_transaction signed"); + // For batch transfers, gas estimation is tricky - use a formula that scales with token count + let token_count = token_ids.len(); + // Base gas (200,000) + extra per token (50,000 each) + let default_gas = Some(200_000 + (token_count as u64 * 50_000)); - // Send the transaction - let tx_hash = provider.send_raw_transaction(signed_tx.clone().into())?; - kiprintln!("PROCESS_LIB::send_transaction tx_hash: {}", tx_hash); + // Format receipt message + let format_receipt = move |_| { + format!( + "Batch transferred {} token types from {} to {}", + token_count, from_address, to_address + ) + }; - // Return both the hash and the raw transaction data - Ok((tx_hash, signed_tx)) + // Prepare and send transaction + prepare_and_send_tx( + token, + call_data, + U256::ZERO, + provider, + signer, + default_gas, + format_receipt + ) } -///// A simple test function to create a note -//pub fn test_create_note( -// parent_entry: &str, // e.g., "lazybonesitis.os" -// provider: Provider, -// signer: &S, -//) -> Result<(), WalletError> { -// println!("=== TESTING NOTE CREATION ==="); -// println!("Parent entry: {}", parent_entry); -// -// // Simple test note -// let note_key = "~test-note"; -// let data = "This is a test note created at ".to_string() + &chrono::Utc::now().to_rfc3339(); -// -// println!("Creating note: {}", note_key); -// println!("Data: {}", data); -// -// match create_note( -// parent_entry, -// note_key, -// data.as_bytes().to_vec(), -// provider, -// signer -// ) { -// Ok(receipt) => { -// println!("Success! Transaction hash: {}", receipt.hash); -// println!("Description: {}", receipt.description); -// Ok(()) -// }, -// Err(e) => { -// println!("Error creating note: {:?}", e); -// Err(e) -// } -// } -//} - -///// Create a note (mutable data) on a Hypermap namespace entry -///// -///// # Parameters -///// - `parent_entry`: The namespace entry (e.g. "mynode.hypr") where the note will be created -///// - `note_key`: The note key to create (must start with '~') -///// - `data`: The data to store in the note -///// - `provider`: The Ethereum provider to use -///// - `signer`: The signer to use for signing the transaction -///// -///// # Returns -///// A result containing a HypermapTxReceipt or a WalletError -//pub fn create_note( -// parent_entry: &str, -// note_key: &str, -// data: Vec, -// provider: Provider, -// signer: &S, -//) -> Result { -// // Verify the note key is valid -// if !valid_note(note_key) { -// return Err(WalletError::NameResolutionError( -// format!("Invalid note key '{}'. Must start with '~' and contain only lowercase letters, numbers, and hyphens", note_key) -// )); -// } -// -// // Get the parent TBA address -// let hypermap = provider.hypermap(); -// let parent_hash_str = namehash(parent_entry); -// let (tba, owner, _) = hypermap.get_hash(&parent_hash_str)?; -// -// // Check that the signer is the owner of the parent entry -// let signer_address = signer.address(); -// if signer_address != owner { -// return Err(WalletError::PermissionDenied( -// format!("Signer address {} does not own the parent entry {}", signer_address, parent_entry) -// )); -// } // -// // Create the note call data -// let note_function = hypermap::contract::noteCall { -// note: Bytes::from(note_key.as_bytes().to_vec()), -// data: Bytes::from(data), -// }; -// let call_data = note_function.abi_encode(); +// HYPERMAP FUNCTIONS // -// // Prepare and send the transaction -// send_tba_transaction( -// tba, -// call_data.into(), -// U256::ZERO, // No ETH value to send -// provider, -// signer, -// format!("Created note '{}' on '{}'", note_key, parent_entry), -// ) -//} -/// Create a fact (immutable data) on a Hypermap namespace entry -/// -/// # Parameters -/// - `parent_entry`: The namespace entry (e.g. "mynode.hypr") where the fact will be created -/// - `fact_key`: The fact key to create (must start with '!') -/// - `data`: The data to store in the fact -/// - `provider`: The Ethereum provider to use -/// - `signer`: The signer to use for signing the transaction -/// -/// # Returns -/// A result containing a HypermapTxReceipt or a WalletError -pub fn create_fact( +/// Create a note (mutable data) on a Hypermap namespace entry +pub fn create_note( parent_entry: &str, - fact_key: &str, + note_key: &str, data: Vec, provider: Provider, signer: &S, ) -> Result { - // Verify the fact key is valid - if !valid_fact(fact_key) { + // Verify the note key is valid + if !valid_note(note_key) { return Err(WalletError::NameResolutionError( - format!("Invalid fact key '{}'. Must start with '!' and contain only lowercase letters, numbers, and hyphens", fact_key) + format!("Invalid note key '{}'. Must start with '~' and contain only lowercase letters, numbers, and hyphens", note_key) )); } - // Get the parent TBA address - let hypermap = provider.hypermap(); - let parent_hash_str = namehash(parent_entry); - let (tba, owner, _) = hypermap.get_hash(&parent_hash_str)?; + // Create the note function call data + let note_function = hypermap::contract::noteCall { + note: Bytes::from(note_key.as_bytes().to_vec()), + data: Bytes::from(data), + }; - // Check that the signer is the owner of the parent entry - let signer_address = signer.address(); - if signer_address != owner { - return Err(WalletError::PermissionDenied( - format!("Signer address {} does not own the parent entry {}", signer_address, parent_entry) + // Create the hypermap transaction + create_hypermap_tx( + parent_entry, + Bytes::from(note_function.abi_encode()), + || format!("Created note '{}' on '{}'", note_key, parent_entry), + provider, + signer + ) +} + +/// Create a fact (immutable data) on a Hypermap namespace entry +pub fn create_fact( + parent_entry: &str, + fact_key: &str, + data: Vec, + provider: Provider, + signer: &S, +) -> Result { + // Verify the fact key is valid + if !valid_fact(fact_key) { + return Err(WalletError::NameResolutionError( + format!("Invalid fact key '{}'. Must start with '!' and contain only lowercase letters, numbers, and hyphens", fact_key) )); } - // Create the fact call data + // Create the fact function call data let fact_function = hypermap::contract::factCall { fact: Bytes::from(fact_key.as_bytes().to_vec()), data: Bytes::from(data), }; - let call_data = fact_function.abi_encode(); - - // Prepare and send the transaction - send_tba_transaction( - tba, - call_data.into(), - U256::ZERO, // No ETH value to send + + // Create the hypermap transaction + create_hypermap_tx( + parent_entry, + Bytes::from(fact_function.abi_encode()), + || format!("Created fact '{}' on '{}'", fact_key, parent_entry), provider, - signer, - format!("Created fact '{}' on '{}'", fact_key, parent_entry), + signer ) } /// Mint a new namespace entry under a parent entry -/// -/// # Parameters -/// - `parent_entry`: The parent namespace entry (e.g. "mynode.hypr") -/// - `label`: The new label to mint (without prefix or parent) -/// - `recipient`: The address that will own the new entry -/// - `implementation`: The address of the token-bound account implementation -/// - `provider`: The Ethereum provider to use -/// - `signer`: The signer to use for signing the transaction -/// -/// # Returns -/// A result containing a HypermapTxReceipt or a WalletError pub fn mint_entry( parent_entry: &str, label: &str, @@ -1009,27 +1184,11 @@ pub fn mint_entry( )); } - // Get the parent TBA address - let hypermap = provider.hypermap(); - let parent_hash_str = namehash(parent_entry); - kiprintln!("PROCESS_LIB::mint_entry parent_hash_str: {}", parent_hash_str); - let (tba, owner, _) = hypermap.get_hash(&parent_hash_str)?; - - // Check that the signer is the owner of the parent entry - let signer_address = signer.address(); - if signer_address != owner { - return Err(WalletError::PermissionDenied( - format!("Signer address {} does not own the parent entry {}", signer_address, parent_entry) - )); - } - - // Resolve recipient address + // Resolve addresses let recipient_address = resolve_name(recipient, provider.chain_id)?; - - // Resolve implementation address let implementation_address = resolve_name(implementation, provider.chain_id)?; - // Create the mint call data + // Create the mint function call data let mint_function = hypermap::contract::mintCall { who: recipient_address, label: Bytes::from(label.as_bytes().to_vec()), @@ -1037,153 +1196,3215 @@ pub fn mint_entry( erc721Data: Bytes::default(), // No ERC721 data implementation: implementation_address, }; - let call_data = mint_function.abi_encode(); - - kiprintln!("Parent entry: {}", parent_entry); - kiprintln!("Parent hash: {}", parent_hash_str); - kiprintln!("TBA address: {}", tba); - kiprintln!("Owner address: {}", owner); - kiprintln!("Signer address: {}", signer_address); - - // Prepare and send the transaction - send_tba_transaction( - tba, - call_data.into(), - U256::ZERO, // No ETH value to send + + // Create the hypermap transaction + create_hypermap_tx( + parent_entry, + Bytes::from(mint_function.abi_encode()), + || format!("Minted new entry '{}' under '{}'", label, parent_entry), provider, - signer, - format!("Minted new entry '{}' under '{}'", label, parent_entry), + signer ) } /// Set the gene for a namespace entry -/// -/// # Parameters -/// - `entry`: The namespace entry (e.g. "mynode.hypr") to set the gene for -/// - `gene_implementation`: The address of the token-bound account implementation to use as the gene -/// - `provider`: The Ethereum provider to use -/// - `signer`: The signer to use for signing the transaction -/// -/// # Returns -/// A result containing a HypermapTxReceipt or a WalletError pub fn set_gene( entry: &str, gene_implementation: &str, provider: Provider, signer: &S, ) -> Result { - // Get the entry's TBA address - let hypermap = provider.hypermap(); - let entry_hash_str = namehash(entry); - let (tba, owner, _) = hypermap.get_hash(&entry_hash_str)?; - - // Check that the signer is the owner of the entry - let signer_address = signer.address(); - if signer_address != owner { - return Err(WalletError::PermissionDenied( - format!("Signer address {} does not own the entry {}", signer_address, entry) - )); - } - // Resolve gene implementation address let gene_address = resolve_name(gene_implementation, provider.chain_id)?; - // Create the gene call data + // Create the gene function call data let gene_function = hypermap::contract::geneCall { _gene: gene_address, }; - let call_data = gene_function.abi_encode(); - - // Prepare and send the transaction - send_tba_transaction( - tba, - call_data.into(), - U256::ZERO, // No ETH value to send + + // Create the hypermap transaction + create_hypermap_tx( + entry, + Bytes::from(gene_function.abi_encode()), + || format!("Set gene for '{}' to '{}'", entry, gene_implementation), provider, - signer, - format!("Set gene for '{}' to '{}'", entry, gene_implementation), + signer ) } -/// Helper function to send a transaction to a token-bound account -fn send_tba_transaction( - tba: EthAddress, - data: Bytes, - value: U256, - provider: Provider, - signer: &S, - description: String, -) -> Result { - kiprintln!("PROCESS_LIB::send_tba_transaction starting"); - - let chain_id = signer.chain_id(); - kiprintln!("PROCESS_LIB::send_tba_transaction chain_id: {}", chain_id); - - // TODO: change from hardcoded base to dynamic - let (base_fee, priority_fee) = calculate_base_gas(&provider)?; - kiprintln!("PROCESS_LIB::send_tba_transaction base_fee: {}, priority_fee: {}", base_fee, priority_fee); - - let gas_price = (base_fee * 180) / 100; - kiprintln!("PROCESS_LIB::send_tba_transaction gas_price: {}", gas_price); - - // Get the current nonce for the signer's address - let from_address = signer.address(); - let nonce = provider.get_transaction_count(from_address, None)? - .to::(); - kiprintln!("PROCESS_LIB::send_tba_transaction nonce: {}", nonce); - - // Estimate gas limit for the transaction - let tx_req = TransactionRequest::default() - .from(from_address) - .to(tba) - .input(data.clone().into()) - .value(value); - - let estimated_gas = match provider.estimate_gas(tx_req, None) { - Ok(gas) => gas.to::(), - Err(_) => { - // If estimation fails, use a conservative gas limit - // This might happen for new contracts or complex interactions - 300_000 - } - }; - kiprintln!("PROCESS_LIB::send_tba_transaction estimated_gas: {}", estimated_gas); - - // Add 120% buffer to estimated gas - let gas_limit = (estimated_gas * 220) / 100; - kiprintln!("PROCESS_LIB::send_tba_transaction gas_limit: {}", gas_limit); - - // Prepare transaction data - let tx_data = TransactionData { - to: tba, - value, - data: Some(data.to_vec()), - nonce, - gas_limit, - gas_price, - max_priority_fee: Some(priority_fee), - chain_id, - }; - //kiprintln!("PROCESS_LIB::send_tba_transaction tx_data: {:#?}", tx_data); - - // Sign the transaction - let signed_tx = signer.sign_transaction(&tx_data)?; - kiprintln!("PROCESS_LIB::send_tba_transaction signed"); - - // Send the transaction - let tx_hash = provider.send_raw_transaction(signed_tx.into())?; - kiprintln!("PROCESS_LIB::send_tba_transaction tx_hash: {}", tx_hash); - - // Return the receipt with transaction details - Ok(HypermapTxReceipt { - hash: tx_hash, - description, - }) -} - +//use crate::eth::{ +// Provider, +// EthError, +// BlockNumberOrTag +//}; +//use crate::signer::{ +// Signer, +// LocalSigner, +// TransactionData, +// SignerError, +// EncryptedSignerData +//}; +//use crate::hypermap::{ +// namehash, +// valid_note, +// valid_fact, +// valid_name, +//}; +//use crate::{ +// hypermap, +// kiprintln +//}; +// +//use thiserror::Error; +//use alloy_primitives::{ +// Address as EthAddress, +// TxHash, +// U256, +// Bytes +//}; +//use alloy::rpc::types::{ +// TransactionReceipt, +// TransactionRequest +//}; +//use alloy_primitives::TxKind; +//use std::str::FromStr; +//use alloy_sol_types::{sol, SolCall}; +// +//sol! { +// interface IERC20 { +// function balanceOf(address who) external view returns (uint256); +// function transfer(address to, uint256 value) external returns (bool); +// function approve(address spender, uint256 value) external returns (bool); +// function transferFrom(address from, address to, uint256 value) external returns (bool); +// function allowance(address owner, address spender) external view returns (uint256); +// function totalSupply() external view returns (uint256); +// function decimals() external view returns (uint8); +// function symbol() external view returns (string); +// function name() external view returns (string); +// } +// +// interface IERC721 { +// function balanceOf(address owner) external view returns (uint256); +// function ownerOf(uint256 tokenId) external view returns (address); +// function safeTransferFrom(address from, address to, uint256 tokenId) external; +// function transferFrom(address from, address to, uint256 tokenId) external; +// function approve(address to, uint256 tokenId) external; +// function setApprovalForAll(address operator, bool approved) external; +// function getApproved(uint256 tokenId) external view returns (address); +// function isApprovedForAll(address owner, address operator) external view returns (bool); +// } +// +// interface IERC1155 { +// function balanceOf(address account, uint256 id) external view returns (uint256); +// function balanceOfBatch(address[] accounts, uint256[] ids) external view returns (uint256[]); +// function setApprovalForAll(address operator, bool approved) external; +// function isApprovedForAll(address account, address operator) external view returns (bool); +// function safeTransferFrom(address from, address to, uint256 id, uint256 amount, bytes data) external; +// function safeBatchTransferFrom(address from, address to, uint256[] ids, uint256[] amounts, bytes data) external; +// } +// +// interface IERC6551Account { +// function execute(address to, uint256 value, bytes calldata data, uint8 operation) external returns (bytes); +// } +//} +// +//#[derive(Debug, Error)] +//pub enum WalletError { +// #[error("signing error: {0}")] +// SignerError(#[from] SignerError), +// +// #[error("ethereum error: {0}")] +// EthError(#[from] EthError), +// +// #[error("name resolution error: {0}")] +// NameResolutionError(String), +// +// #[error("invalid amount: {0}")] +// InvalidAmount(String), +// +// #[error("transaction error: {0}")] +// TransactionError(String), +// +// #[error("gas estimation error: {0}")] +// GasEstimationError(String), +// +// #[error("insufficient funds: {0}")] +// InsufficientFunds(String), +// +// #[error("network congestion: {0}")] +// NetworkCongestion(String), +// +// #[error("transaction underpriced")] +// TransactionUnderpriced, +// +// #[error("transaction nonce too low")] +// TransactionNonceTooLow, +// +// #[error("permission denied: {0}")] +// PermissionDenied(String), +//} +// +///// Represents the storage state of a wallet's private key +//#[derive(Debug, Clone)] +//pub enum KeyStorage { +// /// An unencrypted wallet with a signer +// Decrypted(LocalSigner), +// +// /// An encrypted wallet - contains all the necessary data +// Encrypted(EncryptedSignerData), +//} +// +//impl KeyStorage { +// /// Get the encrypted data if this is an encrypted key storage +// pub fn get_encrypted_data(&self) -> Option> { +// match self { +// KeyStorage::Encrypted(data) => Some(data.encrypted_data.clone()), +// KeyStorage::Decrypted(_) => None, +// } +// } +// +// /// Get the address associated with this wallet +// pub fn get_address(&self) -> String { +// match self { +// KeyStorage::Decrypted(signer) => signer.address().to_string(), +// KeyStorage::Encrypted(data) => data.address.clone(), +// } +// } +// +// /// Get the chain ID associated with this wallet +// pub fn get_chain_id(&self) -> u64 { +// match self { +// KeyStorage::Decrypted(signer) => signer.chain_id(), +// KeyStorage::Encrypted(data) => data.chain_id, +// } +// } +//} +// +///// Represents an amount of ETH with proper formatting +//#[derive(Debug, Clone)] +//pub struct EthAmount { +// /// Value in wei +// wei_value: U256, +//} +// +//impl EthAmount { +// /// Create a new amount from ETH value +// pub fn from_eth(eth_value: f64) -> Self { +// // Convert ETH to wei (1 ETH = 10^18 wei) +// let wei = (eth_value * 1_000_000_000_000_000_000.0) as u128; +// Self { +// wei_value: U256::from(wei), +// } +// } +// +// /// Create from a string like "0.1 ETH" or "10 wei" +// pub fn from_string(amount_str: &str) -> Result { +// let parts: Vec<&str> = amount_str.trim().split_whitespace().collect(); +// +// if parts.is_empty() { +// return Err(WalletError::InvalidAmount( +// "Empty amount string".to_string() +// )); +// } +// +// let value_str = parts[0]; +// let unit = parts.get(1).map(|s| s.to_lowercase()).unwrap_or_else(|| "eth".to_string()); +// +// let value = value_str.parse::() +// .map_err(|_| WalletError::InvalidAmount(format!("Invalid numeric value: {}", value_str)))?; +// +// match unit.as_str() { +// "eth" => Ok(Self::from_eth(value)), +// "wei" => Ok(Self { +// wei_value: U256::from(value as u128), +// }), +// _ => Err(WalletError::InvalidAmount(format!("Unknown unit: {}", unit))), +// } +// } +// +// /// Get the value in wei +// pub fn as_wei(&self) -> U256 { +// self.wei_value +// } +// +// /// Get a human-readable string representation +// pub fn to_string(&self) -> String { +// // For values over 0.0001 ETH, show in ETH, otherwise in wei +// if self.wei_value >= U256::from(100_000_000_000_000u128) { +// // Convert to u128 first (safe since ETH total supply fits in u128) then to f64 +// let wei_u128 = self.wei_value.to::(); +// let eth_value = wei_u128 as f64 / 1_000_000_000_000_000_000.0; +// format!("{:.6} ETH", eth_value) +// } else { +// format!("{} wei", self.wei_value) +// } +// } +//} +// +///// Transaction receipt returned after sending +//#[derive(Debug, Clone)] +//pub struct TxReceipt { +// /// Transaction hash +// pub hash: TxHash, +// /// Transaction details +// pub details: String, +//} +// +//// Resolve a .hypr name to an Ethereum address using Hypermap +//pub fn resolve_name(name: &str, chain_id: u64) -> Result { +// // If it's already an address, just parse it +// if name.starts_with("0x") && name.len() == 42 { +// return EthAddress::from_str(name) +// .map_err(|_| WalletError::NameResolutionError(format!("Invalid address format: {}", name))); +// } +// +// // hardcoded to .hypr for now +// let formatted_name = if !name.contains('.') { +// format!("{}.hypr", name) +// } else { +// kiprintln!("PROCESS_LIB::resolve_name name: {}", name); +// name.to_string() +// }; +// +// // Use hypermap resolution +// let hypermap = hypermap::Hypermap::default(chain_id); +// match hypermap.get(&formatted_name) { +// Ok((_tba, owner, _)) => { +// Ok(owner) +// }, +// Err(e) => { +// Err(WalletError::NameResolutionError( +// format!("Failed to resolve name '{}': {}", name, e) +// )) +// } +// } +//} +// +///// Send ETH to an address or name +//pub fn send_eth( +// to: &str, +// amount: EthAmount, +// provider: Provider, +// signer: &S, +//) -> Result { +// kiprintln!("PROCESS_LIB::send_eth starting transaction"); +// +// // Current chain-specific handling +// let chain_id = signer.chain_id(); +// kiprintln!("PROCESS_LIB::send_eth chain_id: {}", chain_id); +// +// // This part needs improvement - detect network type more robustly +// let is_test_network = chain_id == 31337 || chain_id == 1337; +// +// // Use network-specific gas strategies +// let (gas_price, priority_fee) = match chain_id { +// // just rough calculations for now +// 1 => calculate_eth_mainnet_gas(&provider)?, // mainnet +// 8453 => calculate_base_gas(&provider)?, // Base +// 10 => calculate_optimism_gas(&provider)?, // Optimism +// +// // Test networks - keep your current approach +// _ if is_test_network => (2_000_000_000, 100_000_000), +// +// // 30% buffer on other networks +// _ => { +// kiprintln!("PROCESS_LIB::send_eth getting gas price"); +// let base_fee = provider.get_gas_price()?.to::(); +// kiprintln!("PROCESS_LIB::send_eth base_fee: {}", base_fee); +// let adjusted_fee = (base_fee * 130) / 100; +// kiprintln!("PROCESS_LIB::send_eth adjusted_fee: {}", adjusted_fee); +// (adjusted_fee, adjusted_fee / 10) +// } +// }; +// +// kiprintln!("PROCESS_LIB::send_eth gas_price: {}", gas_price); +// +// // Resolve the name to an address +// let to_address = resolve_name(to, chain_id)?; +// kiprintln!("PROCESS_LIB::send_eth to_address: {}", to_address); +// +// // Get the current nonce for the signer's address +// let from_address = signer.address(); +// let nonce = provider.get_transaction_count(from_address, None)? +// .to::(); +// kiprintln!("PROCESS_LIB::send_eth nonce: {}", nonce); +// +// // Standard gas limit for ETH transfer +// let gas_limit = 21000; +// +// // Prepare transaction data +// let tx_data = TransactionData { +// to: to_address, +// value: amount.as_wei(), +// data: None, // No data for simple ETH transfer +// nonce, +// gas_limit, +// gas_price, +// max_priority_fee: Some(priority_fee), +// chain_id, +// }; +// +// kiprintln!("PROCESS_LIB::send_eth tx_data prepared"); +// +// // Sign the transaction +// let signed_tx = signer.sign_transaction(&tx_data)?; +// kiprintln!("PROCESS_LIB::send_eth transaction signed"); +// +// // Send the transaction +// let tx_hash = provider.send_raw_transaction(signed_tx.into())?; +// kiprintln!("PROCESS_LIB::send_eth tx_hash: {}", tx_hash); +// +// // Return the receipt with transaction details +// Ok(TxReceipt { +// hash: tx_hash, +// details: format!("Sent {} to {}", amount.to_string(), to), +// }) +//} +// +//// Helper function to calculate EIP-1559 gas parameters with network-specific values +//fn calculate_eip1559_gas( +// provider: &Provider, +// buffer_fraction: u128, +// priority_fee: u128 +//) -> Result<(u128, u128), WalletError> { +// kiprintln!("PROCESS_LIB::calculate_eip1559_gas starting"); +// // Get latest block +// let latest_block = provider.get_block_by_number(BlockNumberOrTag::Latest, false)? +// .ok_or_else(|| WalletError::TransactionError("Failed to get latest block".into()))?; +// +// kiprintln!("PROCESS_LIB::calculate_eip1559_gas latest_block received"); +// +// // Get base fee +// let base_fee = latest_block.header.inner.base_fee_per_gas +// .ok_or_else(|| WalletError::TransactionError("No base fee in block".into()))? +// as u128; +// +// kiprintln!("PROCESS_LIB::calculate_eip1559_gas base_fee: {}", base_fee); +// +// // Calculate max fee with the provided buffer fraction +// let max_fee = base_fee + (base_fee / buffer_fraction); +// kiprintln!("PROCESS_LIB::calculate_eip1559_gas max_fee: {}", max_fee); +// +// Ok((max_fee, priority_fee)) +//} +// +//// Network-specific gas calculation for Ethereum mainnet +//fn calculate_eth_mainnet_gas(provider: &Provider) -> Result<(u128, u128), WalletError> { +// // For mainnet: 50% buffer and 1.5 gwei priority fee +// calculate_eip1559_gas(provider, 2, 1_500_000_000u128) +//} +// +//// Gas calculation for Base +//fn calculate_base_gas(provider: &Provider) -> Result<(u128, u128), WalletError> { +// // Get the latest block to determine current gas conditions +// let latest_block = provider.get_block_by_number(BlockNumberOrTag::Latest, false)? +// .ok_or_else(|| WalletError::TransactionError("Failed to get latest block".into()))?; +// +// // Get base fee from the block +// let base_fee = latest_block.header.inner.base_fee_per_gas +// .ok_or_else(|| WalletError::TransactionError("No base fee in block".into()))? +// as u128; +// +// // Calculate max fee with a 33% buffer +// let max_fee = base_fee + (base_fee / 3); +// +// // Dynamic priority fee - 10% of base fee, but with a minimum and a maximum +// // Low minimum for Base which has very low gas prices +// let min_priority_fee = 100_000u128; // 0.0001 gwei minimum +// let max_priority_fee = max_fee / 2; // Never more than half the max fee +// +// let priority_fee = std::cmp::max( +// min_priority_fee, +// std::cmp::min(base_fee / 10, max_priority_fee) +// ); +// +// Ok((max_fee, priority_fee)) +//} +// +//// Gas calculation for Optimism network +//fn calculate_optimism_gas(provider: &Provider) -> Result<(u128, u128), WalletError> { +// // For Optimism: 25% buffer and 0.3 gwei priority fee +// calculate_eip1559_gas(provider, 4, 300_000_000u128) +//} +// +///// Get the ETH balance for an address or name +//pub fn get_eth_balance( +// address_or_name: &str, +// chain_id: u64, +// provider: Provider, +//) -> Result { +// // Resolve name to address +// let address = resolve_name(address_or_name, chain_id)?; +// +// // Query balance +// let balance = provider.get_balance(address, None)?; +// +// // Return formatted amount +// Ok(EthAmount { +// wei_value: balance, +// }) +//} +// +///// Wait for a transaction to be confirmed +//pub fn wait_for_transaction( +// tx_hash: TxHash, +// provider: Provider, +// confirmations: u64, +// timeout_secs: u64 +//) -> Result { +// let start_time = std::time::Instant::now(); +// let timeout = std::time::Duration::from_secs(timeout_secs); +// +// loop { +// // Check if we've exceeded the timeout +// if start_time.elapsed() > timeout { +// return Err(WalletError::TransactionError( +// format!("Transaction confirmation timeout after {} seconds", timeout_secs) +// )); +// } +// +// // Try to get the receipt +// if let Ok(Some(receipt)) = provider.get_transaction_receipt(tx_hash) { +// // Check if we have enough confirmations +// let latest_block = provider.get_block_number()?; +// let receipt_block = receipt.block_number.unwrap_or(0) as u64; +// +// if latest_block >= receipt_block + confirmations { +// return Ok(receipt); +// } +// } +// +// // Wait a bit before checking again +// std::thread::sleep(std::time::Duration::from_secs(2)); +// } +//} +// +//// +//// ERC-20 TOKEN FUNCTIONS +//// +// +///// Get the ERC20 token balance of an address or name +///// Returns the balance in token units (adjusted for decimals) +//pub fn erc20_balance_of( +// token_address: &str, +// owner_address: &str, +// provider: &Provider +//) -> Result { +// kiprintln!("PROCESS_LIB::erc20_balance_of starting"); +// +// // Resolve addresses +// let token = resolve_name(token_address, provider.chain_id)?; +// kiprintln!("PROCESS_LIB::erc20_balance_of token: {}", token); +// let owner = resolve_name(owner_address, provider.chain_id)?; +// kiprintln!("PROCESS_LIB::erc20_balance_of owner: {}", owner); +// +// // Create the call using sol! macro generated struct +// let call = IERC20::balanceOfCall { who: owner }; +// let call_data = call.abi_encode(); +// +// // Create the transaction request for eth_call +// let tx = TransactionRequest { +// to: Some(TxKind::Call(token)), +// input: call_data.into(), +// ..Default::default() +// }; +// +// // Call the contract +// let result = provider.call(tx, None)?; +// +// // Verify we got a valid result +// if result.is_empty() { +// return Err(WalletError::TransactionError("Empty result from balanceOf call".into())); +// } +// +// // Decode the result +// let balance = match IERC20::balanceOfCall::abi_decode_returns(&result, true) { +// Ok(decoded) => decoded._0, // Returns (U256,) +// Err(e) => return Err(WalletError::TransactionError( +// format!("Failed to decode balanceOf result: {}", e) +// )), +// }; +// +// // Get the token's decimals +// let decimals = erc20_decimals(token_address, provider)?; +// +// // Calculate the token amount +// let balance_u128 = balance.to::(); +// let balance_float = balance_u128 as f64 / 10f64.powi(decimals as i32); +// +// Ok(balance_float) +//} +// +///// Get the number of decimals for an ERC20 token +//pub fn erc20_decimals(token_address: &str, provider: &Provider) -> Result { +// kiprintln!("PROCESS_LIB::erc20_decimals starting"); +// +// // Resolve token address +// let token = resolve_name(token_address, provider.chain_id)?; +// kiprintln!("PROCESS_LIB::erc20_decimals token: {}", token); +// +// // Create the call using sol! macro generated struct +// let call = IERC20::decimalsCall {}; +// let call_data = call.abi_encode(); +// +// // Create the transaction request +// let tx = TransactionRequest { +// to: Some(TxKind::Call(token)), +// input: call_data.into(), +// ..Default::default() +// }; +// +// // Call the contract +// let result = provider.call(tx, None)?; +// kiprintln!("PROCESS_LIB::erc20_decimals result received"); +// +// // Verify we got a valid result +// if result.is_empty() { +// return Err(WalletError::TransactionError("Empty result from decimals call".into())); +// } +// +// // Decode the result +// let decimals = match IERC20::decimalsCall::abi_decode_returns(&result, true) { +// Ok(decoded) => decoded._0, // Returns (u8,) +// Err(e) => return Err(WalletError::TransactionError( +// format!("Failed to decode decimals result: {}", e) +// )), +// }; +// +// Ok(decimals) +//} +// +///// Get the token symbol for an ERC20 token +//pub fn erc20_symbol(token_address: &str, provider: &Provider) -> Result { +// kiprintln!("PROCESS_LIB::erc20_symbol starting"); +// +// // Resolve token address +// let token = resolve_name(token_address, provider.chain_id)?; +// +// // Create the call using sol! macro generated struct +// let call = IERC20::symbolCall {}; +// let call_data = call.abi_encode(); +// +// // Create the transaction request +// let tx = TransactionRequest { +// to: Some(TxKind::Call(token)), +// input: call_data.into(), +// ..Default::default() +// }; +// +// // Call the contract +// let result = provider.call(tx, None)?; +// +// // Verify we got a valid result +// if result.is_empty() { +// return Err(WalletError::TransactionError("Empty result from symbol call".into())); +// } +// +// // Decode the result +// let symbol = match IERC20::symbolCall::abi_decode_returns(&result, true) { +// Ok(decoded) => decoded._0, // Returns (String,) +// Err(e) => return Err(WalletError::TransactionError( +// format!("Failed to decode symbol result: {}", e) +// )), +// }; +// +// Ok(symbol) +//} +// +///// Get the token name for an ERC20 token +//pub fn erc20_name(token_address: &str, provider: &Provider) -> Result { +// kiprintln!("PROCESS_LIB::erc20_name starting"); +// +// // Resolve token address +// let token = resolve_name(token_address, provider.chain_id)?; +// +// // Create the call using sol! macro generated struct +// let call = IERC20::nameCall {}; +// let call_data = call.abi_encode(); +// +// // Create the transaction request +// let tx = TransactionRequest { +// to: Some(TxKind::Call(token)), +// input: call_data.into(), +// ..Default::default() +// }; +// +// // Call the contract +// let result = provider.call(tx, None)?; +// +// // Verify we got a valid result +// if result.is_empty() { +// return Err(WalletError::TransactionError("Empty result from name call".into())); +// } +// +// // Decode the result +// let name = match IERC20::nameCall::abi_decode_returns(&result, true) { +// Ok(decoded) => decoded._0, // Returns (String,) +// Err(e) => return Err(WalletError::TransactionError( +// format!("Failed to decode name result: {}", e) +// )), +// }; +// +// Ok(name) +//} +// +///// Get the total supply of an ERC20 token +//pub fn erc20_total_supply(token_address: &str, provider: &Provider) -> Result { +// kiprintln!("PROCESS_LIB::erc20_total_supply starting"); +// +// // Resolve token address +// let token = resolve_name(token_address, provider.chain_id)?; +// +// // Create the call using sol! macro generated struct +// let call = IERC20::totalSupplyCall {}; +// let call_data = call.abi_encode(); +// +// // Create the transaction request +// let tx = TransactionRequest { +// to: Some(TxKind::Call(token)), +// input: call_data.into(), +// ..Default::default() +// }; +// +// // Call the contract +// let result = provider.call(tx, None)?; +// +// // Verify we got a valid result +// if result.is_empty() { +// return Err(WalletError::TransactionError("Empty result from totalSupply call".into())); +// } +// +// // Decode the result +// let total_supply = match IERC20::totalSupplyCall::abi_decode_returns(&result, true) { +// Ok(decoded) => decoded._0, // Returns (U256,) +// Err(e) => return Err(WalletError::TransactionError( +// format!("Failed to decode totalSupply result: {}", e) +// )), +// }; +// +// Ok(total_supply) +//} +// +///// Get the allowance for an ERC20 token +//pub fn erc20_allowance( +// token_address: &str, +// owner_address: &str, +// spender_address: &str, +// provider: &Provider +//) -> Result { +// kiprintln!("PROCESS_LIB::erc20_allowance starting"); +// +// // Resolve addresses +// let token = resolve_name(token_address, provider.chain_id)?; +// let owner = resolve_name(owner_address, provider.chain_id)?; +// let spender = resolve_name(spender_address, provider.chain_id)?; +// +// // Create the call using sol! macro generated struct +// let call = IERC20::allowanceCall { owner, spender }; +// let call_data = call.abi_encode(); +// +// // Create the transaction request +// let tx = TransactionRequest { +// to: Some(TxKind::Call(token)), +// input: call_data.into(), +// ..Default::default() +// }; +// +// // Call the contract +// let result = provider.call(tx, None)?; +// +// // Verify we got a valid result +// if result.is_empty() { +// return Err(WalletError::TransactionError("Empty result from allowance call".into())); +// } +// +// // Decode the result +// let allowance = match IERC20::allowanceCall::abi_decode_returns(&result, true) { +// Ok(decoded) => decoded._0, // Returns (U256,) +// Err(e) => return Err(WalletError::TransactionError( +// format!("Failed to decode allowance result: {}", e) +// )), +// }; +// +// Ok(allowance) +//} +// +///// Transfer ERC20 tokens to an address or name +//pub fn erc20_transfer( +// token_address: &str, +// to_address: &str, +// amount: U256, +// provider: &Provider, +// signer: &S +//) -> Result { +// kiprintln!("PROCESS_LIB::erc20_transfer starting"); +// +// // Resolve addresses +// let token = resolve_name(token_address, provider.chain_id)?; +// let to = resolve_name(to_address, provider.chain_id)?; +// +// // Create the call using sol! macro generated struct +// let call = IERC20::transferCall { to, value: amount }; +// let call_data = call.abi_encode(); +// +// // Get the current nonce for the signer's address +// let from_address = signer.address(); +// let nonce = provider.get_transaction_count(from_address, None)? +// .to::(); +// +// // Estimate gas for the token transfer +// let tx_req = TransactionRequest { +// from: Some(from_address), +// to: Some(TxKind::Call(token)), +// input: call_data.clone().into(), +// ..Default::default() +// }; +// +// // Try to estimate gas, fall back to 100,000 if estimation fails +// let gas_limit = match provider.estimate_gas(tx_req, None) { +// Ok(gas) => (gas.to::() * 120) / 100, // Add 20% buffer +// Err(_) => 100_000, // Default gas limit for ERC20 transfers +// }; +// +// // Calculate gas price based on the chain +// let (gas_price, priority_fee) = match signer.chain_id() { +// 1 => calculate_eth_mainnet_gas(provider)?, +// 8453 => calculate_base_gas(provider)?, +// 10 => calculate_optimism_gas(provider)?, +// _ => { +// let base_fee = provider.get_gas_price()?.to::(); +// let adjusted_fee = (base_fee * 130) / 100; +// (adjusted_fee, adjusted_fee / 10) +// } +// }; +// +// // Prepare transaction data +// let tx_data = TransactionData { +// to: token, +// value: U256::ZERO, // No ETH sent with token transfers +// data: Some(call_data), +// nonce, +// gas_limit, +// gas_price, +// max_priority_fee: Some(priority_fee), +// chain_id: signer.chain_id(), +// }; +// +// // Sign and send transaction +// let signed_tx = signer.sign_transaction(&tx_data)?; +// let tx_hash = provider.send_raw_transaction(signed_tx.into())?; +// +// // Get token details to improve the receipt message +// let token_symbol = match erc20_symbol(token_address, provider) { +// Ok(symbol) => symbol, +// Err(_) => "tokens".to_string(), +// }; +// +// let token_decimals = match erc20_decimals(token_address, provider) { +// Ok(decimals) => decimals, +// Err(_) => 18, // Assume 18 decimals if unavailable +// }; +// +// // Format amount in token units +// let amount_float = amount.to::() as f64 / 10f64.powi(token_decimals as i32); +// +// Ok(TxReceipt { +// hash: tx_hash, +// details: format!("Transferred {:.6} {} to {}", amount_float, token_symbol, to_address), +// }) +//} +// +///// Approve an address to spend ERC20 tokens +//pub fn erc20_approve( +// token_address: &str, +// spender_address: &str, +// amount: U256, +// provider: &Provider, +// signer: &S +//) -> Result { +// kiprintln!("PROCESS_LIB::erc20_approve starting"); +// +// // Resolve addresses +// let token = resolve_name(token_address, provider.chain_id)?; +// let spender = resolve_name(spender_address, provider.chain_id)?; +// +// // Create the call using sol! macro generated struct +// let call = IERC20::approveCall { spender, value: amount }; +// let call_data = call.abi_encode(); +// +// // Get the current nonce for the signer's address +// let from_address = signer.address(); +// let nonce = provider.get_transaction_count(from_address, None)? +// .to::(); +// +// // Estimate gas +// let tx_req = TransactionRequest { +// from: Some(from_address), +// to: Some(TxKind::Call(token)), +// input: call_data.clone().into(), +// ..Default::default() +// }; +// +// // Try to estimate gas, fall back to 60,000 if estimation fails +// let gas_limit = match provider.estimate_gas(tx_req, None) { +// Ok(gas) => (gas.to::() * 120) / 100, // Add 20% buffer +// Err(_) => 60_000, // Default gas limit for ERC20 approvals +// }; +// +// // Calculate gas price based on the chain +// let (gas_price, priority_fee) = match signer.chain_id() { +// 1 => calculate_eth_mainnet_gas(provider)?, +// 8453 => calculate_base_gas(provider)?, +// 10 => calculate_optimism_gas(provider)?, +// _ => { +// let base_fee = provider.get_gas_price()?.to::(); +// let adjusted_fee = (base_fee * 130) / 100; +// (adjusted_fee, adjusted_fee / 10) +// } +// }; +// +// // Prepare transaction data +// let tx_data = TransactionData { +// to: token, +// value: U256::ZERO, // No ETH sent with token approvals +// data: Some(call_data), +// nonce, +// gas_limit, +// gas_price, +// max_priority_fee: Some(priority_fee), +// chain_id: signer.chain_id(), +// }; +// +// // Sign and send transaction +// let signed_tx = signer.sign_transaction(&tx_data)?; +// let tx_hash = provider.send_raw_transaction(signed_tx.into())?; +// +// // Get token details to improve the receipt message +// let token_symbol = match erc20_symbol(token_address, provider) { +// Ok(symbol) => symbol, +// Err(_) => "tokens".to_string(), +// }; +// +// let token_decimals = match erc20_decimals(token_address, provider) { +// Ok(decimals) => decimals, +// Err(_) => 18, // Assume 18 decimals if unavailable +// }; +// +// // Format amount in token units +// let amount_float = amount.to::() as f64 / 10f64.powi(token_decimals as i32); +// +// Ok(TxReceipt { +// hash: tx_hash, +// details: format!("Approved {:.6} {} to be spent by {}", amount_float, token_symbol, spender_address), +// }) +//} +// +///// Transfer ERC20 tokens from one address to another (requires approval) +//pub fn erc20_transfer_from( +// token_address: &str, +// from_address: &str, +// to_address: &str, +// amount: U256, +// provider: &Provider, +// signer: &S +//) -> Result { +// kiprintln!("PROCESS_LIB::erc20_transfer_from starting"); +// +// // Resolve addresses +// let token = resolve_name(token_address, provider.chain_id)?; +// let from = resolve_name(from_address, provider.chain_id)?; +// let to = resolve_name(to_address, provider.chain_id)?; +// +// // Create the call using sol! macro generated struct +// let call = IERC20::transferFromCall { from, to, value: amount }; +// let call_data = call.abi_encode(); +// +// // Get the current nonce for the signer's address +// let signer_address = signer.address(); +// let nonce = provider.get_transaction_count(signer_address, None)? +// .to::(); +// +// // Estimate gas +// let tx_req = TransactionRequest { +// from: Some(signer_address), +// to: Some(TxKind::Call(token)), +// input: call_data.clone().into(), +// ..Default::default() +// }; +// +// // Try to estimate gas, fall back to 100,000 if estimation fails +// let gas_limit = match provider.estimate_gas(tx_req, None) { +// Ok(gas) => (gas.to::() * 120) / 100, // Add 20% buffer +// Err(_) => 100_000, // Default gas limit +// }; +// +// // Calculate gas price based on the chain +// let (gas_price, priority_fee) = match signer.chain_id() { +// 1 => calculate_eth_mainnet_gas(provider)?, +// 8453 => calculate_base_gas(provider)?, +// 10 => calculate_optimism_gas(provider)?, +// _ => { +// let base_fee = provider.get_gas_price()?.to::(); +// let adjusted_fee = (base_fee * 130) / 100; +// (adjusted_fee, adjusted_fee / 10) +// } +// }; +// +// // Prepare transaction data +// let tx_data = TransactionData { +// to: token, +// value: U256::ZERO, // No ETH sent with token transfers +// data: Some(call_data), +// nonce, +// gas_limit, +// gas_price, +// max_priority_fee: Some(priority_fee), +// chain_id: signer.chain_id(), +// }; +// +// // Sign and send transaction +// let signed_tx = signer.sign_transaction(&tx_data)?; +// let tx_hash = provider.send_raw_transaction(signed_tx.into())?; +// +// // Get token details to improve the receipt message +// let token_symbol = match erc20_symbol(token_address, provider) { +// Ok(symbol) => symbol, +// Err(_) => "tokens".to_string(), +// }; +// +// let token_decimals = match erc20_decimals(token_address, provider) { +// Ok(decimals) => decimals, +// Err(_) => 18, // Assume 18 decimals if unavailable +// }; +// +// // Format amount in token units +// let amount_float = amount.to::() as f64 / 10f64.powi(token_decimals as i32); +// +// Ok(TxReceipt { +// hash: tx_hash, +// details: format!("Transferred {:.6} {} from {} to {}", +// amount_float, token_symbol, from_address, to_address), +// }) +//} +// +//// +//// HYPERMAP AND ERC-6551 FUNCTIONS +//// +// +///// Result type for Hypermap transactions +//#[derive(Debug, Clone)] +//pub struct HypermapTxReceipt { +// /// Transaction hash +// pub hash: TxHash, +// /// Description of the operation +// pub description: String, +//} +// +///// Create a note (mutable data) on a Hypermap namespace entry +//pub fn create_note( +// parent_entry: &str, +// note_key: &str, +// data: Vec, +// provider: Provider, +// signer: &S, +//) -> Result { +// // Verify the note key is valid +// if !valid_note(note_key) { +// return Err(WalletError::NameResolutionError( +// format!("Invalid note key '{}'. Must start with '~' and contain only lowercase letters, numbers, and hyphens", note_key) +// )); +// } +// +// // Get the parent TBA address +// let hypermap = provider.hypermap(); +// let parent_hash_str = namehash(parent_entry); +// +// println!("Parent entry: {}", parent_entry); +// println!("Parent hash: {}", parent_hash_str); +// +// let (tba, owner, _) = hypermap.get_hash(&parent_hash_str)?; +// +// println!("TBA address (parent): {}", tba); +// println!("Owner address: {}", owner); +// +// // Check that the signer is the owner of the parent entry +// let signer_address = signer.address(); +// println!("Signer address: {}", signer_address); +// +// if signer_address != owner { +// return Err(WalletError::PermissionDenied( +// format!("Signer address {} does not own the parent entry {}", signer_address, parent_entry) +// )); +// } +// +// // Get the hypermap contract address +// let hypermap_address = *hypermap.address(); +// println!("Hypermap contract address: {}", hypermap_address); +// +// // Create the note function call data +// let note_function = hypermap::contract::noteCall { +// note: Bytes::from(note_key.as_bytes().to_vec()), +// data: Bytes::from(data), +// }; +// let note_call_data = note_function.abi_encode(); +// +// // Now create an ERC-6551 execute call to send from the wallet to the TBA +// let execute_call = IERC6551Account::executeCall { +// to: hypermap_address, +// value: U256::ZERO, +// data: Bytes::from(note_call_data), +// operation: 0 // CALL operation +// }; +// let execute_call_data = execute_call.abi_encode(); +// +// // Send the transaction from the wallet to the TBA +// let (tx_hash, _) = send_transaction( +// tba, +// execute_call_data.into(), +// U256::ZERO, +// provider, +// signer +// )?; +// +// // Return the receipt with transaction details +// Ok(HypermapTxReceipt { +// hash: tx_hash, +// description: format!("Created note '{}' on '{}'", note_key, parent_entry), +// }) +//} +// +///// Create a fact (immutable data) on a Hypermap namespace entry +//pub fn create_fact( +// parent_entry: &str, +// fact_key: &str, +// data: Vec, +// provider: Provider, +// signer: &S, +//) -> Result { +// // Verify the fact key is valid +// if !valid_fact(fact_key) { +// return Err(WalletError::NameResolutionError( +// format!("Invalid fact key '{}'. Must start with '!' and contain only lowercase letters, numbers, and hyphens", fact_key) +// )); +// } +// +// // Get the parent TBA address +// let hypermap = provider.hypermap(); +// let parent_hash_str = namehash(parent_entry); +// let (tba, owner, _) = hypermap.get_hash(&parent_hash_str)?; +// +// // Check that the signer is the owner of the parent entry +// let signer_address = signer.address(); +// if signer_address != owner { +// return Err(WalletError::PermissionDenied( +// format!("Signer address {} does not own the parent entry {}", signer_address, parent_entry) +// )); +// } +// +// // Get the hypermap contract address +// let hypermap_address = *hypermap.address(); +// +// // Create the fact function call data +// let fact_function = hypermap::contract::factCall { +// fact: Bytes::from(fact_key.as_bytes().to_vec()), +// data: Bytes::from(data), +// }; +// let fact_call_data = fact_function.abi_encode(); +// +// // Create an ERC-6551 execute call +// let execute_call = IERC6551Account::executeCall { +// to: hypermap_address, +// value: U256::ZERO, +// data: Bytes::from(fact_call_data), +// operation: 0 // CALL operation +// }; +// let execute_call_data = execute_call.abi_encode(); +// +// // Send the transaction +// let (tx_hash, _) = send_transaction( +// tba, +// execute_call_data.into(), +// U256::ZERO, +// provider, +// signer +// )?; +// +// // Return the receipt with transaction details +// Ok(HypermapTxReceipt { +// hash: tx_hash, +// description: format!("Created fact '{}' on '{}'", fact_key, parent_entry), +// }) +//} +// +///// Mint a new namespace entry under a parent entry +//pub fn mint_entry( +// parent_entry: &str, +// label: &str, +// recipient: &str, +// implementation: &str, +// provider: Provider, +// signer: &S, +//) -> Result { +// // Verify the label is valid +// if !valid_name(label) { +// return Err(WalletError::NameResolutionError( +// format!("Invalid label '{}'. Must contain only lowercase letters, numbers, and hyphens", label) +// )); +// } +// +// // Get the parent TBA address +// let hypermap = provider.hypermap(); +// let parent_hash_str = namehash(parent_entry); +// kiprintln!("PROCESS_LIB::mint_entry parent_hash_str: {}", parent_hash_str); +// let (tba, owner, _) = hypermap.get_hash(&parent_hash_str)?; +// +// // Check that the signer is the owner of the parent entry +// let signer_address = signer.address(); +// if signer_address != owner { +// return Err(WalletError::PermissionDenied( +// format!("Signer address {} does not own the parent entry {}", signer_address, parent_entry) +// )); +// } +// +// // Resolve recipient address +// let recipient_address = resolve_name(recipient, provider.chain_id)?; +// +// // Resolve implementation address +// let implementation_address = resolve_name(implementation, provider.chain_id)?; +// +// // Get the hypermap contract address +// let hypermap_address = *hypermap.address(); +// +// // Create the mint function call data +// let mint_function = hypermap::contract::mintCall { +// who: recipient_address, +// label: Bytes::from(label.as_bytes().to_vec()), +// initialization: Bytes::default(), // No initialization data +// erc721Data: Bytes::default(), // No ERC721 data +// implementation: implementation_address, +// }; +// let mint_call_data = mint_function.abi_encode(); +// +// // Create an ERC-6551 execute call +// let execute_call = IERC6551Account::executeCall { +// to: hypermap_address, +// value: U256::ZERO, +// data: Bytes::from(mint_call_data), +// operation: 0 // CALL operation +// }; +// let execute_call_data = execute_call.abi_encode(); +// +// kiprintln!("Parent entry: {}", parent_entry); +// kiprintln!("Parent hash: {}", parent_hash_str); +// kiprintln!("TBA address: {}", tba); +// kiprintln!("Owner address: {}", owner); +// kiprintln!("Signer address: {}", signer_address); +// +// // Send the transaction +// let (tx_hash, _) = send_transaction( +// tba, +// execute_call_data.into(), +// U256::ZERO, +// provider, +// signer +// )?; +// +// // Return the receipt with transaction details +// Ok(HypermapTxReceipt { +// hash: tx_hash, +// description: format!("Minted new entry '{}' under '{}'", label, parent_entry), +// }) +//} +// +///// Set the gene for a namespace entry +//pub fn set_gene( +// entry: &str, +// gene_implementation: &str, +// provider: Provider, +// signer: &S, +//) -> Result { +// // Get the entry's TBA address +// let hypermap = provider.hypermap(); +// let entry_hash_str = namehash(entry); +// let (tba, owner, _) = hypermap.get_hash(&entry_hash_str)?; +// +// // Check that the signer is the owner of the entry +// let signer_address = signer.address(); +// if signer_address != owner { +// return Err(WalletError::PermissionDenied( +// format!("Signer address {} does not own the entry {}", signer_address, entry) +// )); +// } +// +// // Resolve gene implementation address +// let gene_address = resolve_name(gene_implementation, provider.chain_id)?; +// +// // Get the hypermap contract address +// let hypermap_address = *hypermap.address(); +// +// // Create the gene function call data +// let gene_function = hypermap::contract::geneCall { +// _gene: gene_address, +// }; +// let gene_call_data = gene_function.abi_encode(); +// +// // Create an ERC-6551 execute call +// let execute_call = IERC6551Account::executeCall { +// to: hypermap_address, +// value: U256::ZERO, +// data: Bytes::from(gene_call_data), +// operation: 0 // CALL operation +// }; +// let execute_call_data = execute_call.abi_encode(); +// +// // Send the transaction +// let (tx_hash, _) = send_transaction( +// tba, +// execute_call_data.into(), +// U256::ZERO, +// provider, +// signer +// )?; +// +// // Return the receipt with transaction details +// Ok(HypermapTxReceipt { +// hash: tx_hash, +// description: format!("Set gene for '{}' to '{}'", entry, gene_implementation), +// }) +//} +// +///// Send a transaction to an address with custom data and value +//fn send_transaction( +// to: EthAddress, +// data: Bytes, +// value: U256, +// provider: Provider, +// signer: &S, +//) -> Result<(TxHash, Vec), WalletError> { +// let chain_id = signer.chain_id(); +// +// kiprintln!("PROCESS_LIB::send_transaction starting"); +// kiprintln!("PROCESS_LIB::send_transaction chain_id: {}", chain_id); +// +// // Get gas estimates - use 50% buffer for Base to ensure acceptance +// let base_fee = provider.get_gas_price()?.to::(); +// let gas_price = (base_fee * 150) / 100; // 50% buffer +// let priority_fee = gas_price / 5; // 20% of gas price +// +// kiprintln!("PROCESS_LIB::send_transaction base_fee: {}, priority_fee: {}", base_fee, priority_fee); +// kiprintln!("PROCESS_LIB::send_transaction gas_price: {}", gas_price); +// +// // Get the current nonce for the signer's address +// let from_address = signer.address(); +// let nonce = provider.get_transaction_count(from_address, None)? +// .to::(); +// +// kiprintln!("PROCESS_LIB::send_transaction nonce: {}", nonce); +// +// // For ERC-6551 account operations, use a higher gas limit +// // The ERC-6551 execute function is complex and gas-intensive +// let estimated_gas = 500_000; // Start high for ERC-6551 +// +// // Add 50% buffer to estimated gas since this is a complex operation +// let gas_limit = (estimated_gas * 150) / 100; +// +// kiprintln!("PROCESS_LIB::send_transaction estimated_gas: {}", estimated_gas); +// kiprintln!("PROCESS_LIB::send_transaction gas_limit: {}", gas_limit); +// +// // Prepare transaction data +// let tx_data = TransactionData { +// to, +// value, +// data: Some(data.to_vec()), +// nonce, +// gas_limit, +// gas_price, +// max_priority_fee: Some(priority_fee), +// chain_id, +// }; +// +// // Sign the transaction +// let signed_tx = signer.sign_transaction(&tx_data)?; +// kiprintln!("PROCESS_LIB::send_transaction signed"); +// +// // Send the transaction +// let tx_hash = provider.send_raw_transaction(signed_tx.clone().into())?; +// kiprintln!("PROCESS_LIB::send_transaction tx_hash: {}", tx_hash); +// +// // Return both the hash and the raw transaction data +// Ok((tx_hash, signed_tx)) +//} +// +//// +//// ERC-721 NFT FUNCTIONS +//// +// +///// Get the NFT balance of an address +//pub fn erc721_balance_of( +// token_address: &str, +// owner_address: &str, +// provider: &Provider +//) -> Result { +// kiprintln!("PROCESS_LIB::erc721_balance_of starting"); +// +// // Resolve addresses +// let token = resolve_name(token_address, provider.chain_id)?; +// let owner = resolve_name(owner_address, provider.chain_id)?; +// +// // Create the call using sol! macro generated struct +// let call = IERC721::balanceOfCall { owner }; +// let call_data = call.abi_encode(); +// +// // Create the transaction request +// let tx = TransactionRequest { +// to: Some(TxKind::Call(token)), +// input: call_data.into(), +// ..Default::default() +// }; +// +// // Call the contract +// let result = provider.call(tx, None)?; +// +// // Verify we got a valid result +// if result.is_empty() { +// return Err(WalletError::TransactionError("Empty result from balanceOf call".into())); +// } +// +// // Decode the result +// let balance = match IERC721::balanceOfCall::abi_decode_returns(&result, true) { +// Ok(decoded) => decoded._0, // Returns (U256,) +// Err(e) => return Err(WalletError::TransactionError( +// format!("Failed to decode balanceOf result: {}", e) +// )), +// }; +// +// Ok(balance) +//} +// +///// Get the owner of an NFT token +//pub fn erc721_owner_of( +// token_address: &str, +// token_id: U256, +// provider: &Provider +//) -> Result { +// kiprintln!("PROCESS_LIB::erc721_owner_of starting"); +// +// // Resolve token address +// let token = resolve_name(token_address, provider.chain_id)?; +// +// // Create the call using sol! macro generated struct +// let call = IERC721::ownerOfCall { tokenId: token_id }; +// let call_data = call.abi_encode(); +// +// // Create the transaction request +// let tx = TransactionRequest { +// to: Some(TxKind::Call(token)), +// input: call_data.into(), +// ..Default::default() +// }; +// +// // Call the contract +// let result = provider.call(tx, None)?; +// +// // Verify we got a valid result +// if result.is_empty() { +// return Err(WalletError::TransactionError("Empty result from ownerOf call".into())); +// } +// +// // Decode the result +// let owner = match IERC721::ownerOfCall::abi_decode_returns(&result, true) { +// Ok(decoded) => decoded._0, // Returns (address,) +// Err(e) => return Err(WalletError::TransactionError( +// format!("Failed to decode ownerOf result: {}", e) +// )), +// }; +// +// Ok(owner) +//} +// +///// Check if an operator is approved for all NFTs of an owner +//pub fn erc721_is_approved_for_all( +// token_address: &str, +// owner_address: &str, +// operator_address: &str, +// provider: &Provider +//) -> Result { +// kiprintln!("PROCESS_LIB::erc721_is_approved_for_all starting"); +// +// // Resolve addresses +// let token = resolve_name(token_address, provider.chain_id)?; +// let owner = resolve_name(owner_address, provider.chain_id)?; +// let operator = resolve_name(operator_address, provider.chain_id)?; +// +// // Create the call using sol! macro generated struct +// let call = IERC721::isApprovedForAllCall { owner, operator }; +// let call_data = call.abi_encode(); +// +// // Create the transaction request +// let tx = TransactionRequest { +// to: Some(TxKind::Call(token)), +// input: call_data.into(), +// ..Default::default() +// }; +// +// // Call the contract +// let result = provider.call(tx, None)?; +// +// // Verify we got a valid result +// if result.is_empty() { +// return Err(WalletError::TransactionError("Empty result from isApprovedForAll call".into())); +// } +// +// // Decode the result +// let is_approved = match IERC721::isApprovedForAllCall::abi_decode_returns(&result, true) { +// Ok(decoded) => decoded._0, // Returns (bool,) +// Err(e) => return Err(WalletError::TransactionError( +// format!("Failed to decode isApprovedForAll result: {}", e) +// )), +// }; +// +// Ok(is_approved) +//} +// +///// Safely transfer an NFT +//pub fn erc721_safe_transfer_from( +// token_address: &str, +// from_address: &str, +// to_address: &str, +// token_id: U256, +// provider: &Provider, +// signer: &S +//) -> Result { +// kiprintln!("PROCESS_LIB::erc721_safe_transfer_from starting"); +// +// // Resolve addresses +// let token = resolve_name(token_address, provider.chain_id)?; +// let from = resolve_name(from_address, provider.chain_id)?; +// let to = resolve_name(to_address, provider.chain_id)?; +// +// // Create the call using sol! macro generated struct +// let call = IERC721::safeTransferFromCall { from, to, tokenId: token_id }; +// let call_data = call.abi_encode(); +// +// // Get the current nonce for the signer's address +// let signer_address = signer.address(); +// let nonce = provider.get_transaction_count(signer_address, None)? +// .to::(); +// +// // Estimate gas +// let tx_req = TransactionRequest { +// from: Some(signer_address), +// to: Some(TxKind::Call(token)), +// input: call_data.clone().into(), +// ..Default::default() +// }; +// +// // Try to estimate gas, fall back to 200,000 if estimation fails +// let gas_limit = match provider.estimate_gas(tx_req, None) { +// Ok(gas) => (gas.to::() * 120) / 100, // Add 20% buffer +// Err(_) => 200_000, // Default gas limit for NFT transfers (higher than ERC20) +// }; +// +// // Calculate gas price based on the chain +// let (gas_price, priority_fee) = match signer.chain_id() { +// 1 => calculate_eth_mainnet_gas(provider)?, +// 8453 => calculate_base_gas(provider)?, +// 10 => calculate_optimism_gas(provider)?, +// _ => { +// let base_fee = provider.get_gas_price()?.to::(); +// let adjusted_fee = (base_fee * 130) / 100; +// (adjusted_fee, adjusted_fee / 10) +// } +// }; +// +// // Prepare transaction data +// let tx_data = TransactionData { +// to: token, +// value: U256::ZERO, // No ETH sent with NFT transfers +// data: Some(call_data), +// nonce, +// gas_limit, +// gas_price, +// max_priority_fee: Some(priority_fee), +// chain_id: signer.chain_id(), +// }; +// +// // Sign and send transaction +// let signed_tx = signer.sign_transaction(&tx_data)?; +// let tx_hash = provider.send_raw_transaction(signed_tx.into())?; +// +// Ok(TxReceipt { +// hash: tx_hash, +// details: format!("Safely transferred NFT ID {} from {} to {}", token_id, from_address, to_address), +// }) +//} +// +///// Set approval for all tokens to an operator +//pub fn erc721_set_approval_for_all( +// token_address: &str, +// operator_address: &str, +// approved: bool, +// provider: &Provider, +// signer: &S +//) -> Result { +// kiprintln!("PROCESS_LIB::erc721_set_approval_for_all starting"); +// +// // Resolve addresses +// let token = resolve_name(token_address, provider.chain_id)?; +// let operator = resolve_name(operator_address, provider.chain_id)?; +// +// // Create the call using sol! macro generated struct +// let call = IERC721::setApprovalForAllCall { operator, approved }; +// let call_data = call.abi_encode(); +// +// // Get the current nonce for the signer's address +// let signer_address = signer.address(); +// let nonce = provider.get_transaction_count(signer_address, None)? +// .to::(); +// +// // Estimate gas +// let tx_req = TransactionRequest { +// from: Some(signer_address), +// to: Some(TxKind::Call(token)), +// input: call_data.clone().into(), +// ..Default::default() +// }; +// +// // Try to estimate gas, fall back to 60,000 if estimation fails +// let gas_limit = match provider.estimate_gas(tx_req, None) { +// Ok(gas) => (gas.to::() * 120) / 100, // Add 20% buffer +// Err(_) => 60_000, // Default gas limit +// }; +// +// // Calculate gas price based on the chain +// let (gas_price, priority_fee) = match signer.chain_id() { +// 1 => calculate_eth_mainnet_gas(provider)?, +// 8453 => calculate_base_gas(provider)?, +// 10 => calculate_optimism_gas(provider)?, +// _ => { +// let base_fee = provider.get_gas_price()?.to::(); +// let adjusted_fee = (base_fee * 130) / 100; +// (adjusted_fee, adjusted_fee / 10) +// } +// }; +// +// // Prepare transaction data +// let tx_data = TransactionData { +// to: token, +// value: U256::ZERO, +// data: Some(call_data), +// nonce, +// gas_limit, +// gas_price, +// max_priority_fee: Some(priority_fee), +// chain_id: signer.chain_id(), +// }; +// +// // Sign and send transaction +// let signed_tx = signer.sign_transaction(&tx_data)?; +// let tx_hash = provider.send_raw_transaction(signed_tx.into())?; +// +// Ok(TxReceipt { +// hash: tx_hash, +// details: format!( +// "{} operator {} to manage all of your NFTs in contract {}", +// if approved { "Approved" } else { "Revoked approval for" }, +// operator_address, +// token_address +// ), +// }) +//} +// +//// +//// ERC-1155 MULTI-TOKEN FUNCTIONS +//// +// +///// Get the balance of a specific token ID for an account +//pub fn erc1155_balance_of( +// token_address: &str, +// account_address: &str, +// token_id: U256, +// provider: &Provider +//) -> Result { +// kiprintln!("PROCESS_LIB::erc1155_balance_of starting"); +// +// // Resolve addresses +// let token = resolve_name(token_address, provider.chain_id)?; +// let account = resolve_name(account_address, provider.chain_id)?; +// +// // Create the call using sol! macro generated struct +// let call = IERC1155::balanceOfCall { account, id: token_id }; +// let call_data = call.abi_encode(); +// +// // Create the transaction request +// let tx = TransactionRequest { +// to: Some(TxKind::Call(token)), +// input: call_data.into(), +// ..Default::default() +// }; +// +// // Call the contract +// let result = provider.call(tx, None)?; +// +// // Verify we got a valid result +// if result.is_empty() { +// return Err(WalletError::TransactionError("Empty result from balanceOf call".into())); +// } +// +// // Decode the result +// let balance = match IERC1155::balanceOfCall::abi_decode_returns(&result, true) { +// Ok(decoded) => decoded._0, // Returns (U256,) +// Err(e) => return Err(WalletError::TransactionError( +// format!("Failed to decode balanceOf result: {}", e) +// )), +// }; +// +// Ok(balance) +//} +// +///// Get balances for multiple accounts and token IDs +//pub fn erc1155_balance_of_batch( +// token_address: &str, +// account_addresses: Vec<&str>, +// token_ids: Vec, +// provider: &Provider +//) -> Result, WalletError> { +// kiprintln!("PROCESS_LIB::erc1155_balance_of_batch starting"); +// +// // Check that arrays are same length +// if account_addresses.len() != token_ids.len() { +// return Err(WalletError::TransactionError( +// "Arrays of accounts and token IDs must have the same length".into() +// )); +// } +// +// // Resolve token address +// let token = resolve_name(token_address, provider.chain_id)?; +// +// // Resolve all account addresses +// let mut accounts = Vec::with_capacity(account_addresses.len()); +// for addr in account_addresses { +// accounts.push(resolve_name(addr, provider.chain_id)?); +// } +// +// // Create the call using sol! macro generated struct +// let call = IERC1155::balanceOfBatchCall { accounts, ids: token_ids }; +// let call_data = call.abi_encode(); +// +// // Create the transaction request +// let tx = TransactionRequest { +// to: Some(TxKind::Call(token)), +// input: call_data.into(), +// ..Default::default() +// }; +// +// // Call the contract +// let result = provider.call(tx, None)?; +// +// // Verify we got a valid result +// if result.is_empty() { +// return Err(WalletError::TransactionError("Empty result from balanceOfBatch call".into())); +// } +// +// // Decode the result +// let balances = match IERC1155::balanceOfBatchCall::abi_decode_returns(&result, true) { +// Ok(decoded) => decoded._0, // Returns (Vec,) +// Err(e) => return Err(WalletError::TransactionError( +// format!("Failed to decode balanceOfBatch result: {}", e) +// )), +// }; +// +// Ok(balances) +//} +// +///// Check if an operator is approved for all tokens of an account +//pub fn erc1155_is_approved_for_all( +// token_address: &str, +// account_address: &str, +// operator_address: &str, +// provider: &Provider +//) -> Result { +// kiprintln!("PROCESS_LIB::erc1155_is_approved_for_all starting"); +// +// // Resolve addresses +// let token = resolve_name(token_address, provider.chain_id)?; +// let account = resolve_name(account_address, provider.chain_id)?; +// let operator = resolve_name(operator_address, provider.chain_id)?; +// +// // Create the call using sol! macro generated struct +// let call = IERC1155::isApprovedForAllCall { account, operator }; +// let call_data = call.abi_encode(); +// +// // Create the transaction request +// let tx = TransactionRequest { +// to: Some(TxKind::Call(token)), +// input: call_data.into(), +// ..Default::default() +// }; +// +// // Call the contract +// let result = provider.call(tx, None)?; +// +// // Verify we got a valid result +// if result.is_empty() { +// return Err(WalletError::TransactionError("Empty result from isApprovedForAll call".into())); +// } +// +// // Decode the result +// let is_approved = match IERC1155::isApprovedForAllCall::abi_decode_returns(&result, true) { +// Ok(decoded) => decoded._0, // Returns (bool,) +// Err(e) => return Err(WalletError::TransactionError( +// format!("Failed to decode isApprovedForAll result: {}", e) +// )), +// }; +// +// Ok(is_approved) +//} +// +///// Set approval for all tokens to an operator +//pub fn erc1155_set_approval_for_all( +// token_address: &str, +// operator_address: &str, +// approved: bool, +// provider: &Provider, +// signer: &S +//) -> Result { +// kiprintln!("PROCESS_LIB::erc1155_set_approval_for_all starting"); +// +// // Resolve addresses +// let token = resolve_name(token_address, provider.chain_id)?; +// let operator = resolve_name(operator_address, provider.chain_id)?; +// +// // Create the call using sol! macro generated struct +// let call = IERC1155::setApprovalForAllCall { operator, approved }; +// let call_data = call.abi_encode(); +// +// // Get the current nonce for the signer's address +// let signer_address = signer.address(); +// let nonce = provider.get_transaction_count(signer_address, None)? +// .to::(); +// +// // Estimate gas +// let tx_req = TransactionRequest { +// from: Some(signer_address), +// to: Some(TxKind::Call(token)), +// input: call_data.clone().into(), +// ..Default::default() +// }; +// +// // Try to estimate gas, fall back to 60,000 if estimation fails +// let gas_limit = match provider.estimate_gas(tx_req, None) { +// Ok(gas) => (gas.to::() * 120) / 100, // Add 20% buffer +// Err(_) => 60_000, // Default gas limit +// }; +// +// // Calculate gas price based on the chain +// let (gas_price, priority_fee) = match signer.chain_id() { +// 1 => calculate_eth_mainnet_gas(provider)?, +// 8453 => calculate_base_gas(provider)?, +// 10 => calculate_optimism_gas(provider)?, +// _ => { +// let base_fee = provider.get_gas_price()?.to::(); +// let adjusted_fee = (base_fee * 130) / 100; +// (adjusted_fee, adjusted_fee / 10) +// } +// }; +// +// // Prepare transaction data +// let tx_data = TransactionData { +// to: token, +// value: U256::ZERO, +// data: Some(call_data), +// nonce, +// gas_limit, +// gas_price, +// max_priority_fee: Some(priority_fee), +// chain_id: signer.chain_id(), +// }; +// +// // Sign and send transaction +// let signed_tx = signer.sign_transaction(&tx_data)?; +// let tx_hash = provider.send_raw_transaction(signed_tx.into())?; +// +// Ok(TxReceipt { +// hash: tx_hash, +// details: format!( +// "{} operator {} to manage all of your ERC1155 tokens in contract {}", +// if approved { "Approved" } else { "Revoked approval for" }, +// operator_address, +// token_address +// ), +// }) +//} +// +///// Transfer a single ERC1155 token +//pub fn erc1155_safe_transfer_from( +// token_address: &str, +// from_address: &str, +// to_address: &str, +// token_id: U256, +// amount: U256, +// data: Vec, +// provider: &Provider, +// signer: &S +//) -> Result { +// kiprintln!("PROCESS_LIB::erc1155_safe_transfer_from starting"); +// +// // Resolve addresses +// let token = resolve_name(token_address, provider.chain_id)?; +// let from = resolve_name(from_address, provider.chain_id)?; +// let to = resolve_name(to_address, provider.chain_id)?; +// +// // Create the call using sol! macro generated struct +// let call = IERC1155::safeTransferFromCall { +// from, +// to, +// id: token_id, +// amount, +// data: Bytes::from(data) +// }; +// let call_data = call.abi_encode(); +// +// // Get the current nonce for the signer's address +// let signer_address = signer.address(); +// let nonce = provider.get_transaction_count(signer_address, None)? +// .to::(); +// +// // Estimate gas +// let tx_req = TransactionRequest { +// from: Some(signer_address), +// to: Some(TxKind::Call(token)), +// input: call_data.clone().into(), +// ..Default::default() +// }; +// +// // Try to estimate gas, fall back to 150,000 if estimation fails +// let gas_limit = match provider.estimate_gas(tx_req, None) { +// Ok(gas) => (gas.to::() * 120) / 100, // Add 20% buffer +// Err(_) => 150_000, // Default gas limit +// }; +// +// // Calculate gas price based on the chain +// let (gas_price, priority_fee) = match signer.chain_id() { +// 1 => calculate_eth_mainnet_gas(provider)?, +// 8453 => calculate_base_gas(provider)?, +// 10 => calculate_optimism_gas(provider)?, +// _ => { +// let base_fee = provider.get_gas_price()?.to::(); +// let adjusted_fee = (base_fee * 130) / 100; +// (adjusted_fee, adjusted_fee / 10) +// } +// }; +// +// // Prepare transaction data +// let tx_data = TransactionData { +// to: token, +// value: U256::ZERO, +// data: Some(call_data), +// nonce, +// gas_limit, +// gas_price, +// max_priority_fee: Some(priority_fee), +// chain_id: signer.chain_id(), +// }; +// +// // Sign and send transaction +// let signed_tx = signer.sign_transaction(&tx_data)?; +// let tx_hash = provider.send_raw_transaction(signed_tx.into())?; +// +// Ok(TxReceipt { +// hash: tx_hash, +// details: format!( +// "Transferred {} of token ID {} from {} to {}", +// amount, token_id, from_address, to_address +// ), +// }) +//} +// +///// Batch transfer multiple ERC1155 tokens +//pub fn erc1155_safe_batch_transfer_from( +// token_address: &str, +// from_address: &str, +// to_address: &str, +// token_ids: Vec, +// amounts: Vec, +// data: Vec, +// provider: &Provider, +// signer: &S +//) -> Result { +// kiprintln!("PROCESS_LIB::erc1155_safe_batch_transfer_from starting"); +// +// // Check that arrays are same length +// if token_ids.len() != amounts.len() { +// return Err(WalletError::TransactionError( +// "Arrays of token IDs and amounts must have the same length".into() +// )); +// } +// +// // Resolve addresses +// let token = resolve_name(token_address, provider.chain_id)?; +// let from = resolve_name(from_address, provider.chain_id)?; +// let to = resolve_name(to_address, provider.chain_id)?; +// +// // Create the call using sol! macro generated struct +// let call = IERC1155::safeBatchTransferFromCall { +// from, +// to, +// ids: token_ids.clone(), +// amounts: amounts.clone(), +// data: Bytes::from(data) +// }; +// let call_data = call.abi_encode(); +// +// // Get the current nonce for the signer's address +// let signer_address = signer.address(); +// let nonce = provider.get_transaction_count(signer_address, None)? +// .to::(); +// +// // Estimate gas - this can be expensive, especially with many tokens +// let tx_req = TransactionRequest { +// from: Some(signer_address), +// to: Some(TxKind::Call(token)), +// input: call_data.clone().into(), +// ..Default::default() +// }; +// +// // Try to estimate gas, fall back to 200,000 + 50,000 per token if estimation fails +// let fallback_gas = 200_000 + (token_ids.len() as u64 * 50_000); +// let gas_limit = match provider.estimate_gas(tx_req, None) { +// Ok(gas) => (gas.to::() * 120) / 100, // Add 20% buffer +// Err(_) => fallback_gas, // Default gas limit that scales with number of tokens +// }; +// +// // Calculate gas price based on the chain +// let (gas_price, priority_fee) = match signer.chain_id() { +// 1 => calculate_eth_mainnet_gas(provider)?, +// 8453 => calculate_base_gas(provider)?, +// 10 => calculate_optimism_gas(provider)?, +// _ => { +// let base_fee = provider.get_gas_price()?.to::(); +// let adjusted_fee = (base_fee * 130) / 100; +// (adjusted_fee, adjusted_fee / 10) +// } +// }; +// +// // Prepare transaction data +// let tx_data = TransactionData { +// to: token, +// value: U256::ZERO, +// data: Some(call_data), +// nonce, +// gas_limit, +// gas_price, +// max_priority_fee: Some(priority_fee), +// chain_id: signer.chain_id(), +// }; +// +// // Sign and send transaction +// let signed_tx = signer.sign_transaction(&tx_data)?; +// let tx_hash = provider.send_raw_transaction(signed_tx.into())?; +// +// Ok(TxReceipt { +// hash: tx_hash, +// details: format!( +// "Batch transferred {} token types from {} to {}", +// token_ids.len(), from_address, to_address +// ), +// }) +//} + + + +////! Ethereum wallet functionality for Hyperware. +////! +////! This module provides higher-level wallet functionality, building on top of +////! the cryptographic operations in the signer module. It handles transaction +////! construction, name resolution, and account management. +////! +////! wallet module: +////! 1. Provides convenient transaction creation and submission +////! 2. Handles Hypermap name resolution +////! 3. Manages account state and balances +////! 4. Offers a simpler interface for common ETH operations (more to do here) +// +//use crate::eth::{ +// Provider, +// EthError, +// BlockNumberOrTag +//}; +//use crate::signer::{ +// Signer, +// LocalSigner, +// TransactionData, +// SignerError, +// EncryptedSignerData +//}; +//use crate::hypermap::{ +// namehash, +// valid_note, +// valid_fact, +// valid_name, +//}; +//use crate::{hypermap, kiprintln}; +// +//use thiserror::Error; +//use alloy_primitives::{ +// Address as EthAddress, +// TxHash, +// U256, +// Bytes +//}; +//use alloy::rpc::types::{ +// TransactionReceipt, +// TransactionRequest +//}; +//use alloy_primitives::TxKind; +//use std::str::FromStr; +//use alloy_sol_types::SolCall; +// +//#[derive(Debug, Error)] +//pub enum WalletError { +// #[error("signing error: {0}")] +// SignerError(#[from] SignerError), +// +// #[error("ethereum error: {0}")] +// EthError(#[from] EthError), +// +// #[error("name resolution error: {0}")] +// NameResolutionError(String), +// +// #[error("invalid amount: {0}")] +// InvalidAmount(String), +// +// #[error("transaction error: {0}")] +// TransactionError(String), +// +// #[error("gas estimation error: {0}")] +// GasEstimationError(String), +// +// #[error("insufficient funds: {0}")] +// InsufficientFunds(String), +// +// #[error("network congestion: {0}")] +// NetworkCongestion(String), +// +// #[error("transaction underpriced")] +// TransactionUnderpriced, +// +// #[error("transaction nonce too low")] +// TransactionNonceTooLow, +// +// #[error("permission denied: {0}")] +// PermissionDenied(String), +//} +// +///// Represents the storage state of a wallet's private key +//#[derive(Debug, Clone)] +//pub enum KeyStorage { +// /// An unencrypted wallet with a signer +// Decrypted(LocalSigner), +// +// /// An encrypted wallet - contains all the necessary data +// Encrypted(EncryptedSignerData), +//} +// +//impl KeyStorage { +// /// Get the encrypted data if this is an encrypted key storage +// pub fn get_encrypted_data(&self) -> Option> { +// match self { +// KeyStorage::Encrypted(data) => Some(data.encrypted_data.clone()), +// KeyStorage::Decrypted(_) => None, +// } +// } +// +// /// Get the address associated with this wallet +// pub fn get_address(&self) -> String { +// match self { +// KeyStorage::Decrypted(signer) => signer.address().to_string(), +// KeyStorage::Encrypted(data) => data.address.clone(), +// } +// } +// +// /// Get the chain ID associated with this wallet +// pub fn get_chain_id(&self) -> u64 { +// match self { +// KeyStorage::Decrypted(signer) => signer.chain_id(), +// KeyStorage::Encrypted(data) => data.chain_id, +// } +// } +//} +// +///// Represents an amount of ETH with proper formatting +//#[derive(Debug, Clone)] +//pub struct EthAmount { +// /// Value in wei +// wei_value: U256, +//} +// +//impl EthAmount { +// /// Create a new amount from ETH value +// pub fn from_eth(eth_value: f64) -> Self { +// // Convert ETH to wei (1 ETH = 10^18 wei) +// let wei = (eth_value * 1_000_000_000_000_000_000.0) as u128; +// Self { +// wei_value: U256::from(wei), +// } +// } +// +// /// Create from a string like "0.1 ETH" or "10 wei" +// pub fn from_string(amount_str: &str) -> Result { +// let parts: Vec<&str> = amount_str.trim().split_whitespace().collect(); +// +// if parts.is_empty() { +// return Err(WalletError::InvalidAmount( +// "Empty amount string".to_string() +// )); +// } +// +// let value_str = parts[0]; +// let unit = parts.get(1).map(|s| s.to_lowercase()).unwrap_or_else(|| "eth".to_string()); +// +// let value = value_str.parse::() +// .map_err(|_| WalletError::InvalidAmount(format!("Invalid numeric value: {}", value_str)))?; +// +// match unit.as_str() { +// "eth" => Ok(Self::from_eth(value)), +// "wei" => Ok(Self { +// wei_value: U256::from(value as u128), +// }), +// _ => Err(WalletError::InvalidAmount(format!("Unknown unit: {}", unit))), +// } +// } +// +// /// Get the value in wei +// pub fn as_wei(&self) -> U256 { +// self.wei_value +// } +// +// /// Get a human-readable string representation +// pub fn to_string(&self) -> String { +// // For values over 0.0001 ETH, show in ETH, otherwise in wei +// if self.wei_value >= U256::from(100_000_000_000_000u128) { +// // Convert to u128 first (safe since ETH total supply fits in u128) then to f64 +// let wei_u128 = self.wei_value.to::(); +// let eth_value = wei_u128 as f64 / 1_000_000_000_000_000_000.0; +// format!("{:.6} ETH", eth_value) +// } else { +// format!("{} wei", self.wei_value) +// } +// } +//} +// +///// Transaction receipt returned after sending +//#[derive(Debug, Clone)] +//pub struct TxReceipt { +// /// Transaction hash +// pub hash: TxHash, +// /// Transaction details +// pub details: String, +//} +// +//// The checks here aren't solid, but it works for now. Will also expand with full hypermap support +///// Resolve a .hypr name to an Ethereum address using Hypermap. +//pub fn resolve_name(name: &str, _chain_id: u64) -> Result { +// // If it's already an address, just parse it +// if name.starts_with("0x") && name.len() == 42 { +// return EthAddress::from_str(name) +// .map_err(|_| WalletError::NameResolutionError(format!("Invalid address format: {}", name))); +// } +// +// // hardcoded to .hypr for now +// let formatted_name = if !name.contains('.') { +// format!("{}.hypr", name) +// } else { +// kiprintln!("PROCESS_LIB::resolve_name name: {}", name); +// name.to_string() +// }; +// +// // Use hypermap resolution +// let hypermap = hypermap::Hypermap::default(60); +// match hypermap.get(&formatted_name) { +// Ok((_tba, owner, _)) => { +// Ok(owner) +// }, +// Err(e) => { +// Err(WalletError::NameResolutionError( +// format!("Failed to resolve name '{}': {}", name, e) +// )) +// } +// } +//} +// +///// Send ETH to an address or name +//pub fn send_eth( +// to: &str, +// amount: EthAmount, +// provider: Provider, +// signer: &S, +//) -> Result { +// +// kiprintln!("PROCESS_LIB::send_eth provider: {:#?}", provider); +// +// // Current chain-specific handling +// let chain_id = signer.chain_id(); +// kiprintln!("PROCESS_LIB::send_eth chain_id: {}", chain_id); +// +// // This part needs improvement - detect network type more robustly +// let is_test_network = chain_id == 31337 || chain_id == 1337; +// +// // Use network-specific gas strategies +// let (gas_price, priority_fee) = match chain_id { +// +// // just rough calculations for now +// 1 => calculate_eth_mainnet_gas(&provider)?, // mainnet +// 8453 => calculate_base_gas(&provider)?, // Base +// 10 => calculate_optimism_gas(&provider)?, // Optimism +// +// // Test networks - keep your current approach +// _ if is_test_network => (2_000_000_000, 100_000_000), +// +// // 30% +// _ => { +// kiprintln!("PROCESS_LIB::send_eth getting gas price"); +// let base_fee = provider.get_gas_price()?.to::(); +// kiprintln!("PROCESS_LIB::send_eth base_fee: {}", base_fee); +// let adjusted_fee = (base_fee * 130) / 100; +// kiprintln!("PROCESS_LIB::send_eth adjusted_fee: {}", adjusted_fee); +// (adjusted_fee, adjusted_fee / 10) +// } +// }; +// +// kiprintln!("PROCESS_LIB::send_eth gas_price: {}", gas_price); +// +// // Resolve the name to an address +// let to_address = resolve_name(to, chain_id)?; +// +// kiprintln!("PROCESS_LIB::send_eth to_address: {}", to_address); +// +// // Get the current nonce for the signer's address +// let from_address = signer.address(); +// let nonce = provider.get_transaction_count(from_address, None)? +// .to::(); +// +// kiprintln!("PROCESS_LIB::send_eth nonce: {}", nonce); +// +// // Standard gas limit for ETH transfer +// let gas_limit = 21000; +// +// // Prepare transaction data +// let tx_data = TransactionData { +// to: to_address, +// value: amount.as_wei(), +// data: None, // No data for simple ETH transfer +// nonce, +// gas_limit, +// gas_price, +// max_priority_fee: Some(priority_fee), +// chain_id, +// }; +// +// kiprintln!("PROCESS_LIB::send_eth tx_data: {:#?}", tx_data); +// +// // Sign the transaction +// let signed_tx = signer.sign_transaction(&tx_data)?; +// +// kiprintln!("PROCESS_LIB::send_eth signed_tx: {:?}", signed_tx); +// +// // Send the transaction +// let tx_hash = provider.send_raw_transaction(signed_tx.into())?; +// +// kiprintln!("lol PROCESS_LIB::send_eth tx_hash: {}", tx_hash); +// +// // Return the receipt with transaction details +// Ok(TxReceipt { +// hash: tx_hash, +// details: format!("Sent {} to {}", amount.to_string(), to), +// }) +//} +// +//// Helper function to calculate EIP-1559 gas parameters with network-specific values +//fn calculate_eip1559_gas( +// provider: &Provider, +// buffer_fraction: u128, +// priority_fee: u128 +//) -> Result<(u128, u128), WalletError> { +// kiprintln!("PROCESS_LIB::calculate_eip1559_gas provider\n", ); +// // Get latest block +// let latest_block = provider.get_block_by_number(BlockNumberOrTag::Latest, false)? +// .ok_or_else(|| WalletError::TransactionError("Failed to get latest block".into()))?; +// +// kiprintln!("PROCESS_LIB::calculate_eip1559_gas latest_block: {:#?}", latest_block); +// +// // Get base fee +// let base_fee = latest_block.header.inner.base_fee_per_gas +// .ok_or_else(|| WalletError::TransactionError("No base fee in block".into()))? +// as u128; +// +// kiprintln!("PROCESS_LIB::calculate_eip1559_gas base_fee: {}", base_fee); +// +// // Calculate max fee with the provided buffer fraction +// let max_fee = base_fee + (base_fee / buffer_fraction); +// +// kiprintln!("PROCESS_LIB::calculate_eip1559_gas max_fee: {}", max_fee); +// +// Ok((max_fee, priority_fee)) +//} +// +//// Network-specific gas calculation for Ethereum mainnet +//fn calculate_eth_mainnet_gas(provider: &Provider) -> Result<(u128, u128), WalletError> { +// // For mainnet: 50% buffer and 1.5 gwei priority fee +// calculate_eip1559_gas(provider, 2, 1_500_000_000u128) +//} +// +//fn calculate_base_gas(provider: &Provider) -> Result<(u128, u128), WalletError> { +// // Get the latest block to determine current gas conditions +// let latest_block = provider.get_block_by_number(BlockNumberOrTag::Latest, false)? +// .ok_or_else(|| WalletError::TransactionError("Failed to get latest block".into()))?; +// +// // Get base fee from the block +// let base_fee = latest_block.header.inner.base_fee_per_gas +// .ok_or_else(|| WalletError::TransactionError("No base fee in block".into()))? +// as u128; +// +// // Calculate max fee with a 33% buffer +// let max_fee = base_fee + (base_fee / 3); +// +// // Dynamic priority fee - 10% of base fee, but with a minimum and a maximum +// // Low minimum for Base which has very low gas prices +// let min_priority_fee = 100_000u128; // 0.0001 gwei minimum +// let max_priority_fee = max_fee / 2; // Never more than half the max fee +// +// let priority_fee = std::cmp::max( +// min_priority_fee, +// std::cmp::min(base_fee / 10, max_priority_fee) +// ); +// +// Ok((max_fee, priority_fee)) +//} +// +//// Gas calculation for Optimism network +//fn calculate_optimism_gas(provider: &Provider) -> Result<(u128, u128), WalletError> { +// // For Optimism: 25% buffer and 0.3 gwei priority fee +// calculate_eip1559_gas(provider, 4, 300_000_000u128) +//} +// +///// Get the ETH balance for an address or name +//pub fn get_balance( +// address_or_name: &str, +// chain_id: u64, +// provider: Provider, +//) -> Result { +// // Resolve name to address +// let address = resolve_name(address_or_name, chain_id)?; +// +// // Query balance +// let balance = provider.get_balance(address, None)?; +// +// // Return formatted amount +// Ok(EthAmount { +// wei_value: balance, +// }) +//} +// +//pub fn wait_for_transaction( +// tx_hash: TxHash, +// provider: Provider, +// confirmations: u64, +// timeout_secs: u64 +//) -> Result { +// let start_time = std::time::Instant::now(); +// let timeout = std::time::Duration::from_secs(timeout_secs); +// +// loop { +// // Check if we've exceeded the timeout +// if start_time.elapsed() > timeout { +// return Err(WalletError::TransactionError( +// format!("Transaction confirmation timeout after {} seconds", timeout_secs) +// )); +// } +// +// // Try to get the receipt +// if let Ok(Some(receipt)) = provider.get_transaction_receipt(tx_hash) { +// // Check if we have enough confirmations +// let latest_block = provider.get_block_number()?; +// let receipt_block = receipt.block_number.unwrap_or(0) as u64; +// +// if latest_block >= receipt_block + confirmations { +// return Ok(receipt); +// } +// } +// +// // Wait a bit before checking again +// std::thread::sleep(std::time::Duration::from_secs(2)); +// } +//} +// +////// Extract error information from RPC errors +////fn extract_rpc_error(error: &EthError) -> WalletError { +//// match error { +//// EthError::RpcError(value) => { +//// // Try to parse the error message +//// if let Some(message) = value.get("message").and_then(|m| m.as_str()) { +//// if message.contains("insufficient funds") { +//// return WalletError::InsufficientFunds(message.to_string()); +//// } else if message.contains("underpriced") { +//// return WalletError::TransactionUnderpriced; +//// } else if message.contains("nonce too low") { +//// return WalletError::TransactionNonceTooLow; +//// } +//// // Add more error patterns as needed +//// } +//// WalletError::TransactionError(format!("RPC error: {:?}", value)) +//// }, +//// _ => WalletError::TransactionError(format!("Ethereum error: {:?}", error)) +//// } +////} +// +//// ERC-20 Read Function Selectors (_ = NOT CHECKED) +//const ERC20_BALANCE_OF: [u8; 4] = [0x70, 0xa0, 0x82, 0x31]; // balanceOf(address) +//const ERC20_DECIMALS: [u8; 4] = [0x31, 0x3c, 0xe5, 0x67]; // decimals() +//const _ERC20_ALLOWANCE: [u8; 4] = [0xdd, 0x62, 0xed, 0x3e]; // allowance(address,address) +//const _ERC20_TOTAL_SUPPLY: [u8; 4] = [0x18, 0x16, 0x0d, 0xdd]; // totalSupply() +//const _ERC20_SYMBOL: [u8; 4] = [0x95, 0xd8, 0x9b, 0x41]; // symbol() +//const _ERC20_NAME: [u8; 4] = [0x06, 0xfd, 0xde, 0x03]; // name() +// +//// ERC-20 Write Function Selectors +//const _ERC20_TRANSFER: [u8; 4] = [0xa9, 0x05, 0x9c, 0xbb]; // transfer(address,uint256) +//const _ERC20_TRANSFER_FROM: [u8; 4] = [0x23, 0xb8, 0x72, 0xdd]; // transferFrom(address,address,uint256) +//const _ERC20_APPROVE: [u8; 4] = [0x09, 0x5e, 0xa7, 0xb3]; // approve(address,uint256) +// +///// Get the balance of ERC20 tokens for an address +//pub fn erc20_balance_of( +// token_address: &str, +// owner_address: &str, +// provider: Provider +//) -> Result { +// kiprintln!("PROCESS_LIB::erc20_balance_of token_address: {}", token_address); +// // Resolve addresses +// let token: EthAddress = resolve_name(&token_address, provider.chain_id)?; +// kiprintln!("PROCESS_LIB::erc20_balance_of token: {}", token); +// let owner = resolve_name(&owner_address, provider.chain_id)?; +// kiprintln!("PROCESS_LIB::erc20_balance_of owner: {}", owner); +// +// // The ERC20 balanceOf function selector: keccak256("balanceOf(address)")[0:4] +// //let selector = [0xa9, 0x05, 0x9c, 0xbb]; +// let selector = ERC20_BALANCE_OF; +// kiprintln!("PROCESS_LIB::erc20_balance_of selector: {:?}", selector); +// +// // Encode the owner address parameter (padded to 32 bytes) +// let mut call_data = Vec::with_capacity(4 + 32); +// call_data.extend_from_slice(&selector); +// call_data.extend_from_slice(&[0u8; 12]); // 12 bytes of padding +// call_data.extend_from_slice(owner.as_slice()); +// kiprintln!("PROCESS_LIB::erc20_balance_of call_data: {:?}", call_data); +// +// // Create the transaction request for eth_call +// let tx = TransactionRequest { +// to: Some(TxKind::Call(token)), +// input: call_data.into(), +// ..Default::default() +// }; +// +// kiprintln!("PROCESS_LIB::erc20_balance_of tx: {:#?}", tx); +// +// // Call the contract +// let result = provider.call(tx, None)?; +// +// kiprintln!("PROCESS_LIB::erc20_balance_of result: {:?}", result); +// +// // Parse the result (a uint256 value) +// if result.len() < 32 { +// kiprintln!("PROCESS_LIB::erc20_balance_of Invalid result length"); +// return Err(WalletError::TransactionError("Invalid result length".into())); +// } +// +// // Convert the bytes to a U256 +// let balance = U256::from_be_bytes::<32>(result[0..32].try_into().unwrap()); +// +// // TODO: This should be based on the contract's decimals, fix later with a LUT +// //Ok(EthAmount { wei_value: balance }) +// +// let decimals = erc20_decimals(token, &provider)?; +// let balance_u128 = balance.to::(); +// let balance_float = balance_u128 as f64 / 10f64.powi(decimals as i32); +// Ok(balance_float) // Returns balance in full tokens (e.g., 390.159112 USDC) +// +//} +// +//pub fn erc20_decimals(token_address: EthAddress, provider: &Provider) -> Result { +// kiprintln!("PROCESS_LIB::erc20_decimals token_address: {}", token_address); +// let token = token_address; +// kiprintln!("PROCESS_LIB::erc20_decimals token: {}", token); +// let selector = ERC20_DECIMALS; +// let call_data = selector.to_vec(); // ✅ Ensure it's exactly 4 bytes +// +// kiprintln!("PROCESS_LIB::erc20_decimals selector: {:?}", selector); +// +// let tx = TransactionRequest { +// to: Some(TxKind::Call(token)), +// input: call_data.into(), +// ..Default::default() +// }; +// +// let result = provider.call(tx, None)?; +// kiprintln!("PROCESS_LIB::erc20_decimals result: {:?}", result); +// +// if result.len() < 32 { +// return Err(WalletError::TransactionError("Invalid decimals response".into())); +// } +// kiprintln!("PROCESS_LIB::erc20_decimals done", ); +// +// Ok(result[31]) // Decimals are stored in the last byte of the 32-byte response +//} +// +// +/////// Transfer ERC20 tokens to another address +////pub fn erc20_transfer( +//// token_address: &str, +//// to_address: &str, +//// amount: EthAmount, +//// provider: Provider, +//// signer: &S +////) -> Result { +//// // Resolve addresses +//// let token = resolve_name(token_address, provider.chain_id())?; +//// let to = resolve_name(to_address, provider.chain_id())?; +//// +//// // The ERC20 transfer function selector: keccak256("transfer(address,uint256)")[0:4] +//// let selector = [0xa9, 0x05, 0x9c, 0xbb]; +//// +//// // Encode the parameters: address recipient, uint256 amount +//// let mut call_data = Vec::with_capacity(4 + 32 + 32); +//// call_data.extend_from_slice(&selector); +//// +//// // Recipient address (padded to 32 bytes) +//// call_data.extend_from_slice(&[0u8; 12]); // 12 bytes of padding +//// call_data.extend_from_slice(to.as_slice()); +//// +//// // Amount (padded to 32 bytes) +//// let amount_bytes = amount.as_wei().to_be_bytes::<32>(); +//// call_data.extend_from_slice(&amount_bytes); +//// +//// // Get the current nonce +//// let from_address = signer.address(); +//// let nonce = provider.get_transaction_count(from_address, None)?.to::(); +//// +//// // Estimate gas for the token transfer (usually around 60k for ERC20 transfers) +//// let tx_req = TransactionRequest { +//// from: Some(from_address), +//// to: Some(token), +//// data: Some(call_data.clone().into()), +//// ..Default::default() +//// }; +//// +//// let gas_limit = provider.estimate_gas(tx_req, None)? +//// .to::() +//// .saturating_mul(12).saturating_div(10); // Add 20% buffer +//// +//// // Calculate gas price based on the chain +//// let (gas_price, priority_fee) = match signer.chain_id() { +//// // Use your existing gas calculation functions +//// 1 => calculate_eth_mainnet_gas(&provider)?, +//// 8453 => calculate_base_gas(&provider)?, +//// _ => { +//// let base_fee = provider.get_gas_price()?.to::(); +//// let adjusted_fee = (base_fee * 130) / 100; +//// (adjusted_fee, adjusted_fee / 10) +//// } +//// }; +//// +//// // Create transaction data +//// let tx_data = TransactionData { +//// to: token, +//// value: U256::ZERO, // No ETH sent with token transfers +//// data: Some(call_data), +//// nonce, +//// gas_limit, +//// gas_price, +//// max_priority_fee: Some(priority_fee), +//// chain_id: signer.chain_id(), +//// }; +//// +//// // Sign and send transaction +//// let signed_tx = signer.sign_transaction(&tx_data)?; +//// let tx_hash = provider.send_raw_transaction(signed_tx.into())?; +//// +//// Ok(TxReceipt { +//// hash: tx_hash, +//// details: format!("Sent {} tokens to {}", amount.to_string(), to_address), +//// }) +////} +// +//// THE HYPERMAP stuff +// +///// Result type for Hypermap transactions +//#[derive(Debug, Clone)] +//pub struct HypermapTxReceipt { +// /// Transaction hash +// pub hash: TxHash, +// /// Description of the operation +// pub description: String, +//} +// +///// Create a note (mutable data) on a Hypermap namespace entry +///// +///// # Parameters +///// - `parent_entry`: The namespace entry (e.g. "mynode.hypr") where the note will be created +///// - `note_key`: The note key to create (must start with '~') +///// - `data`: The data to store in the note +///// - `provider`: The Ethereum provider to use +///// - `signer`: The signer to use for signing the transaction +///// +///// # Returns +///// A result containing a HypermapTxReceipt or a WalletError +//pub fn create_note( +// parent_entry: &str, +// note_key: &str, +// data: Vec, +// provider: Provider, +// signer: &S, +//) -> Result { +// // Verify the note key is valid +// if !valid_note(note_key) { +// return Err(WalletError::NameResolutionError( +// format!("Invalid note key '{}'. Must start with '~' and contain only lowercase letters, numbers, and hyphens", note_key) +// )); +// } +// +// // Get the parent TBA address +// let hypermap = provider.hypermap(); +// let parent_hash_str = namehash(parent_entry); +// +// println!("Parent entry: {}", parent_entry); +// println!("Parent hash: {}", parent_hash_str); +// +// let (tba, owner, _) = hypermap.get_hash(&parent_hash_str)?; +// +// println!("TBA address (parent): {}", tba); +// println!("Owner address: {}", owner); +// +// // Check that the signer is the owner of the parent entry +// let signer_address = signer.address(); +// println!("Signer address: {}", signer_address); +// +// if signer_address != owner { +// return Err(WalletError::PermissionDenied( +// format!("Signer address {} does not own the parent entry {}", signer_address, parent_entry) +// )); +// } +// +// // Get the hypermap contract address +// let hypermap_address = *hypermap.address(); +// println!("Hypermap contract address: {}", hypermap_address); +// +// // Create the note function call data +// let note_function = hypermap::contract::noteCall { +// note: Bytes::from(note_key.as_bytes().to_vec()), +// data: Bytes::from(data), +// }; +// let note_call_data = note_function.abi_encode(); +// +// // ?? Bytes::from(note_call_data) or note_call_data? +// // Now create an ERC-6551 execute call to send from the wallet to the TBA +// let execute_call_data = create_execute_calldata( +// hypermap_address, +// U256::ZERO, +// Bytes::from(note_call_data), +// 0 // CALL operation +// ); +// +// // Send the transaction from the wallet to the TBA +// let (tx_hash, tx_data) = send_transaction( +// tba, +// execute_call_data, +// U256::ZERO, +// provider, +// signer +// )?; +// +// // Return the receipt with transaction details +// Ok(HypermapTxReceipt { +// hash: tx_hash, +// description: format!("Created note '{}' on '{}'", note_key, parent_entry), +// }) +//} +// +///// Helper function to create calldata for the TBA's execute function +///// +///// The ERC-6551 execute function has this signature: +///// function execute(address to, uint256 value, bytes calldata data, uint8 operation) +///// +///// Parameters: +///// - to: The target contract to call (the Hypermap contract) +///// - value: Amount of ETH to send (usually 0) +///// - data: The calldata for the target function +///// - operation: The type of operation (0 = CALL, 1 = DELEGATECALL, etc.) +//fn create_execute_calldata( +// to: EthAddress, +// value: U256, +// data: Bytes, +// operation: u8 +//) -> Bytes { +// // Function selector for execute(address,uint256,bytes,uint8) +// // keccak256("execute(address,uint256,bytes,uint8)")[0:4] +// let selector = [0x44, 0xc0, 0x28, 0xfe]; +// +// // Encode to address (padded to 32 bytes) +// let mut to_bytes = vec![0u8; 32]; +// to_bytes[12..32].copy_from_slice(to.as_slice()); +// +// // Encode value (uint256) +// let value_bytes = value.to_be_bytes::<32>(); +// +// // Calculate offset for the dynamic bytes data +// // This is the offset in 32-byte words to where the bytes data starts +// // 3 fixed params (address, uint256, uint256 offset) + 1 more fixed param after = 4 * 32 = 128 bytes +// let offset = U256::from(128); +// let offset_bytes = offset.to_be_bytes::<32>(); +// +// // Encode operation (padded to 32 bytes) +// let mut operation_bytes = vec![0u8; 32]; +// operation_bytes[31] = operation; +// +// // Encode bytes length +// let data_len = U256::from(data.len()); +// let data_len_bytes = data_len.to_be_bytes::<32>(); +// +// // Encode bytes data (with padding to 32-byte boundary) +// let mut padded_data = data.to_vec(); +// if padded_data.len() % 32 != 0 { +// let padding = vec![0u8; 32 - (padded_data.len() % 32)]; +// padded_data.extend_from_slice(&padding); +// } +// +// // Combine everything into final calldata +// let mut result = Vec::new(); +// result.extend_from_slice(&selector); // Function selector (4 bytes) +// result.extend_from_slice(&to_bytes); // To address (32 bytes) +// result.extend_from_slice(&value_bytes); // Value (32 bytes) +// result.extend_from_slice(&offset_bytes); // Data offset (32 bytes) +// result.extend_from_slice(&operation_bytes); // Operation (32 bytes) +// result.extend_from_slice(&data_len_bytes); // Data length (32 bytes) +// result.extend_from_slice(&padded_data); // Data (padded) +// +// Bytes::from(result) +//} +// +///// Send a transaction to the token-bound account +//fn send_transaction( +// to: EthAddress, +// data: Bytes, +// value: U256, +// provider: Provider, +// signer: &S, +//) -> Result<(TxHash, Vec), WalletError> { +// let chain_id = signer.chain_id(); +// +// kiprintln!("PROCESS_LIB::send_transaction starting"); +// kiprintln!("PROCESS_LIB::send_transaction chain_id: {}", chain_id); +// +// // Get gas estimates - use 50% buffer for Base to ensure acceptance +// let base_fee = provider.get_gas_price()?.to::(); +// let gas_price = (base_fee * 150) / 100; // 50% buffer +// let priority_fee = gas_price / 5; // 20% of gas price +// +// kiprintln!("PROCESS_LIB::send_transaction base_fee: {}, priority_fee: {}", base_fee, priority_fee); +// kiprintln!("PROCESS_LIB::send_transaction gas_price: {}", gas_price); +// +// // Get the current nonce for the signer's address +// let from_address = signer.address(); +// let nonce = provider.get_transaction_count(from_address, None)? +// .to::(); +// +// kiprintln!("PROCESS_LIB::send_transaction nonce: {}", nonce); +// +// // For ERC-6551 account operations, use a higher gas limit +// // The ERC-6551 execute function is complex and gas-intensive +// let estimated_gas = 500_000; // Start high for ERC-6551 +// +// // Add 50% buffer to estimated gas since this is a complex operation +// let gas_limit = (estimated_gas * 150) / 100; +// +// kiprintln!("PROCESS_LIB::send_transaction estimated_gas: {}", estimated_gas); +// kiprintln!("PROCESS_LIB::send_transaction gas_limit: {}", gas_limit); +// +// // Prepare transaction data +// let tx_data = TransactionData { +// to, +// value, +// data: Some(data.to_vec()), +// nonce, +// gas_limit, +// gas_price, +// max_priority_fee: Some(priority_fee), +// chain_id, +// }; +// +// // Sign the transaction +// let signed_tx = signer.sign_transaction(&tx_data)?; +// kiprintln!("PROCESS_LIB::send_transaction signed"); +// +// // Send the transaction +// let tx_hash = provider.send_raw_transaction(signed_tx.clone().into())?; +// kiprintln!("PROCESS_LIB::send_transaction tx_hash: {}", tx_hash); +// +// // Return both the hash and the raw transaction data +// Ok((tx_hash, signed_tx)) +//} +// +/////// A simple test function to create a note +////pub fn test_create_note( +//// parent_entry: &str, // e.g., "lazybonesitis.os" +//// provider: Provider, +//// signer: &S, +////) -> Result<(), WalletError> { +//// println!("=== TESTING NOTE CREATION ==="); +//// println!("Parent entry: {}", parent_entry); +//// +//// // Simple test note +//// let note_key = "~test-note"; +//// let data = "This is a test note created at ".to_string() + &chrono::Utc::now().to_rfc3339(); +//// +//// println!("Creating note: {}", note_key); +//// println!("Data: {}", data); +//// +//// match create_note( +//// parent_entry, +//// note_key, +//// data.as_bytes().to_vec(), +//// provider, +//// signer +//// ) { +//// Ok(receipt) => { +//// println!("Success! Transaction hash: {}", receipt.hash); +//// println!("Description: {}", receipt.description); +//// Ok(()) +//// }, +//// Err(e) => { +//// println!("Error creating note: {:?}", e); +//// Err(e) +//// } +//// } +////} +// +/////// Create a note (mutable data) on a Hypermap namespace entry +/////// +/////// # Parameters +/////// - `parent_entry`: The namespace entry (e.g. "mynode.hypr") where the note will be created +/////// - `note_key`: The note key to create (must start with '~') +/////// - `data`: The data to store in the note +/////// - `provider`: The Ethereum provider to use +/////// - `signer`: The signer to use for signing the transaction +/////// +/////// # Returns +/////// A result containing a HypermapTxReceipt or a WalletError +////pub fn create_note( +//// parent_entry: &str, +//// note_key: &str, +//// data: Vec, +//// provider: Provider, +//// signer: &S, +////) -> Result { +//// // Verify the note key is valid +//// if !valid_note(note_key) { +//// return Err(WalletError::NameResolutionError( +//// format!("Invalid note key '{}'. Must start with '~' and contain only lowercase letters, numbers, and hyphens", note_key) +//// )); +//// } +//// +//// // Get the parent TBA address +//// let hypermap = provider.hypermap(); +//// let parent_hash_str = namehash(parent_entry); +//// let (tba, owner, _) = hypermap.get_hash(&parent_hash_str)?; +//// +//// // Check that the signer is the owner of the parent entry +//// let signer_address = signer.address(); +//// if signer_address != owner { +//// return Err(WalletError::PermissionDenied( +//// format!("Signer address {} does not own the parent entry {}", signer_address, parent_entry) +//// )); +//// } +//// +//// // Create the note call data +//// let note_function = hypermap::contract::noteCall { +//// note: Bytes::from(note_key.as_bytes().to_vec()), +//// data: Bytes::from(data), +//// }; +//// let call_data = note_function.abi_encode(); +//// +//// // Prepare and send the transaction +//// send_tba_transaction( +//// tba, +//// call_data.into(), +//// U256::ZERO, // No ETH value to send +//// provider, +//// signer, +//// format!("Created note '{}' on '{}'", note_key, parent_entry), +//// ) +////} +// +///// Create a fact (immutable data) on a Hypermap namespace entry +///// +///// # Parameters +///// - `parent_entry`: The namespace entry (e.g. "mynode.hypr") where the fact will be created +///// - `fact_key`: The fact key to create (must start with '!') +///// - `data`: The data to store in the fact +///// - `provider`: The Ethereum provider to use +///// - `signer`: The signer to use for signing the transaction +///// +///// # Returns +///// A result containing a HypermapTxReceipt or a WalletError +//pub fn create_fact( +// parent_entry: &str, +// fact_key: &str, +// data: Vec, +// provider: Provider, +// signer: &S, +//) -> Result { +// // Verify the fact key is valid +// if !valid_fact(fact_key) { +// return Err(WalletError::NameResolutionError( +// format!("Invalid fact key '{}'. Must start with '!' and contain only lowercase letters, numbers, and hyphens", fact_key) +// )); +// } +// +// // Get the parent TBA address +// let hypermap = provider.hypermap(); +// let parent_hash_str = namehash(parent_entry); +// let (tba, owner, _) = hypermap.get_hash(&parent_hash_str)?; +// +// // Check that the signer is the owner of the parent entry +// let signer_address = signer.address(); +// if signer_address != owner { +// return Err(WalletError::PermissionDenied( +// format!("Signer address {} does not own the parent entry {}", signer_address, parent_entry) +// )); +// } +// +// // Create the fact call data +// let fact_function = hypermap::contract::factCall { +// fact: Bytes::from(fact_key.as_bytes().to_vec()), +// data: Bytes::from(data), +// }; +// let call_data = fact_function.abi_encode(); +// +// // Prepare and send the transaction +// send_tba_transaction( +// tba, +// call_data.into(), +// U256::ZERO, // No ETH value to send +// provider, +// signer, +// format!("Created fact '{}' on '{}'", fact_key, parent_entry), +// ) +//} +// +///// Mint a new namespace entry under a parent entry +///// +///// # Parameters +///// - `parent_entry`: The parent namespace entry (e.g. "mynode.hypr") +///// - `label`: The new label to mint (without prefix or parent) +///// - `recipient`: The address that will own the new entry +///// - `implementation`: The address of the token-bound account implementation +///// - `provider`: The Ethereum provider to use +///// - `signer`: The signer to use for signing the transaction +///// +///// # Returns +///// A result containing a HypermapTxReceipt or a WalletError +//pub fn mint_entry( +// parent_entry: &str, +// label: &str, +// recipient: &str, +// implementation: &str, +// provider: Provider, +// signer: &S, +//) -> Result { +// // Verify the label is valid +// if !valid_name(label) { +// return Err(WalletError::NameResolutionError( +// format!("Invalid label '{}'. Must contain only lowercase letters, numbers, and hyphens", label) +// )); +// } +// +// // Get the parent TBA address +// let hypermap = provider.hypermap(); +// let parent_hash_str = namehash(parent_entry); +// kiprintln!("PROCESS_LIB::mint_entry parent_hash_str: {}", parent_hash_str); +// let (tba, owner, _) = hypermap.get_hash(&parent_hash_str)?; +// +// // Check that the signer is the owner of the parent entry +// let signer_address = signer.address(); +// if signer_address != owner { +// return Err(WalletError::PermissionDenied( +// format!("Signer address {} does not own the parent entry {}", signer_address, parent_entry) +// )); +// } +// +// // Resolve recipient address +// let recipient_address = resolve_name(recipient, provider.chain_id)?; +// +// // Resolve implementation address +// let implementation_address = resolve_name(implementation, provider.chain_id)?; +// +// // Create the mint call data +// let mint_function = hypermap::contract::mintCall { +// who: recipient_address, +// label: Bytes::from(label.as_bytes().to_vec()), +// initialization: Bytes::default(), // No initialization data +// erc721Data: Bytes::default(), // No ERC721 data +// implementation: implementation_address, +// }; +// let call_data = mint_function.abi_encode(); +// +// kiprintln!("Parent entry: {}", parent_entry); +// kiprintln!("Parent hash: {}", parent_hash_str); +// kiprintln!("TBA address: {}", tba); +// kiprintln!("Owner address: {}", owner); +// kiprintln!("Signer address: {}", signer_address); +// +// // Prepare and send the transaction +// send_tba_transaction( +// tba, +// call_data.into(), +// U256::ZERO, // No ETH value to send +// provider, +// signer, +// format!("Minted new entry '{}' under '{}'", label, parent_entry), +// ) +//} +// +///// Set the gene for a namespace entry +///// +///// # Parameters +///// - `entry`: The namespace entry (e.g. "mynode.hypr") to set the gene for +///// - `gene_implementation`: The address of the token-bound account implementation to use as the gene +///// - `provider`: The Ethereum provider to use +///// - `signer`: The signer to use for signing the transaction +///// +///// # Returns +///// A result containing a HypermapTxReceipt or a WalletError +//pub fn set_gene( +// entry: &str, +// gene_implementation: &str, +// provider: Provider, +// signer: &S, +//) -> Result { +// // Get the entry's TBA address +// let hypermap = provider.hypermap(); +// let entry_hash_str = namehash(entry); +// let (tba, owner, _) = hypermap.get_hash(&entry_hash_str)?; +// +// // Check that the signer is the owner of the entry +// let signer_address = signer.address(); +// if signer_address != owner { +// return Err(WalletError::PermissionDenied( +// format!("Signer address {} does not own the entry {}", signer_address, entry) +// )); +// } +// +// // Resolve gene implementation address +// let gene_address = resolve_name(gene_implementation, provider.chain_id)?; +// +// // Create the gene call data +// let gene_function = hypermap::contract::geneCall { +// _gene: gene_address, +// }; +// let call_data = gene_function.abi_encode(); +// +// // Prepare and send the transaction +// send_tba_transaction( +// tba, +// call_data.into(), +// U256::ZERO, // No ETH value to send +// provider, +// signer, +// format!("Set gene for '{}' to '{}'", entry, gene_implementation), +// ) +//} +// +///// Helper function to send a transaction to a token-bound account +//fn send_tba_transaction( +// tba: EthAddress, +// data: Bytes, +// value: U256, +// provider: Provider, +// signer: &S, +// description: String, +//) -> Result { +// kiprintln!("PROCESS_LIB::send_tba_transaction starting"); +// +// let chain_id = signer.chain_id(); +// kiprintln!("PROCESS_LIB::send_tba_transaction chain_id: {}", chain_id); +// +// // TODO: change from hardcoded base to dynamic +// let (base_fee, priority_fee) = calculate_base_gas(&provider)?; +// kiprintln!("PROCESS_LIB::send_tba_transaction base_fee: {}, priority_fee: {}", base_fee, priority_fee); +// +// let gas_price = (base_fee * 180) / 100; +// kiprintln!("PROCESS_LIB::send_tba_transaction gas_price: {}", gas_price); +// +// // Get the current nonce for the signer's address +// let from_address = signer.address(); +// let nonce = provider.get_transaction_count(from_address, None)? +// .to::(); +// kiprintln!("PROCESS_LIB::send_tba_transaction nonce: {}", nonce); +// +// // Estimate gas limit for the transaction +// let tx_req = TransactionRequest::default() +// .from(from_address) +// .to(tba) +// .input(data.clone().into()) +// .value(value); +// +// let estimated_gas = match provider.estimate_gas(tx_req, None) { +// Ok(gas) => gas.to::(), +// Err(_) => { +// // If estimation fails, use a conservative gas limit +// // This might happen for new contracts or complex interactions +// 300_000 +// } +// }; +// kiprintln!("PROCESS_LIB::send_tba_transaction estimated_gas: {}", estimated_gas); +// +// // Add 120% buffer to estimated gas +// let gas_limit = (estimated_gas * 220) / 100; +// kiprintln!("PROCESS_LIB::send_tba_transaction gas_limit: {}", gas_limit); +// +// // Prepare transaction data +// let tx_data = TransactionData { +// to: tba, +// value, +// data: Some(data.to_vec()), +// nonce, +// gas_limit, +// gas_price, +// max_priority_fee: Some(priority_fee), +// chain_id, +// }; +// //kiprintln!("PROCESS_LIB::send_tba_transaction tx_data: {:#?}", tx_data); +// +// // Sign the transaction +// let signed_tx = signer.sign_transaction(&tx_data)?; +// kiprintln!("PROCESS_LIB::send_tba_transaction signed"); +// +// // Send the transaction +// let tx_hash = provider.send_raw_transaction(signed_tx.into())?; +// kiprintln!("PROCESS_LIB::send_tba_transaction tx_hash: {}", tx_hash); +// +// // Return the receipt with transaction details +// Ok(HypermapTxReceipt { +// hash: tx_hash, +// description, +// }) +//} +// +// +// +// +// From 13b47f03302c54a76820231c8594096b9fe22a5e Mon Sep 17 00:00:00 2001 From: Hallmane Date: Tue, 1 Apr 2025 14:47:51 +0200 Subject: [PATCH 06/96] token address resolution --- src/wallet.rs | 3339 +++---------------------------------------------- 1 file changed, 149 insertions(+), 3190 deletions(-) diff --git a/src/wallet.rs b/src/wallet.rs index 04068b7..19e3fda 100644 --- a/src/wallet.rs +++ b/src/wallet.rs @@ -36,7 +36,6 @@ use alloy_primitives::TxKind; use std::str::FromStr; use alloy_sol_types::{sol, SolCall}; -// Define token standards using sol! macro sol! { interface IERC20 { function balanceOf(address who) external view returns (uint256); @@ -116,8 +115,6 @@ pub enum WalletError { pub enum KeyStorage { /// An unencrypted wallet with a signer Decrypted(LocalSigner), - - /// An encrypted wallet - contains all the necessary data Encrypted(EncryptedSignerData), } @@ -490,6 +487,115 @@ pub fn resolve_name(name: &str, chain_id: u64) -> Result Result { + // If it's already an address, just parse it + if token.starts_with("0x") && token.len() == 42 { + return EthAddress::from_str(token) + .map_err(|_| WalletError::NameResolutionError(format!("Invalid address format: {}", token))); + } + + // Convert to uppercase for case-insensitive comparison + let token_upper = token.to_uppercase(); + + // Map of known token addresses by chain ID and symbol + match chain_id { + 1 => { // Ethereum Mainnet + match token_upper.as_str() { + "USDC" => EthAddress::from_str("0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48") + .map_err(|_| WalletError::NameResolutionError("Invalid USDC address format".to_string())), + "USDT" => EthAddress::from_str("0xdAC17F958D2ee523a2206206994597C13D831ec7") + .map_err(|_| WalletError::NameResolutionError("Invalid USDT address format".to_string())), + "DAI" => EthAddress::from_str("0x6B175474E89094C44Da98b954EedeAC495271d0F") + .map_err(|_| WalletError::NameResolutionError("Invalid DAI address format".to_string())), + "WETH" => EthAddress::from_str("0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2") + .map_err(|_| WalletError::NameResolutionError("Invalid WETH address format".to_string())), + "WBTC" => EthAddress::from_str("0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599") + .map_err(|_| WalletError::NameResolutionError("Invalid WBTC address format".to_string())), + _ => Err(WalletError::NameResolutionError( + format!("Token '{}' not recognized on chain ID {}", token, chain_id) + )), + } + }, + 8453 => { // Base + match token_upper.as_str() { + "USDC" => EthAddress::from_str("0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913") + .map_err(|_| WalletError::NameResolutionError("Invalid USDC address format".to_string())), + "DAI" => EthAddress::from_str("0x50c5725949A6F0c72E6C4a641F24049A917DB0Cb") + .map_err(|_| WalletError::NameResolutionError("Invalid DAI address format".to_string())), + "WETH" => EthAddress::from_str("0x4200000000000000000000000000000000000006") + .map_err(|_| WalletError::NameResolutionError("Invalid WETH address format".to_string())), + _ => Err(WalletError::NameResolutionError( + format!("Token '{}' not recognized on chain ID {}", token, chain_id) + )), + } + }, + 10 => { // Optimism + match token_upper.as_str() { + "USDC" => EthAddress::from_str("0x7F5c764cBc14f9669B88837ca1490cCa17c31607") + .map_err(|_| WalletError::NameResolutionError("Invalid USDC address format".to_string())), + "DAI" => EthAddress::from_str("0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1") + .map_err(|_| WalletError::NameResolutionError("Invalid DAI address format".to_string())), + "WETH" => EthAddress::from_str("0x4200000000000000000000000000000000000006") + .map_err(|_| WalletError::NameResolutionError("Invalid WETH address format".to_string())), + _ => Err(WalletError::NameResolutionError( + format!("Token '{}' not recognized on chain ID {}", token, chain_id) + )), + } + }, + 137 => { // Polygon + match token_upper.as_str() { + "USDC" => EthAddress::from_str("0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174") + .map_err(|_| WalletError::NameResolutionError("Invalid USDC address format".to_string())), + "USDT" => EthAddress::from_str("0xc2132D05D31c914a87C6611C10748AEb04B58e8F") + .map_err(|_| WalletError::NameResolutionError("Invalid USDT address format".to_string())), + "DAI" => EthAddress::from_str("0x8f3Cf7ad23Cd3CaDbD9735AFf958023239c6A063") + .map_err(|_| WalletError::NameResolutionError("Invalid DAI address format".to_string())), + "WETH" => EthAddress::from_str("0x7ceB23fD6bC0adD59E62ac25578270cFf1b9f619") + .map_err(|_| WalletError::NameResolutionError("Invalid WETH address format".to_string())), + "WMATIC" => EthAddress::from_str("0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270") + .map_err(|_| WalletError::NameResolutionError("Invalid WMATIC address format".to_string())), + _ => Err(WalletError::NameResolutionError( + format!("Token '{}' not recognized on chain ID {}", token, chain_id) + )), + } + }, + 42161 => { // Arbitrum + match token_upper.as_str() { + "USDC" => EthAddress::from_str("0xFF970A61A04b1cA14834A43f5dE4533eBDDB5CC8") + .map_err(|_| WalletError::NameResolutionError("Invalid USDC address format".to_string())), + "USDT" => EthAddress::from_str("0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9") + .map_err(|_| WalletError::NameResolutionError("Invalid USDT address format".to_string())), + "DAI" => EthAddress::from_str("0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1") + .map_err(|_| WalletError::NameResolutionError("Invalid DAI address format".to_string())), + "WETH" => EthAddress::from_str("0x82aF49447D8a07e3bd95BD0d56f35241523fBab1") + .map_err(|_| WalletError::NameResolutionError("Invalid WETH address format".to_string())), + _ => Err(WalletError::NameResolutionError( + format!("Token '{}' not recognized on chain ID {}", token, chain_id) + )), + } + }, + // Test networks + 31337 | 1337 => { + return Err(WalletError::NameResolutionError( + format!("Token symbol resolution not supported on test networks. Please use the full contract address.") + )); + }, + _ => { + return Err(WalletError::NameResolutionError( + format!("Token symbol resolution not supported for chain ID {}", chain_id) + )); + } + } + .map_err(|e| match e { + WalletError::NameResolutionError(msg) => WalletError::NameResolutionError(msg), + _ => WalletError::NameResolutionError( + format!("Invalid address format for token '{}' on chain ID {}", token, chain_id) + ), + }) +} + + // // ETHEREUM FUNCTIONS // @@ -591,7 +697,12 @@ pub fn erc20_balance_of( owner_address: &str, provider: &Provider ) -> Result { - let token = resolve_name(token_address, provider.chain_id)?; + // First try to resolve the token as a symbol + let token = match resolve_token_symbol(token_address, provider.chain_id) { + Ok(addr) => addr, + Err(_) => resolve_name(token_address, provider.chain_id)?, // Fall back to regular name resolution + }; + let owner = resolve_name(owner_address, provider.chain_id)?; let call = IERC20::balanceOfCall { who: owner }; @@ -605,7 +716,11 @@ pub fn erc20_balance_of( /// Get the number of decimals for an ERC20 token pub fn erc20_decimals(token_address: &str, provider: &Provider) -> Result { - let token = resolve_name(token_address, provider.chain_id)?; + let token = match resolve_token_symbol(token_address, provider.chain_id) { + Ok(addr) => addr, + Err(_) => resolve_name(token_address, provider.chain_id)?, + }; + let call = IERC20::decimalsCall {}; let decimals = call_view_function(token, call, provider)?; Ok(decimals._0) @@ -613,7 +728,11 @@ pub fn erc20_decimals(token_address: &str, provider: &Provider) -> Result Result { - let token = resolve_name(token_address, provider.chain_id)?; + let token = match resolve_token_symbol(token_address, provider.chain_id) { + Ok(addr) => addr, + Err(_) => resolve_name(token_address, provider.chain_id)?, + }; + let call = IERC20::symbolCall {}; let symbol = call_view_function(token, call, provider)?; Ok(symbol._0) @@ -621,7 +740,11 @@ pub fn erc20_symbol(token_address: &str, provider: &Provider) -> Result Result { - let token = resolve_name(token_address, provider.chain_id)?; + let token = match resolve_token_symbol(token_address, provider.chain_id) { + Ok(addr) => addr, + Err(_) => resolve_name(token_address, provider.chain_id)?, + }; + let call = IERC20::nameCall {}; let name = call_view_function(token, call, provider)?; Ok(name._0) @@ -629,7 +752,11 @@ pub fn erc20_name(token_address: &str, provider: &Provider) -> Result Result { - let token = resolve_name(token_address, provider.chain_id)?; + let token = match resolve_token_symbol(token_address, provider.chain_id) { + Ok(addr) => addr, + Err(_) => resolve_name(token_address, provider.chain_id)?, + }; + let call = IERC20::totalSupplyCall {}; let total_supply = call_view_function(token, call, provider)?; Ok(total_supply._0) @@ -642,7 +769,11 @@ pub fn erc20_allowance( spender_address: &str, provider: &Provider ) -> Result { - let token = resolve_name(token_address, provider.chain_id)?; + let token = match resolve_token_symbol(token_address, provider.chain_id) { + Ok(addr) => addr, + Err(_) => resolve_name(token_address, provider.chain_id)?, + }; + let owner = resolve_name(owner_address, provider.chain_id)?; let spender = resolve_name(spender_address, provider.chain_id)?; @@ -661,11 +792,16 @@ pub fn erc20_transfer( ) -> Result { kiprintln!("PL:: Transferring {} tokens to {} on {}", amount, to_address, provider.chain_id); - // Resolve addresses - let token = resolve_name(token_address, provider.chain_id)?; - let to = resolve_name(to_address, provider.chain_id)?; + // Resolve token address (could be a symbol like "USDC") + let token = match resolve_token_symbol(token_address, provider.chain_id) { + Ok(addr) => addr, + Err(_) => resolve_name(token_address, provider.chain_id)?, + }; kiprintln!("PL:: Resolved token address: {}", token); + + // Resolve recipient address + let to = resolve_name(to_address, provider.chain_id)?; kiprintln!("PL:: Resolved recipient address: {}", to); // Create the call @@ -1230,3181 +1366,4 @@ pub fn set_gene( provider, signer ) -} - - - - - -//use crate::eth::{ -// Provider, -// EthError, -// BlockNumberOrTag -//}; -//use crate::signer::{ -// Signer, -// LocalSigner, -// TransactionData, -// SignerError, -// EncryptedSignerData -//}; -//use crate::hypermap::{ -// namehash, -// valid_note, -// valid_fact, -// valid_name, -//}; -//use crate::{ -// hypermap, -// kiprintln -//}; -// -//use thiserror::Error; -//use alloy_primitives::{ -// Address as EthAddress, -// TxHash, -// U256, -// Bytes -//}; -//use alloy::rpc::types::{ -// TransactionReceipt, -// TransactionRequest -//}; -//use alloy_primitives::TxKind; -//use std::str::FromStr; -//use alloy_sol_types::{sol, SolCall}; -// -//sol! { -// interface IERC20 { -// function balanceOf(address who) external view returns (uint256); -// function transfer(address to, uint256 value) external returns (bool); -// function approve(address spender, uint256 value) external returns (bool); -// function transferFrom(address from, address to, uint256 value) external returns (bool); -// function allowance(address owner, address spender) external view returns (uint256); -// function totalSupply() external view returns (uint256); -// function decimals() external view returns (uint8); -// function symbol() external view returns (string); -// function name() external view returns (string); -// } -// -// interface IERC721 { -// function balanceOf(address owner) external view returns (uint256); -// function ownerOf(uint256 tokenId) external view returns (address); -// function safeTransferFrom(address from, address to, uint256 tokenId) external; -// function transferFrom(address from, address to, uint256 tokenId) external; -// function approve(address to, uint256 tokenId) external; -// function setApprovalForAll(address operator, bool approved) external; -// function getApproved(uint256 tokenId) external view returns (address); -// function isApprovedForAll(address owner, address operator) external view returns (bool); -// } -// -// interface IERC1155 { -// function balanceOf(address account, uint256 id) external view returns (uint256); -// function balanceOfBatch(address[] accounts, uint256[] ids) external view returns (uint256[]); -// function setApprovalForAll(address operator, bool approved) external; -// function isApprovedForAll(address account, address operator) external view returns (bool); -// function safeTransferFrom(address from, address to, uint256 id, uint256 amount, bytes data) external; -// function safeBatchTransferFrom(address from, address to, uint256[] ids, uint256[] amounts, bytes data) external; -// } -// -// interface IERC6551Account { -// function execute(address to, uint256 value, bytes calldata data, uint8 operation) external returns (bytes); -// } -//} -// -//#[derive(Debug, Error)] -//pub enum WalletError { -// #[error("signing error: {0}")] -// SignerError(#[from] SignerError), -// -// #[error("ethereum error: {0}")] -// EthError(#[from] EthError), -// -// #[error("name resolution error: {0}")] -// NameResolutionError(String), -// -// #[error("invalid amount: {0}")] -// InvalidAmount(String), -// -// #[error("transaction error: {0}")] -// TransactionError(String), -// -// #[error("gas estimation error: {0}")] -// GasEstimationError(String), -// -// #[error("insufficient funds: {0}")] -// InsufficientFunds(String), -// -// #[error("network congestion: {0}")] -// NetworkCongestion(String), -// -// #[error("transaction underpriced")] -// TransactionUnderpriced, -// -// #[error("transaction nonce too low")] -// TransactionNonceTooLow, -// -// #[error("permission denied: {0}")] -// PermissionDenied(String), -//} -// -///// Represents the storage state of a wallet's private key -//#[derive(Debug, Clone)] -//pub enum KeyStorage { -// /// An unencrypted wallet with a signer -// Decrypted(LocalSigner), -// -// /// An encrypted wallet - contains all the necessary data -// Encrypted(EncryptedSignerData), -//} -// -//impl KeyStorage { -// /// Get the encrypted data if this is an encrypted key storage -// pub fn get_encrypted_data(&self) -> Option> { -// match self { -// KeyStorage::Encrypted(data) => Some(data.encrypted_data.clone()), -// KeyStorage::Decrypted(_) => None, -// } -// } -// -// /// Get the address associated with this wallet -// pub fn get_address(&self) -> String { -// match self { -// KeyStorage::Decrypted(signer) => signer.address().to_string(), -// KeyStorage::Encrypted(data) => data.address.clone(), -// } -// } -// -// /// Get the chain ID associated with this wallet -// pub fn get_chain_id(&self) -> u64 { -// match self { -// KeyStorage::Decrypted(signer) => signer.chain_id(), -// KeyStorage::Encrypted(data) => data.chain_id, -// } -// } -//} -// -///// Represents an amount of ETH with proper formatting -//#[derive(Debug, Clone)] -//pub struct EthAmount { -// /// Value in wei -// wei_value: U256, -//} -// -//impl EthAmount { -// /// Create a new amount from ETH value -// pub fn from_eth(eth_value: f64) -> Self { -// // Convert ETH to wei (1 ETH = 10^18 wei) -// let wei = (eth_value * 1_000_000_000_000_000_000.0) as u128; -// Self { -// wei_value: U256::from(wei), -// } -// } -// -// /// Create from a string like "0.1 ETH" or "10 wei" -// pub fn from_string(amount_str: &str) -> Result { -// let parts: Vec<&str> = amount_str.trim().split_whitespace().collect(); -// -// if parts.is_empty() { -// return Err(WalletError::InvalidAmount( -// "Empty amount string".to_string() -// )); -// } -// -// let value_str = parts[0]; -// let unit = parts.get(1).map(|s| s.to_lowercase()).unwrap_or_else(|| "eth".to_string()); -// -// let value = value_str.parse::() -// .map_err(|_| WalletError::InvalidAmount(format!("Invalid numeric value: {}", value_str)))?; -// -// match unit.as_str() { -// "eth" => Ok(Self::from_eth(value)), -// "wei" => Ok(Self { -// wei_value: U256::from(value as u128), -// }), -// _ => Err(WalletError::InvalidAmount(format!("Unknown unit: {}", unit))), -// } -// } -// -// /// Get the value in wei -// pub fn as_wei(&self) -> U256 { -// self.wei_value -// } -// -// /// Get a human-readable string representation -// pub fn to_string(&self) -> String { -// // For values over 0.0001 ETH, show in ETH, otherwise in wei -// if self.wei_value >= U256::from(100_000_000_000_000u128) { -// // Convert to u128 first (safe since ETH total supply fits in u128) then to f64 -// let wei_u128 = self.wei_value.to::(); -// let eth_value = wei_u128 as f64 / 1_000_000_000_000_000_000.0; -// format!("{:.6} ETH", eth_value) -// } else { -// format!("{} wei", self.wei_value) -// } -// } -//} -// -///// Transaction receipt returned after sending -//#[derive(Debug, Clone)] -//pub struct TxReceipt { -// /// Transaction hash -// pub hash: TxHash, -// /// Transaction details -// pub details: String, -//} -// -//// Resolve a .hypr name to an Ethereum address using Hypermap -//pub fn resolve_name(name: &str, chain_id: u64) -> Result { -// // If it's already an address, just parse it -// if name.starts_with("0x") && name.len() == 42 { -// return EthAddress::from_str(name) -// .map_err(|_| WalletError::NameResolutionError(format!("Invalid address format: {}", name))); -// } -// -// // hardcoded to .hypr for now -// let formatted_name = if !name.contains('.') { -// format!("{}.hypr", name) -// } else { -// kiprintln!("PROCESS_LIB::resolve_name name: {}", name); -// name.to_string() -// }; -// -// // Use hypermap resolution -// let hypermap = hypermap::Hypermap::default(chain_id); -// match hypermap.get(&formatted_name) { -// Ok((_tba, owner, _)) => { -// Ok(owner) -// }, -// Err(e) => { -// Err(WalletError::NameResolutionError( -// format!("Failed to resolve name '{}': {}", name, e) -// )) -// } -// } -//} -// -///// Send ETH to an address or name -//pub fn send_eth( -// to: &str, -// amount: EthAmount, -// provider: Provider, -// signer: &S, -//) -> Result { -// kiprintln!("PROCESS_LIB::send_eth starting transaction"); -// -// // Current chain-specific handling -// let chain_id = signer.chain_id(); -// kiprintln!("PROCESS_LIB::send_eth chain_id: {}", chain_id); -// -// // This part needs improvement - detect network type more robustly -// let is_test_network = chain_id == 31337 || chain_id == 1337; -// -// // Use network-specific gas strategies -// let (gas_price, priority_fee) = match chain_id { -// // just rough calculations for now -// 1 => calculate_eth_mainnet_gas(&provider)?, // mainnet -// 8453 => calculate_base_gas(&provider)?, // Base -// 10 => calculate_optimism_gas(&provider)?, // Optimism -// -// // Test networks - keep your current approach -// _ if is_test_network => (2_000_000_000, 100_000_000), -// -// // 30% buffer on other networks -// _ => { -// kiprintln!("PROCESS_LIB::send_eth getting gas price"); -// let base_fee = provider.get_gas_price()?.to::(); -// kiprintln!("PROCESS_LIB::send_eth base_fee: {}", base_fee); -// let adjusted_fee = (base_fee * 130) / 100; -// kiprintln!("PROCESS_LIB::send_eth adjusted_fee: {}", adjusted_fee); -// (adjusted_fee, adjusted_fee / 10) -// } -// }; -// -// kiprintln!("PROCESS_LIB::send_eth gas_price: {}", gas_price); -// -// // Resolve the name to an address -// let to_address = resolve_name(to, chain_id)?; -// kiprintln!("PROCESS_LIB::send_eth to_address: {}", to_address); -// -// // Get the current nonce for the signer's address -// let from_address = signer.address(); -// let nonce = provider.get_transaction_count(from_address, None)? -// .to::(); -// kiprintln!("PROCESS_LIB::send_eth nonce: {}", nonce); -// -// // Standard gas limit for ETH transfer -// let gas_limit = 21000; -// -// // Prepare transaction data -// let tx_data = TransactionData { -// to: to_address, -// value: amount.as_wei(), -// data: None, // No data for simple ETH transfer -// nonce, -// gas_limit, -// gas_price, -// max_priority_fee: Some(priority_fee), -// chain_id, -// }; -// -// kiprintln!("PROCESS_LIB::send_eth tx_data prepared"); -// -// // Sign the transaction -// let signed_tx = signer.sign_transaction(&tx_data)?; -// kiprintln!("PROCESS_LIB::send_eth transaction signed"); -// -// // Send the transaction -// let tx_hash = provider.send_raw_transaction(signed_tx.into())?; -// kiprintln!("PROCESS_LIB::send_eth tx_hash: {}", tx_hash); -// -// // Return the receipt with transaction details -// Ok(TxReceipt { -// hash: tx_hash, -// details: format!("Sent {} to {}", amount.to_string(), to), -// }) -//} -// -//// Helper function to calculate EIP-1559 gas parameters with network-specific values -//fn calculate_eip1559_gas( -// provider: &Provider, -// buffer_fraction: u128, -// priority_fee: u128 -//) -> Result<(u128, u128), WalletError> { -// kiprintln!("PROCESS_LIB::calculate_eip1559_gas starting"); -// // Get latest block -// let latest_block = provider.get_block_by_number(BlockNumberOrTag::Latest, false)? -// .ok_or_else(|| WalletError::TransactionError("Failed to get latest block".into()))?; -// -// kiprintln!("PROCESS_LIB::calculate_eip1559_gas latest_block received"); -// -// // Get base fee -// let base_fee = latest_block.header.inner.base_fee_per_gas -// .ok_or_else(|| WalletError::TransactionError("No base fee in block".into()))? -// as u128; -// -// kiprintln!("PROCESS_LIB::calculate_eip1559_gas base_fee: {}", base_fee); -// -// // Calculate max fee with the provided buffer fraction -// let max_fee = base_fee + (base_fee / buffer_fraction); -// kiprintln!("PROCESS_LIB::calculate_eip1559_gas max_fee: {}", max_fee); -// -// Ok((max_fee, priority_fee)) -//} -// -//// Network-specific gas calculation for Ethereum mainnet -//fn calculate_eth_mainnet_gas(provider: &Provider) -> Result<(u128, u128), WalletError> { -// // For mainnet: 50% buffer and 1.5 gwei priority fee -// calculate_eip1559_gas(provider, 2, 1_500_000_000u128) -//} -// -//// Gas calculation for Base -//fn calculate_base_gas(provider: &Provider) -> Result<(u128, u128), WalletError> { -// // Get the latest block to determine current gas conditions -// let latest_block = provider.get_block_by_number(BlockNumberOrTag::Latest, false)? -// .ok_or_else(|| WalletError::TransactionError("Failed to get latest block".into()))?; -// -// // Get base fee from the block -// let base_fee = latest_block.header.inner.base_fee_per_gas -// .ok_or_else(|| WalletError::TransactionError("No base fee in block".into()))? -// as u128; -// -// // Calculate max fee with a 33% buffer -// let max_fee = base_fee + (base_fee / 3); -// -// // Dynamic priority fee - 10% of base fee, but with a minimum and a maximum -// // Low minimum for Base which has very low gas prices -// let min_priority_fee = 100_000u128; // 0.0001 gwei minimum -// let max_priority_fee = max_fee / 2; // Never more than half the max fee -// -// let priority_fee = std::cmp::max( -// min_priority_fee, -// std::cmp::min(base_fee / 10, max_priority_fee) -// ); -// -// Ok((max_fee, priority_fee)) -//} -// -//// Gas calculation for Optimism network -//fn calculate_optimism_gas(provider: &Provider) -> Result<(u128, u128), WalletError> { -// // For Optimism: 25% buffer and 0.3 gwei priority fee -// calculate_eip1559_gas(provider, 4, 300_000_000u128) -//} -// -///// Get the ETH balance for an address or name -//pub fn get_eth_balance( -// address_or_name: &str, -// chain_id: u64, -// provider: Provider, -//) -> Result { -// // Resolve name to address -// let address = resolve_name(address_or_name, chain_id)?; -// -// // Query balance -// let balance = provider.get_balance(address, None)?; -// -// // Return formatted amount -// Ok(EthAmount { -// wei_value: balance, -// }) -//} -// -///// Wait for a transaction to be confirmed -//pub fn wait_for_transaction( -// tx_hash: TxHash, -// provider: Provider, -// confirmations: u64, -// timeout_secs: u64 -//) -> Result { -// let start_time = std::time::Instant::now(); -// let timeout = std::time::Duration::from_secs(timeout_secs); -// -// loop { -// // Check if we've exceeded the timeout -// if start_time.elapsed() > timeout { -// return Err(WalletError::TransactionError( -// format!("Transaction confirmation timeout after {} seconds", timeout_secs) -// )); -// } -// -// // Try to get the receipt -// if let Ok(Some(receipt)) = provider.get_transaction_receipt(tx_hash) { -// // Check if we have enough confirmations -// let latest_block = provider.get_block_number()?; -// let receipt_block = receipt.block_number.unwrap_or(0) as u64; -// -// if latest_block >= receipt_block + confirmations { -// return Ok(receipt); -// } -// } -// -// // Wait a bit before checking again -// std::thread::sleep(std::time::Duration::from_secs(2)); -// } -//} -// -//// -//// ERC-20 TOKEN FUNCTIONS -//// -// -///// Get the ERC20 token balance of an address or name -///// Returns the balance in token units (adjusted for decimals) -//pub fn erc20_balance_of( -// token_address: &str, -// owner_address: &str, -// provider: &Provider -//) -> Result { -// kiprintln!("PROCESS_LIB::erc20_balance_of starting"); -// -// // Resolve addresses -// let token = resolve_name(token_address, provider.chain_id)?; -// kiprintln!("PROCESS_LIB::erc20_balance_of token: {}", token); -// let owner = resolve_name(owner_address, provider.chain_id)?; -// kiprintln!("PROCESS_LIB::erc20_balance_of owner: {}", owner); -// -// // Create the call using sol! macro generated struct -// let call = IERC20::balanceOfCall { who: owner }; -// let call_data = call.abi_encode(); -// -// // Create the transaction request for eth_call -// let tx = TransactionRequest { -// to: Some(TxKind::Call(token)), -// input: call_data.into(), -// ..Default::default() -// }; -// -// // Call the contract -// let result = provider.call(tx, None)?; -// -// // Verify we got a valid result -// if result.is_empty() { -// return Err(WalletError::TransactionError("Empty result from balanceOf call".into())); -// } -// -// // Decode the result -// let balance = match IERC20::balanceOfCall::abi_decode_returns(&result, true) { -// Ok(decoded) => decoded._0, // Returns (U256,) -// Err(e) => return Err(WalletError::TransactionError( -// format!("Failed to decode balanceOf result: {}", e) -// )), -// }; -// -// // Get the token's decimals -// let decimals = erc20_decimals(token_address, provider)?; -// -// // Calculate the token amount -// let balance_u128 = balance.to::(); -// let balance_float = balance_u128 as f64 / 10f64.powi(decimals as i32); -// -// Ok(balance_float) -//} -// -///// Get the number of decimals for an ERC20 token -//pub fn erc20_decimals(token_address: &str, provider: &Provider) -> Result { -// kiprintln!("PROCESS_LIB::erc20_decimals starting"); -// -// // Resolve token address -// let token = resolve_name(token_address, provider.chain_id)?; -// kiprintln!("PROCESS_LIB::erc20_decimals token: {}", token); -// -// // Create the call using sol! macro generated struct -// let call = IERC20::decimalsCall {}; -// let call_data = call.abi_encode(); -// -// // Create the transaction request -// let tx = TransactionRequest { -// to: Some(TxKind::Call(token)), -// input: call_data.into(), -// ..Default::default() -// }; -// -// // Call the contract -// let result = provider.call(tx, None)?; -// kiprintln!("PROCESS_LIB::erc20_decimals result received"); -// -// // Verify we got a valid result -// if result.is_empty() { -// return Err(WalletError::TransactionError("Empty result from decimals call".into())); -// } -// -// // Decode the result -// let decimals = match IERC20::decimalsCall::abi_decode_returns(&result, true) { -// Ok(decoded) => decoded._0, // Returns (u8,) -// Err(e) => return Err(WalletError::TransactionError( -// format!("Failed to decode decimals result: {}", e) -// )), -// }; -// -// Ok(decimals) -//} -// -///// Get the token symbol for an ERC20 token -//pub fn erc20_symbol(token_address: &str, provider: &Provider) -> Result { -// kiprintln!("PROCESS_LIB::erc20_symbol starting"); -// -// // Resolve token address -// let token = resolve_name(token_address, provider.chain_id)?; -// -// // Create the call using sol! macro generated struct -// let call = IERC20::symbolCall {}; -// let call_data = call.abi_encode(); -// -// // Create the transaction request -// let tx = TransactionRequest { -// to: Some(TxKind::Call(token)), -// input: call_data.into(), -// ..Default::default() -// }; -// -// // Call the contract -// let result = provider.call(tx, None)?; -// -// // Verify we got a valid result -// if result.is_empty() { -// return Err(WalletError::TransactionError("Empty result from symbol call".into())); -// } -// -// // Decode the result -// let symbol = match IERC20::symbolCall::abi_decode_returns(&result, true) { -// Ok(decoded) => decoded._0, // Returns (String,) -// Err(e) => return Err(WalletError::TransactionError( -// format!("Failed to decode symbol result: {}", e) -// )), -// }; -// -// Ok(symbol) -//} -// -///// Get the token name for an ERC20 token -//pub fn erc20_name(token_address: &str, provider: &Provider) -> Result { -// kiprintln!("PROCESS_LIB::erc20_name starting"); -// -// // Resolve token address -// let token = resolve_name(token_address, provider.chain_id)?; -// -// // Create the call using sol! macro generated struct -// let call = IERC20::nameCall {}; -// let call_data = call.abi_encode(); -// -// // Create the transaction request -// let tx = TransactionRequest { -// to: Some(TxKind::Call(token)), -// input: call_data.into(), -// ..Default::default() -// }; -// -// // Call the contract -// let result = provider.call(tx, None)?; -// -// // Verify we got a valid result -// if result.is_empty() { -// return Err(WalletError::TransactionError("Empty result from name call".into())); -// } -// -// // Decode the result -// let name = match IERC20::nameCall::abi_decode_returns(&result, true) { -// Ok(decoded) => decoded._0, // Returns (String,) -// Err(e) => return Err(WalletError::TransactionError( -// format!("Failed to decode name result: {}", e) -// )), -// }; -// -// Ok(name) -//} -// -///// Get the total supply of an ERC20 token -//pub fn erc20_total_supply(token_address: &str, provider: &Provider) -> Result { -// kiprintln!("PROCESS_LIB::erc20_total_supply starting"); -// -// // Resolve token address -// let token = resolve_name(token_address, provider.chain_id)?; -// -// // Create the call using sol! macro generated struct -// let call = IERC20::totalSupplyCall {}; -// let call_data = call.abi_encode(); -// -// // Create the transaction request -// let tx = TransactionRequest { -// to: Some(TxKind::Call(token)), -// input: call_data.into(), -// ..Default::default() -// }; -// -// // Call the contract -// let result = provider.call(tx, None)?; -// -// // Verify we got a valid result -// if result.is_empty() { -// return Err(WalletError::TransactionError("Empty result from totalSupply call".into())); -// } -// -// // Decode the result -// let total_supply = match IERC20::totalSupplyCall::abi_decode_returns(&result, true) { -// Ok(decoded) => decoded._0, // Returns (U256,) -// Err(e) => return Err(WalletError::TransactionError( -// format!("Failed to decode totalSupply result: {}", e) -// )), -// }; -// -// Ok(total_supply) -//} -// -///// Get the allowance for an ERC20 token -//pub fn erc20_allowance( -// token_address: &str, -// owner_address: &str, -// spender_address: &str, -// provider: &Provider -//) -> Result { -// kiprintln!("PROCESS_LIB::erc20_allowance starting"); -// -// // Resolve addresses -// let token = resolve_name(token_address, provider.chain_id)?; -// let owner = resolve_name(owner_address, provider.chain_id)?; -// let spender = resolve_name(spender_address, provider.chain_id)?; -// -// // Create the call using sol! macro generated struct -// let call = IERC20::allowanceCall { owner, spender }; -// let call_data = call.abi_encode(); -// -// // Create the transaction request -// let tx = TransactionRequest { -// to: Some(TxKind::Call(token)), -// input: call_data.into(), -// ..Default::default() -// }; -// -// // Call the contract -// let result = provider.call(tx, None)?; -// -// // Verify we got a valid result -// if result.is_empty() { -// return Err(WalletError::TransactionError("Empty result from allowance call".into())); -// } -// -// // Decode the result -// let allowance = match IERC20::allowanceCall::abi_decode_returns(&result, true) { -// Ok(decoded) => decoded._0, // Returns (U256,) -// Err(e) => return Err(WalletError::TransactionError( -// format!("Failed to decode allowance result: {}", e) -// )), -// }; -// -// Ok(allowance) -//} -// -///// Transfer ERC20 tokens to an address or name -//pub fn erc20_transfer( -// token_address: &str, -// to_address: &str, -// amount: U256, -// provider: &Provider, -// signer: &S -//) -> Result { -// kiprintln!("PROCESS_LIB::erc20_transfer starting"); -// -// // Resolve addresses -// let token = resolve_name(token_address, provider.chain_id)?; -// let to = resolve_name(to_address, provider.chain_id)?; -// -// // Create the call using sol! macro generated struct -// let call = IERC20::transferCall { to, value: amount }; -// let call_data = call.abi_encode(); -// -// // Get the current nonce for the signer's address -// let from_address = signer.address(); -// let nonce = provider.get_transaction_count(from_address, None)? -// .to::(); -// -// // Estimate gas for the token transfer -// let tx_req = TransactionRequest { -// from: Some(from_address), -// to: Some(TxKind::Call(token)), -// input: call_data.clone().into(), -// ..Default::default() -// }; -// -// // Try to estimate gas, fall back to 100,000 if estimation fails -// let gas_limit = match provider.estimate_gas(tx_req, None) { -// Ok(gas) => (gas.to::() * 120) / 100, // Add 20% buffer -// Err(_) => 100_000, // Default gas limit for ERC20 transfers -// }; -// -// // Calculate gas price based on the chain -// let (gas_price, priority_fee) = match signer.chain_id() { -// 1 => calculate_eth_mainnet_gas(provider)?, -// 8453 => calculate_base_gas(provider)?, -// 10 => calculate_optimism_gas(provider)?, -// _ => { -// let base_fee = provider.get_gas_price()?.to::(); -// let adjusted_fee = (base_fee * 130) / 100; -// (adjusted_fee, adjusted_fee / 10) -// } -// }; -// -// // Prepare transaction data -// let tx_data = TransactionData { -// to: token, -// value: U256::ZERO, // No ETH sent with token transfers -// data: Some(call_data), -// nonce, -// gas_limit, -// gas_price, -// max_priority_fee: Some(priority_fee), -// chain_id: signer.chain_id(), -// }; -// -// // Sign and send transaction -// let signed_tx = signer.sign_transaction(&tx_data)?; -// let tx_hash = provider.send_raw_transaction(signed_tx.into())?; -// -// // Get token details to improve the receipt message -// let token_symbol = match erc20_symbol(token_address, provider) { -// Ok(symbol) => symbol, -// Err(_) => "tokens".to_string(), -// }; -// -// let token_decimals = match erc20_decimals(token_address, provider) { -// Ok(decimals) => decimals, -// Err(_) => 18, // Assume 18 decimals if unavailable -// }; -// -// // Format amount in token units -// let amount_float = amount.to::() as f64 / 10f64.powi(token_decimals as i32); -// -// Ok(TxReceipt { -// hash: tx_hash, -// details: format!("Transferred {:.6} {} to {}", amount_float, token_symbol, to_address), -// }) -//} -// -///// Approve an address to spend ERC20 tokens -//pub fn erc20_approve( -// token_address: &str, -// spender_address: &str, -// amount: U256, -// provider: &Provider, -// signer: &S -//) -> Result { -// kiprintln!("PROCESS_LIB::erc20_approve starting"); -// -// // Resolve addresses -// let token = resolve_name(token_address, provider.chain_id)?; -// let spender = resolve_name(spender_address, provider.chain_id)?; -// -// // Create the call using sol! macro generated struct -// let call = IERC20::approveCall { spender, value: amount }; -// let call_data = call.abi_encode(); -// -// // Get the current nonce for the signer's address -// let from_address = signer.address(); -// let nonce = provider.get_transaction_count(from_address, None)? -// .to::(); -// -// // Estimate gas -// let tx_req = TransactionRequest { -// from: Some(from_address), -// to: Some(TxKind::Call(token)), -// input: call_data.clone().into(), -// ..Default::default() -// }; -// -// // Try to estimate gas, fall back to 60,000 if estimation fails -// let gas_limit = match provider.estimate_gas(tx_req, None) { -// Ok(gas) => (gas.to::() * 120) / 100, // Add 20% buffer -// Err(_) => 60_000, // Default gas limit for ERC20 approvals -// }; -// -// // Calculate gas price based on the chain -// let (gas_price, priority_fee) = match signer.chain_id() { -// 1 => calculate_eth_mainnet_gas(provider)?, -// 8453 => calculate_base_gas(provider)?, -// 10 => calculate_optimism_gas(provider)?, -// _ => { -// let base_fee = provider.get_gas_price()?.to::(); -// let adjusted_fee = (base_fee * 130) / 100; -// (adjusted_fee, adjusted_fee / 10) -// } -// }; -// -// // Prepare transaction data -// let tx_data = TransactionData { -// to: token, -// value: U256::ZERO, // No ETH sent with token approvals -// data: Some(call_data), -// nonce, -// gas_limit, -// gas_price, -// max_priority_fee: Some(priority_fee), -// chain_id: signer.chain_id(), -// }; -// -// // Sign and send transaction -// let signed_tx = signer.sign_transaction(&tx_data)?; -// let tx_hash = provider.send_raw_transaction(signed_tx.into())?; -// -// // Get token details to improve the receipt message -// let token_symbol = match erc20_symbol(token_address, provider) { -// Ok(symbol) => symbol, -// Err(_) => "tokens".to_string(), -// }; -// -// let token_decimals = match erc20_decimals(token_address, provider) { -// Ok(decimals) => decimals, -// Err(_) => 18, // Assume 18 decimals if unavailable -// }; -// -// // Format amount in token units -// let amount_float = amount.to::() as f64 / 10f64.powi(token_decimals as i32); -// -// Ok(TxReceipt { -// hash: tx_hash, -// details: format!("Approved {:.6} {} to be spent by {}", amount_float, token_symbol, spender_address), -// }) -//} -// -///// Transfer ERC20 tokens from one address to another (requires approval) -//pub fn erc20_transfer_from( -// token_address: &str, -// from_address: &str, -// to_address: &str, -// amount: U256, -// provider: &Provider, -// signer: &S -//) -> Result { -// kiprintln!("PROCESS_LIB::erc20_transfer_from starting"); -// -// // Resolve addresses -// let token = resolve_name(token_address, provider.chain_id)?; -// let from = resolve_name(from_address, provider.chain_id)?; -// let to = resolve_name(to_address, provider.chain_id)?; -// -// // Create the call using sol! macro generated struct -// let call = IERC20::transferFromCall { from, to, value: amount }; -// let call_data = call.abi_encode(); -// -// // Get the current nonce for the signer's address -// let signer_address = signer.address(); -// let nonce = provider.get_transaction_count(signer_address, None)? -// .to::(); -// -// // Estimate gas -// let tx_req = TransactionRequest { -// from: Some(signer_address), -// to: Some(TxKind::Call(token)), -// input: call_data.clone().into(), -// ..Default::default() -// }; -// -// // Try to estimate gas, fall back to 100,000 if estimation fails -// let gas_limit = match provider.estimate_gas(tx_req, None) { -// Ok(gas) => (gas.to::() * 120) / 100, // Add 20% buffer -// Err(_) => 100_000, // Default gas limit -// }; -// -// // Calculate gas price based on the chain -// let (gas_price, priority_fee) = match signer.chain_id() { -// 1 => calculate_eth_mainnet_gas(provider)?, -// 8453 => calculate_base_gas(provider)?, -// 10 => calculate_optimism_gas(provider)?, -// _ => { -// let base_fee = provider.get_gas_price()?.to::(); -// let adjusted_fee = (base_fee * 130) / 100; -// (adjusted_fee, adjusted_fee / 10) -// } -// }; -// -// // Prepare transaction data -// let tx_data = TransactionData { -// to: token, -// value: U256::ZERO, // No ETH sent with token transfers -// data: Some(call_data), -// nonce, -// gas_limit, -// gas_price, -// max_priority_fee: Some(priority_fee), -// chain_id: signer.chain_id(), -// }; -// -// // Sign and send transaction -// let signed_tx = signer.sign_transaction(&tx_data)?; -// let tx_hash = provider.send_raw_transaction(signed_tx.into())?; -// -// // Get token details to improve the receipt message -// let token_symbol = match erc20_symbol(token_address, provider) { -// Ok(symbol) => symbol, -// Err(_) => "tokens".to_string(), -// }; -// -// let token_decimals = match erc20_decimals(token_address, provider) { -// Ok(decimals) => decimals, -// Err(_) => 18, // Assume 18 decimals if unavailable -// }; -// -// // Format amount in token units -// let amount_float = amount.to::() as f64 / 10f64.powi(token_decimals as i32); -// -// Ok(TxReceipt { -// hash: tx_hash, -// details: format!("Transferred {:.6} {} from {} to {}", -// amount_float, token_symbol, from_address, to_address), -// }) -//} -// -//// -//// HYPERMAP AND ERC-6551 FUNCTIONS -//// -// -///// Result type for Hypermap transactions -//#[derive(Debug, Clone)] -//pub struct HypermapTxReceipt { -// /// Transaction hash -// pub hash: TxHash, -// /// Description of the operation -// pub description: String, -//} -// -///// Create a note (mutable data) on a Hypermap namespace entry -//pub fn create_note( -// parent_entry: &str, -// note_key: &str, -// data: Vec, -// provider: Provider, -// signer: &S, -//) -> Result { -// // Verify the note key is valid -// if !valid_note(note_key) { -// return Err(WalletError::NameResolutionError( -// format!("Invalid note key '{}'. Must start with '~' and contain only lowercase letters, numbers, and hyphens", note_key) -// )); -// } -// -// // Get the parent TBA address -// let hypermap = provider.hypermap(); -// let parent_hash_str = namehash(parent_entry); -// -// println!("Parent entry: {}", parent_entry); -// println!("Parent hash: {}", parent_hash_str); -// -// let (tba, owner, _) = hypermap.get_hash(&parent_hash_str)?; -// -// println!("TBA address (parent): {}", tba); -// println!("Owner address: {}", owner); -// -// // Check that the signer is the owner of the parent entry -// let signer_address = signer.address(); -// println!("Signer address: {}", signer_address); -// -// if signer_address != owner { -// return Err(WalletError::PermissionDenied( -// format!("Signer address {} does not own the parent entry {}", signer_address, parent_entry) -// )); -// } -// -// // Get the hypermap contract address -// let hypermap_address = *hypermap.address(); -// println!("Hypermap contract address: {}", hypermap_address); -// -// // Create the note function call data -// let note_function = hypermap::contract::noteCall { -// note: Bytes::from(note_key.as_bytes().to_vec()), -// data: Bytes::from(data), -// }; -// let note_call_data = note_function.abi_encode(); -// -// // Now create an ERC-6551 execute call to send from the wallet to the TBA -// let execute_call = IERC6551Account::executeCall { -// to: hypermap_address, -// value: U256::ZERO, -// data: Bytes::from(note_call_data), -// operation: 0 // CALL operation -// }; -// let execute_call_data = execute_call.abi_encode(); -// -// // Send the transaction from the wallet to the TBA -// let (tx_hash, _) = send_transaction( -// tba, -// execute_call_data.into(), -// U256::ZERO, -// provider, -// signer -// )?; -// -// // Return the receipt with transaction details -// Ok(HypermapTxReceipt { -// hash: tx_hash, -// description: format!("Created note '{}' on '{}'", note_key, parent_entry), -// }) -//} -// -///// Create a fact (immutable data) on a Hypermap namespace entry -//pub fn create_fact( -// parent_entry: &str, -// fact_key: &str, -// data: Vec, -// provider: Provider, -// signer: &S, -//) -> Result { -// // Verify the fact key is valid -// if !valid_fact(fact_key) { -// return Err(WalletError::NameResolutionError( -// format!("Invalid fact key '{}'. Must start with '!' and contain only lowercase letters, numbers, and hyphens", fact_key) -// )); -// } -// -// // Get the parent TBA address -// let hypermap = provider.hypermap(); -// let parent_hash_str = namehash(parent_entry); -// let (tba, owner, _) = hypermap.get_hash(&parent_hash_str)?; -// -// // Check that the signer is the owner of the parent entry -// let signer_address = signer.address(); -// if signer_address != owner { -// return Err(WalletError::PermissionDenied( -// format!("Signer address {} does not own the parent entry {}", signer_address, parent_entry) -// )); -// } -// -// // Get the hypermap contract address -// let hypermap_address = *hypermap.address(); -// -// // Create the fact function call data -// let fact_function = hypermap::contract::factCall { -// fact: Bytes::from(fact_key.as_bytes().to_vec()), -// data: Bytes::from(data), -// }; -// let fact_call_data = fact_function.abi_encode(); -// -// // Create an ERC-6551 execute call -// let execute_call = IERC6551Account::executeCall { -// to: hypermap_address, -// value: U256::ZERO, -// data: Bytes::from(fact_call_data), -// operation: 0 // CALL operation -// }; -// let execute_call_data = execute_call.abi_encode(); -// -// // Send the transaction -// let (tx_hash, _) = send_transaction( -// tba, -// execute_call_data.into(), -// U256::ZERO, -// provider, -// signer -// )?; -// -// // Return the receipt with transaction details -// Ok(HypermapTxReceipt { -// hash: tx_hash, -// description: format!("Created fact '{}' on '{}'", fact_key, parent_entry), -// }) -//} -// -///// Mint a new namespace entry under a parent entry -//pub fn mint_entry( -// parent_entry: &str, -// label: &str, -// recipient: &str, -// implementation: &str, -// provider: Provider, -// signer: &S, -//) -> Result { -// // Verify the label is valid -// if !valid_name(label) { -// return Err(WalletError::NameResolutionError( -// format!("Invalid label '{}'. Must contain only lowercase letters, numbers, and hyphens", label) -// )); -// } -// -// // Get the parent TBA address -// let hypermap = provider.hypermap(); -// let parent_hash_str = namehash(parent_entry); -// kiprintln!("PROCESS_LIB::mint_entry parent_hash_str: {}", parent_hash_str); -// let (tba, owner, _) = hypermap.get_hash(&parent_hash_str)?; -// -// // Check that the signer is the owner of the parent entry -// let signer_address = signer.address(); -// if signer_address != owner { -// return Err(WalletError::PermissionDenied( -// format!("Signer address {} does not own the parent entry {}", signer_address, parent_entry) -// )); -// } -// -// // Resolve recipient address -// let recipient_address = resolve_name(recipient, provider.chain_id)?; -// -// // Resolve implementation address -// let implementation_address = resolve_name(implementation, provider.chain_id)?; -// -// // Get the hypermap contract address -// let hypermap_address = *hypermap.address(); -// -// // Create the mint function call data -// let mint_function = hypermap::contract::mintCall { -// who: recipient_address, -// label: Bytes::from(label.as_bytes().to_vec()), -// initialization: Bytes::default(), // No initialization data -// erc721Data: Bytes::default(), // No ERC721 data -// implementation: implementation_address, -// }; -// let mint_call_data = mint_function.abi_encode(); -// -// // Create an ERC-6551 execute call -// let execute_call = IERC6551Account::executeCall { -// to: hypermap_address, -// value: U256::ZERO, -// data: Bytes::from(mint_call_data), -// operation: 0 // CALL operation -// }; -// let execute_call_data = execute_call.abi_encode(); -// -// kiprintln!("Parent entry: {}", parent_entry); -// kiprintln!("Parent hash: {}", parent_hash_str); -// kiprintln!("TBA address: {}", tba); -// kiprintln!("Owner address: {}", owner); -// kiprintln!("Signer address: {}", signer_address); -// -// // Send the transaction -// let (tx_hash, _) = send_transaction( -// tba, -// execute_call_data.into(), -// U256::ZERO, -// provider, -// signer -// )?; -// -// // Return the receipt with transaction details -// Ok(HypermapTxReceipt { -// hash: tx_hash, -// description: format!("Minted new entry '{}' under '{}'", label, parent_entry), -// }) -//} -// -///// Set the gene for a namespace entry -//pub fn set_gene( -// entry: &str, -// gene_implementation: &str, -// provider: Provider, -// signer: &S, -//) -> Result { -// // Get the entry's TBA address -// let hypermap = provider.hypermap(); -// let entry_hash_str = namehash(entry); -// let (tba, owner, _) = hypermap.get_hash(&entry_hash_str)?; -// -// // Check that the signer is the owner of the entry -// let signer_address = signer.address(); -// if signer_address != owner { -// return Err(WalletError::PermissionDenied( -// format!("Signer address {} does not own the entry {}", signer_address, entry) -// )); -// } -// -// // Resolve gene implementation address -// let gene_address = resolve_name(gene_implementation, provider.chain_id)?; -// -// // Get the hypermap contract address -// let hypermap_address = *hypermap.address(); -// -// // Create the gene function call data -// let gene_function = hypermap::contract::geneCall { -// _gene: gene_address, -// }; -// let gene_call_data = gene_function.abi_encode(); -// -// // Create an ERC-6551 execute call -// let execute_call = IERC6551Account::executeCall { -// to: hypermap_address, -// value: U256::ZERO, -// data: Bytes::from(gene_call_data), -// operation: 0 // CALL operation -// }; -// let execute_call_data = execute_call.abi_encode(); -// -// // Send the transaction -// let (tx_hash, _) = send_transaction( -// tba, -// execute_call_data.into(), -// U256::ZERO, -// provider, -// signer -// )?; -// -// // Return the receipt with transaction details -// Ok(HypermapTxReceipt { -// hash: tx_hash, -// description: format!("Set gene for '{}' to '{}'", entry, gene_implementation), -// }) -//} -// -///// Send a transaction to an address with custom data and value -//fn send_transaction( -// to: EthAddress, -// data: Bytes, -// value: U256, -// provider: Provider, -// signer: &S, -//) -> Result<(TxHash, Vec), WalletError> { -// let chain_id = signer.chain_id(); -// -// kiprintln!("PROCESS_LIB::send_transaction starting"); -// kiprintln!("PROCESS_LIB::send_transaction chain_id: {}", chain_id); -// -// // Get gas estimates - use 50% buffer for Base to ensure acceptance -// let base_fee = provider.get_gas_price()?.to::(); -// let gas_price = (base_fee * 150) / 100; // 50% buffer -// let priority_fee = gas_price / 5; // 20% of gas price -// -// kiprintln!("PROCESS_LIB::send_transaction base_fee: {}, priority_fee: {}", base_fee, priority_fee); -// kiprintln!("PROCESS_LIB::send_transaction gas_price: {}", gas_price); -// -// // Get the current nonce for the signer's address -// let from_address = signer.address(); -// let nonce = provider.get_transaction_count(from_address, None)? -// .to::(); -// -// kiprintln!("PROCESS_LIB::send_transaction nonce: {}", nonce); -// -// // For ERC-6551 account operations, use a higher gas limit -// // The ERC-6551 execute function is complex and gas-intensive -// let estimated_gas = 500_000; // Start high for ERC-6551 -// -// // Add 50% buffer to estimated gas since this is a complex operation -// let gas_limit = (estimated_gas * 150) / 100; -// -// kiprintln!("PROCESS_LIB::send_transaction estimated_gas: {}", estimated_gas); -// kiprintln!("PROCESS_LIB::send_transaction gas_limit: {}", gas_limit); -// -// // Prepare transaction data -// let tx_data = TransactionData { -// to, -// value, -// data: Some(data.to_vec()), -// nonce, -// gas_limit, -// gas_price, -// max_priority_fee: Some(priority_fee), -// chain_id, -// }; -// -// // Sign the transaction -// let signed_tx = signer.sign_transaction(&tx_data)?; -// kiprintln!("PROCESS_LIB::send_transaction signed"); -// -// // Send the transaction -// let tx_hash = provider.send_raw_transaction(signed_tx.clone().into())?; -// kiprintln!("PROCESS_LIB::send_transaction tx_hash: {}", tx_hash); -// -// // Return both the hash and the raw transaction data -// Ok((tx_hash, signed_tx)) -//} -// -//// -//// ERC-721 NFT FUNCTIONS -//// -// -///// Get the NFT balance of an address -//pub fn erc721_balance_of( -// token_address: &str, -// owner_address: &str, -// provider: &Provider -//) -> Result { -// kiprintln!("PROCESS_LIB::erc721_balance_of starting"); -// -// // Resolve addresses -// let token = resolve_name(token_address, provider.chain_id)?; -// let owner = resolve_name(owner_address, provider.chain_id)?; -// -// // Create the call using sol! macro generated struct -// let call = IERC721::balanceOfCall { owner }; -// let call_data = call.abi_encode(); -// -// // Create the transaction request -// let tx = TransactionRequest { -// to: Some(TxKind::Call(token)), -// input: call_data.into(), -// ..Default::default() -// }; -// -// // Call the contract -// let result = provider.call(tx, None)?; -// -// // Verify we got a valid result -// if result.is_empty() { -// return Err(WalletError::TransactionError("Empty result from balanceOf call".into())); -// } -// -// // Decode the result -// let balance = match IERC721::balanceOfCall::abi_decode_returns(&result, true) { -// Ok(decoded) => decoded._0, // Returns (U256,) -// Err(e) => return Err(WalletError::TransactionError( -// format!("Failed to decode balanceOf result: {}", e) -// )), -// }; -// -// Ok(balance) -//} -// -///// Get the owner of an NFT token -//pub fn erc721_owner_of( -// token_address: &str, -// token_id: U256, -// provider: &Provider -//) -> Result { -// kiprintln!("PROCESS_LIB::erc721_owner_of starting"); -// -// // Resolve token address -// let token = resolve_name(token_address, provider.chain_id)?; -// -// // Create the call using sol! macro generated struct -// let call = IERC721::ownerOfCall { tokenId: token_id }; -// let call_data = call.abi_encode(); -// -// // Create the transaction request -// let tx = TransactionRequest { -// to: Some(TxKind::Call(token)), -// input: call_data.into(), -// ..Default::default() -// }; -// -// // Call the contract -// let result = provider.call(tx, None)?; -// -// // Verify we got a valid result -// if result.is_empty() { -// return Err(WalletError::TransactionError("Empty result from ownerOf call".into())); -// } -// -// // Decode the result -// let owner = match IERC721::ownerOfCall::abi_decode_returns(&result, true) { -// Ok(decoded) => decoded._0, // Returns (address,) -// Err(e) => return Err(WalletError::TransactionError( -// format!("Failed to decode ownerOf result: {}", e) -// )), -// }; -// -// Ok(owner) -//} -// -///// Check if an operator is approved for all NFTs of an owner -//pub fn erc721_is_approved_for_all( -// token_address: &str, -// owner_address: &str, -// operator_address: &str, -// provider: &Provider -//) -> Result { -// kiprintln!("PROCESS_LIB::erc721_is_approved_for_all starting"); -// -// // Resolve addresses -// let token = resolve_name(token_address, provider.chain_id)?; -// let owner = resolve_name(owner_address, provider.chain_id)?; -// let operator = resolve_name(operator_address, provider.chain_id)?; -// -// // Create the call using sol! macro generated struct -// let call = IERC721::isApprovedForAllCall { owner, operator }; -// let call_data = call.abi_encode(); -// -// // Create the transaction request -// let tx = TransactionRequest { -// to: Some(TxKind::Call(token)), -// input: call_data.into(), -// ..Default::default() -// }; -// -// // Call the contract -// let result = provider.call(tx, None)?; -// -// // Verify we got a valid result -// if result.is_empty() { -// return Err(WalletError::TransactionError("Empty result from isApprovedForAll call".into())); -// } -// -// // Decode the result -// let is_approved = match IERC721::isApprovedForAllCall::abi_decode_returns(&result, true) { -// Ok(decoded) => decoded._0, // Returns (bool,) -// Err(e) => return Err(WalletError::TransactionError( -// format!("Failed to decode isApprovedForAll result: {}", e) -// )), -// }; -// -// Ok(is_approved) -//} -// -///// Safely transfer an NFT -//pub fn erc721_safe_transfer_from( -// token_address: &str, -// from_address: &str, -// to_address: &str, -// token_id: U256, -// provider: &Provider, -// signer: &S -//) -> Result { -// kiprintln!("PROCESS_LIB::erc721_safe_transfer_from starting"); -// -// // Resolve addresses -// let token = resolve_name(token_address, provider.chain_id)?; -// let from = resolve_name(from_address, provider.chain_id)?; -// let to = resolve_name(to_address, provider.chain_id)?; -// -// // Create the call using sol! macro generated struct -// let call = IERC721::safeTransferFromCall { from, to, tokenId: token_id }; -// let call_data = call.abi_encode(); -// -// // Get the current nonce for the signer's address -// let signer_address = signer.address(); -// let nonce = provider.get_transaction_count(signer_address, None)? -// .to::(); -// -// // Estimate gas -// let tx_req = TransactionRequest { -// from: Some(signer_address), -// to: Some(TxKind::Call(token)), -// input: call_data.clone().into(), -// ..Default::default() -// }; -// -// // Try to estimate gas, fall back to 200,000 if estimation fails -// let gas_limit = match provider.estimate_gas(tx_req, None) { -// Ok(gas) => (gas.to::() * 120) / 100, // Add 20% buffer -// Err(_) => 200_000, // Default gas limit for NFT transfers (higher than ERC20) -// }; -// -// // Calculate gas price based on the chain -// let (gas_price, priority_fee) = match signer.chain_id() { -// 1 => calculate_eth_mainnet_gas(provider)?, -// 8453 => calculate_base_gas(provider)?, -// 10 => calculate_optimism_gas(provider)?, -// _ => { -// let base_fee = provider.get_gas_price()?.to::(); -// let adjusted_fee = (base_fee * 130) / 100; -// (adjusted_fee, adjusted_fee / 10) -// } -// }; -// -// // Prepare transaction data -// let tx_data = TransactionData { -// to: token, -// value: U256::ZERO, // No ETH sent with NFT transfers -// data: Some(call_data), -// nonce, -// gas_limit, -// gas_price, -// max_priority_fee: Some(priority_fee), -// chain_id: signer.chain_id(), -// }; -// -// // Sign and send transaction -// let signed_tx = signer.sign_transaction(&tx_data)?; -// let tx_hash = provider.send_raw_transaction(signed_tx.into())?; -// -// Ok(TxReceipt { -// hash: tx_hash, -// details: format!("Safely transferred NFT ID {} from {} to {}", token_id, from_address, to_address), -// }) -//} -// -///// Set approval for all tokens to an operator -//pub fn erc721_set_approval_for_all( -// token_address: &str, -// operator_address: &str, -// approved: bool, -// provider: &Provider, -// signer: &S -//) -> Result { -// kiprintln!("PROCESS_LIB::erc721_set_approval_for_all starting"); -// -// // Resolve addresses -// let token = resolve_name(token_address, provider.chain_id)?; -// let operator = resolve_name(operator_address, provider.chain_id)?; -// -// // Create the call using sol! macro generated struct -// let call = IERC721::setApprovalForAllCall { operator, approved }; -// let call_data = call.abi_encode(); -// -// // Get the current nonce for the signer's address -// let signer_address = signer.address(); -// let nonce = provider.get_transaction_count(signer_address, None)? -// .to::(); -// -// // Estimate gas -// let tx_req = TransactionRequest { -// from: Some(signer_address), -// to: Some(TxKind::Call(token)), -// input: call_data.clone().into(), -// ..Default::default() -// }; -// -// // Try to estimate gas, fall back to 60,000 if estimation fails -// let gas_limit = match provider.estimate_gas(tx_req, None) { -// Ok(gas) => (gas.to::() * 120) / 100, // Add 20% buffer -// Err(_) => 60_000, // Default gas limit -// }; -// -// // Calculate gas price based on the chain -// let (gas_price, priority_fee) = match signer.chain_id() { -// 1 => calculate_eth_mainnet_gas(provider)?, -// 8453 => calculate_base_gas(provider)?, -// 10 => calculate_optimism_gas(provider)?, -// _ => { -// let base_fee = provider.get_gas_price()?.to::(); -// let adjusted_fee = (base_fee * 130) / 100; -// (adjusted_fee, adjusted_fee / 10) -// } -// }; -// -// // Prepare transaction data -// let tx_data = TransactionData { -// to: token, -// value: U256::ZERO, -// data: Some(call_data), -// nonce, -// gas_limit, -// gas_price, -// max_priority_fee: Some(priority_fee), -// chain_id: signer.chain_id(), -// }; -// -// // Sign and send transaction -// let signed_tx = signer.sign_transaction(&tx_data)?; -// let tx_hash = provider.send_raw_transaction(signed_tx.into())?; -// -// Ok(TxReceipt { -// hash: tx_hash, -// details: format!( -// "{} operator {} to manage all of your NFTs in contract {}", -// if approved { "Approved" } else { "Revoked approval for" }, -// operator_address, -// token_address -// ), -// }) -//} -// -//// -//// ERC-1155 MULTI-TOKEN FUNCTIONS -//// -// -///// Get the balance of a specific token ID for an account -//pub fn erc1155_balance_of( -// token_address: &str, -// account_address: &str, -// token_id: U256, -// provider: &Provider -//) -> Result { -// kiprintln!("PROCESS_LIB::erc1155_balance_of starting"); -// -// // Resolve addresses -// let token = resolve_name(token_address, provider.chain_id)?; -// let account = resolve_name(account_address, provider.chain_id)?; -// -// // Create the call using sol! macro generated struct -// let call = IERC1155::balanceOfCall { account, id: token_id }; -// let call_data = call.abi_encode(); -// -// // Create the transaction request -// let tx = TransactionRequest { -// to: Some(TxKind::Call(token)), -// input: call_data.into(), -// ..Default::default() -// }; -// -// // Call the contract -// let result = provider.call(tx, None)?; -// -// // Verify we got a valid result -// if result.is_empty() { -// return Err(WalletError::TransactionError("Empty result from balanceOf call".into())); -// } -// -// // Decode the result -// let balance = match IERC1155::balanceOfCall::abi_decode_returns(&result, true) { -// Ok(decoded) => decoded._0, // Returns (U256,) -// Err(e) => return Err(WalletError::TransactionError( -// format!("Failed to decode balanceOf result: {}", e) -// )), -// }; -// -// Ok(balance) -//} -// -///// Get balances for multiple accounts and token IDs -//pub fn erc1155_balance_of_batch( -// token_address: &str, -// account_addresses: Vec<&str>, -// token_ids: Vec, -// provider: &Provider -//) -> Result, WalletError> { -// kiprintln!("PROCESS_LIB::erc1155_balance_of_batch starting"); -// -// // Check that arrays are same length -// if account_addresses.len() != token_ids.len() { -// return Err(WalletError::TransactionError( -// "Arrays of accounts and token IDs must have the same length".into() -// )); -// } -// -// // Resolve token address -// let token = resolve_name(token_address, provider.chain_id)?; -// -// // Resolve all account addresses -// let mut accounts = Vec::with_capacity(account_addresses.len()); -// for addr in account_addresses { -// accounts.push(resolve_name(addr, provider.chain_id)?); -// } -// -// // Create the call using sol! macro generated struct -// let call = IERC1155::balanceOfBatchCall { accounts, ids: token_ids }; -// let call_data = call.abi_encode(); -// -// // Create the transaction request -// let tx = TransactionRequest { -// to: Some(TxKind::Call(token)), -// input: call_data.into(), -// ..Default::default() -// }; -// -// // Call the contract -// let result = provider.call(tx, None)?; -// -// // Verify we got a valid result -// if result.is_empty() { -// return Err(WalletError::TransactionError("Empty result from balanceOfBatch call".into())); -// } -// -// // Decode the result -// let balances = match IERC1155::balanceOfBatchCall::abi_decode_returns(&result, true) { -// Ok(decoded) => decoded._0, // Returns (Vec,) -// Err(e) => return Err(WalletError::TransactionError( -// format!("Failed to decode balanceOfBatch result: {}", e) -// )), -// }; -// -// Ok(balances) -//} -// -///// Check if an operator is approved for all tokens of an account -//pub fn erc1155_is_approved_for_all( -// token_address: &str, -// account_address: &str, -// operator_address: &str, -// provider: &Provider -//) -> Result { -// kiprintln!("PROCESS_LIB::erc1155_is_approved_for_all starting"); -// -// // Resolve addresses -// let token = resolve_name(token_address, provider.chain_id)?; -// let account = resolve_name(account_address, provider.chain_id)?; -// let operator = resolve_name(operator_address, provider.chain_id)?; -// -// // Create the call using sol! macro generated struct -// let call = IERC1155::isApprovedForAllCall { account, operator }; -// let call_data = call.abi_encode(); -// -// // Create the transaction request -// let tx = TransactionRequest { -// to: Some(TxKind::Call(token)), -// input: call_data.into(), -// ..Default::default() -// }; -// -// // Call the contract -// let result = provider.call(tx, None)?; -// -// // Verify we got a valid result -// if result.is_empty() { -// return Err(WalletError::TransactionError("Empty result from isApprovedForAll call".into())); -// } -// -// // Decode the result -// let is_approved = match IERC1155::isApprovedForAllCall::abi_decode_returns(&result, true) { -// Ok(decoded) => decoded._0, // Returns (bool,) -// Err(e) => return Err(WalletError::TransactionError( -// format!("Failed to decode isApprovedForAll result: {}", e) -// )), -// }; -// -// Ok(is_approved) -//} -// -///// Set approval for all tokens to an operator -//pub fn erc1155_set_approval_for_all( -// token_address: &str, -// operator_address: &str, -// approved: bool, -// provider: &Provider, -// signer: &S -//) -> Result { -// kiprintln!("PROCESS_LIB::erc1155_set_approval_for_all starting"); -// -// // Resolve addresses -// let token = resolve_name(token_address, provider.chain_id)?; -// let operator = resolve_name(operator_address, provider.chain_id)?; -// -// // Create the call using sol! macro generated struct -// let call = IERC1155::setApprovalForAllCall { operator, approved }; -// let call_data = call.abi_encode(); -// -// // Get the current nonce for the signer's address -// let signer_address = signer.address(); -// let nonce = provider.get_transaction_count(signer_address, None)? -// .to::(); -// -// // Estimate gas -// let tx_req = TransactionRequest { -// from: Some(signer_address), -// to: Some(TxKind::Call(token)), -// input: call_data.clone().into(), -// ..Default::default() -// }; -// -// // Try to estimate gas, fall back to 60,000 if estimation fails -// let gas_limit = match provider.estimate_gas(tx_req, None) { -// Ok(gas) => (gas.to::() * 120) / 100, // Add 20% buffer -// Err(_) => 60_000, // Default gas limit -// }; -// -// // Calculate gas price based on the chain -// let (gas_price, priority_fee) = match signer.chain_id() { -// 1 => calculate_eth_mainnet_gas(provider)?, -// 8453 => calculate_base_gas(provider)?, -// 10 => calculate_optimism_gas(provider)?, -// _ => { -// let base_fee = provider.get_gas_price()?.to::(); -// let adjusted_fee = (base_fee * 130) / 100; -// (adjusted_fee, adjusted_fee / 10) -// } -// }; -// -// // Prepare transaction data -// let tx_data = TransactionData { -// to: token, -// value: U256::ZERO, -// data: Some(call_data), -// nonce, -// gas_limit, -// gas_price, -// max_priority_fee: Some(priority_fee), -// chain_id: signer.chain_id(), -// }; -// -// // Sign and send transaction -// let signed_tx = signer.sign_transaction(&tx_data)?; -// let tx_hash = provider.send_raw_transaction(signed_tx.into())?; -// -// Ok(TxReceipt { -// hash: tx_hash, -// details: format!( -// "{} operator {} to manage all of your ERC1155 tokens in contract {}", -// if approved { "Approved" } else { "Revoked approval for" }, -// operator_address, -// token_address -// ), -// }) -//} -// -///// Transfer a single ERC1155 token -//pub fn erc1155_safe_transfer_from( -// token_address: &str, -// from_address: &str, -// to_address: &str, -// token_id: U256, -// amount: U256, -// data: Vec, -// provider: &Provider, -// signer: &S -//) -> Result { -// kiprintln!("PROCESS_LIB::erc1155_safe_transfer_from starting"); -// -// // Resolve addresses -// let token = resolve_name(token_address, provider.chain_id)?; -// let from = resolve_name(from_address, provider.chain_id)?; -// let to = resolve_name(to_address, provider.chain_id)?; -// -// // Create the call using sol! macro generated struct -// let call = IERC1155::safeTransferFromCall { -// from, -// to, -// id: token_id, -// amount, -// data: Bytes::from(data) -// }; -// let call_data = call.abi_encode(); -// -// // Get the current nonce for the signer's address -// let signer_address = signer.address(); -// let nonce = provider.get_transaction_count(signer_address, None)? -// .to::(); -// -// // Estimate gas -// let tx_req = TransactionRequest { -// from: Some(signer_address), -// to: Some(TxKind::Call(token)), -// input: call_data.clone().into(), -// ..Default::default() -// }; -// -// // Try to estimate gas, fall back to 150,000 if estimation fails -// let gas_limit = match provider.estimate_gas(tx_req, None) { -// Ok(gas) => (gas.to::() * 120) / 100, // Add 20% buffer -// Err(_) => 150_000, // Default gas limit -// }; -// -// // Calculate gas price based on the chain -// let (gas_price, priority_fee) = match signer.chain_id() { -// 1 => calculate_eth_mainnet_gas(provider)?, -// 8453 => calculate_base_gas(provider)?, -// 10 => calculate_optimism_gas(provider)?, -// _ => { -// let base_fee = provider.get_gas_price()?.to::(); -// let adjusted_fee = (base_fee * 130) / 100; -// (adjusted_fee, adjusted_fee / 10) -// } -// }; -// -// // Prepare transaction data -// let tx_data = TransactionData { -// to: token, -// value: U256::ZERO, -// data: Some(call_data), -// nonce, -// gas_limit, -// gas_price, -// max_priority_fee: Some(priority_fee), -// chain_id: signer.chain_id(), -// }; -// -// // Sign and send transaction -// let signed_tx = signer.sign_transaction(&tx_data)?; -// let tx_hash = provider.send_raw_transaction(signed_tx.into())?; -// -// Ok(TxReceipt { -// hash: tx_hash, -// details: format!( -// "Transferred {} of token ID {} from {} to {}", -// amount, token_id, from_address, to_address -// ), -// }) -//} -// -///// Batch transfer multiple ERC1155 tokens -//pub fn erc1155_safe_batch_transfer_from( -// token_address: &str, -// from_address: &str, -// to_address: &str, -// token_ids: Vec, -// amounts: Vec, -// data: Vec, -// provider: &Provider, -// signer: &S -//) -> Result { -// kiprintln!("PROCESS_LIB::erc1155_safe_batch_transfer_from starting"); -// -// // Check that arrays are same length -// if token_ids.len() != amounts.len() { -// return Err(WalletError::TransactionError( -// "Arrays of token IDs and amounts must have the same length".into() -// )); -// } -// -// // Resolve addresses -// let token = resolve_name(token_address, provider.chain_id)?; -// let from = resolve_name(from_address, provider.chain_id)?; -// let to = resolve_name(to_address, provider.chain_id)?; -// -// // Create the call using sol! macro generated struct -// let call = IERC1155::safeBatchTransferFromCall { -// from, -// to, -// ids: token_ids.clone(), -// amounts: amounts.clone(), -// data: Bytes::from(data) -// }; -// let call_data = call.abi_encode(); -// -// // Get the current nonce for the signer's address -// let signer_address = signer.address(); -// let nonce = provider.get_transaction_count(signer_address, None)? -// .to::(); -// -// // Estimate gas - this can be expensive, especially with many tokens -// let tx_req = TransactionRequest { -// from: Some(signer_address), -// to: Some(TxKind::Call(token)), -// input: call_data.clone().into(), -// ..Default::default() -// }; -// -// // Try to estimate gas, fall back to 200,000 + 50,000 per token if estimation fails -// let fallback_gas = 200_000 + (token_ids.len() as u64 * 50_000); -// let gas_limit = match provider.estimate_gas(tx_req, None) { -// Ok(gas) => (gas.to::() * 120) / 100, // Add 20% buffer -// Err(_) => fallback_gas, // Default gas limit that scales with number of tokens -// }; -// -// // Calculate gas price based on the chain -// let (gas_price, priority_fee) = match signer.chain_id() { -// 1 => calculate_eth_mainnet_gas(provider)?, -// 8453 => calculate_base_gas(provider)?, -// 10 => calculate_optimism_gas(provider)?, -// _ => { -// let base_fee = provider.get_gas_price()?.to::(); -// let adjusted_fee = (base_fee * 130) / 100; -// (adjusted_fee, adjusted_fee / 10) -// } -// }; -// -// // Prepare transaction data -// let tx_data = TransactionData { -// to: token, -// value: U256::ZERO, -// data: Some(call_data), -// nonce, -// gas_limit, -// gas_price, -// max_priority_fee: Some(priority_fee), -// chain_id: signer.chain_id(), -// }; -// -// // Sign and send transaction -// let signed_tx = signer.sign_transaction(&tx_data)?; -// let tx_hash = provider.send_raw_transaction(signed_tx.into())?; -// -// Ok(TxReceipt { -// hash: tx_hash, -// details: format!( -// "Batch transferred {} token types from {} to {}", -// token_ids.len(), from_address, to_address -// ), -// }) -//} - - - -////! Ethereum wallet functionality for Hyperware. -////! -////! This module provides higher-level wallet functionality, building on top of -////! the cryptographic operations in the signer module. It handles transaction -////! construction, name resolution, and account management. -////! -////! wallet module: -////! 1. Provides convenient transaction creation and submission -////! 2. Handles Hypermap name resolution -////! 3. Manages account state and balances -////! 4. Offers a simpler interface for common ETH operations (more to do here) -// -//use crate::eth::{ -// Provider, -// EthError, -// BlockNumberOrTag -//}; -//use crate::signer::{ -// Signer, -// LocalSigner, -// TransactionData, -// SignerError, -// EncryptedSignerData -//}; -//use crate::hypermap::{ -// namehash, -// valid_note, -// valid_fact, -// valid_name, -//}; -//use crate::{hypermap, kiprintln}; -// -//use thiserror::Error; -//use alloy_primitives::{ -// Address as EthAddress, -// TxHash, -// U256, -// Bytes -//}; -//use alloy::rpc::types::{ -// TransactionReceipt, -// TransactionRequest -//}; -//use alloy_primitives::TxKind; -//use std::str::FromStr; -//use alloy_sol_types::SolCall; -// -//#[derive(Debug, Error)] -//pub enum WalletError { -// #[error("signing error: {0}")] -// SignerError(#[from] SignerError), -// -// #[error("ethereum error: {0}")] -// EthError(#[from] EthError), -// -// #[error("name resolution error: {0}")] -// NameResolutionError(String), -// -// #[error("invalid amount: {0}")] -// InvalidAmount(String), -// -// #[error("transaction error: {0}")] -// TransactionError(String), -// -// #[error("gas estimation error: {0}")] -// GasEstimationError(String), -// -// #[error("insufficient funds: {0}")] -// InsufficientFunds(String), -// -// #[error("network congestion: {0}")] -// NetworkCongestion(String), -// -// #[error("transaction underpriced")] -// TransactionUnderpriced, -// -// #[error("transaction nonce too low")] -// TransactionNonceTooLow, -// -// #[error("permission denied: {0}")] -// PermissionDenied(String), -//} -// -///// Represents the storage state of a wallet's private key -//#[derive(Debug, Clone)] -//pub enum KeyStorage { -// /// An unencrypted wallet with a signer -// Decrypted(LocalSigner), -// -// /// An encrypted wallet - contains all the necessary data -// Encrypted(EncryptedSignerData), -//} -// -//impl KeyStorage { -// /// Get the encrypted data if this is an encrypted key storage -// pub fn get_encrypted_data(&self) -> Option> { -// match self { -// KeyStorage::Encrypted(data) => Some(data.encrypted_data.clone()), -// KeyStorage::Decrypted(_) => None, -// } -// } -// -// /// Get the address associated with this wallet -// pub fn get_address(&self) -> String { -// match self { -// KeyStorage::Decrypted(signer) => signer.address().to_string(), -// KeyStorage::Encrypted(data) => data.address.clone(), -// } -// } -// -// /// Get the chain ID associated with this wallet -// pub fn get_chain_id(&self) -> u64 { -// match self { -// KeyStorage::Decrypted(signer) => signer.chain_id(), -// KeyStorage::Encrypted(data) => data.chain_id, -// } -// } -//} -// -///// Represents an amount of ETH with proper formatting -//#[derive(Debug, Clone)] -//pub struct EthAmount { -// /// Value in wei -// wei_value: U256, -//} -// -//impl EthAmount { -// /// Create a new amount from ETH value -// pub fn from_eth(eth_value: f64) -> Self { -// // Convert ETH to wei (1 ETH = 10^18 wei) -// let wei = (eth_value * 1_000_000_000_000_000_000.0) as u128; -// Self { -// wei_value: U256::from(wei), -// } -// } -// -// /// Create from a string like "0.1 ETH" or "10 wei" -// pub fn from_string(amount_str: &str) -> Result { -// let parts: Vec<&str> = amount_str.trim().split_whitespace().collect(); -// -// if parts.is_empty() { -// return Err(WalletError::InvalidAmount( -// "Empty amount string".to_string() -// )); -// } -// -// let value_str = parts[0]; -// let unit = parts.get(1).map(|s| s.to_lowercase()).unwrap_or_else(|| "eth".to_string()); -// -// let value = value_str.parse::() -// .map_err(|_| WalletError::InvalidAmount(format!("Invalid numeric value: {}", value_str)))?; -// -// match unit.as_str() { -// "eth" => Ok(Self::from_eth(value)), -// "wei" => Ok(Self { -// wei_value: U256::from(value as u128), -// }), -// _ => Err(WalletError::InvalidAmount(format!("Unknown unit: {}", unit))), -// } -// } -// -// /// Get the value in wei -// pub fn as_wei(&self) -> U256 { -// self.wei_value -// } -// -// /// Get a human-readable string representation -// pub fn to_string(&self) -> String { -// // For values over 0.0001 ETH, show in ETH, otherwise in wei -// if self.wei_value >= U256::from(100_000_000_000_000u128) { -// // Convert to u128 first (safe since ETH total supply fits in u128) then to f64 -// let wei_u128 = self.wei_value.to::(); -// let eth_value = wei_u128 as f64 / 1_000_000_000_000_000_000.0; -// format!("{:.6} ETH", eth_value) -// } else { -// format!("{} wei", self.wei_value) -// } -// } -//} -// -///// Transaction receipt returned after sending -//#[derive(Debug, Clone)] -//pub struct TxReceipt { -// /// Transaction hash -// pub hash: TxHash, -// /// Transaction details -// pub details: String, -//} -// -//// The checks here aren't solid, but it works for now. Will also expand with full hypermap support -///// Resolve a .hypr name to an Ethereum address using Hypermap. -//pub fn resolve_name(name: &str, _chain_id: u64) -> Result { -// // If it's already an address, just parse it -// if name.starts_with("0x") && name.len() == 42 { -// return EthAddress::from_str(name) -// .map_err(|_| WalletError::NameResolutionError(format!("Invalid address format: {}", name))); -// } -// -// // hardcoded to .hypr for now -// let formatted_name = if !name.contains('.') { -// format!("{}.hypr", name) -// } else { -// kiprintln!("PROCESS_LIB::resolve_name name: {}", name); -// name.to_string() -// }; -// -// // Use hypermap resolution -// let hypermap = hypermap::Hypermap::default(60); -// match hypermap.get(&formatted_name) { -// Ok((_tba, owner, _)) => { -// Ok(owner) -// }, -// Err(e) => { -// Err(WalletError::NameResolutionError( -// format!("Failed to resolve name '{}': {}", name, e) -// )) -// } -// } -//} -// -///// Send ETH to an address or name -//pub fn send_eth( -// to: &str, -// amount: EthAmount, -// provider: Provider, -// signer: &S, -//) -> Result { -// -// kiprintln!("PROCESS_LIB::send_eth provider: {:#?}", provider); -// -// // Current chain-specific handling -// let chain_id = signer.chain_id(); -// kiprintln!("PROCESS_LIB::send_eth chain_id: {}", chain_id); -// -// // This part needs improvement - detect network type more robustly -// let is_test_network = chain_id == 31337 || chain_id == 1337; -// -// // Use network-specific gas strategies -// let (gas_price, priority_fee) = match chain_id { -// -// // just rough calculations for now -// 1 => calculate_eth_mainnet_gas(&provider)?, // mainnet -// 8453 => calculate_base_gas(&provider)?, // Base -// 10 => calculate_optimism_gas(&provider)?, // Optimism -// -// // Test networks - keep your current approach -// _ if is_test_network => (2_000_000_000, 100_000_000), -// -// // 30% -// _ => { -// kiprintln!("PROCESS_LIB::send_eth getting gas price"); -// let base_fee = provider.get_gas_price()?.to::(); -// kiprintln!("PROCESS_LIB::send_eth base_fee: {}", base_fee); -// let adjusted_fee = (base_fee * 130) / 100; -// kiprintln!("PROCESS_LIB::send_eth adjusted_fee: {}", adjusted_fee); -// (adjusted_fee, adjusted_fee / 10) -// } -// }; -// -// kiprintln!("PROCESS_LIB::send_eth gas_price: {}", gas_price); -// -// // Resolve the name to an address -// let to_address = resolve_name(to, chain_id)?; -// -// kiprintln!("PROCESS_LIB::send_eth to_address: {}", to_address); -// -// // Get the current nonce for the signer's address -// let from_address = signer.address(); -// let nonce = provider.get_transaction_count(from_address, None)? -// .to::(); -// -// kiprintln!("PROCESS_LIB::send_eth nonce: {}", nonce); -// -// // Standard gas limit for ETH transfer -// let gas_limit = 21000; -// -// // Prepare transaction data -// let tx_data = TransactionData { -// to: to_address, -// value: amount.as_wei(), -// data: None, // No data for simple ETH transfer -// nonce, -// gas_limit, -// gas_price, -// max_priority_fee: Some(priority_fee), -// chain_id, -// }; -// -// kiprintln!("PROCESS_LIB::send_eth tx_data: {:#?}", tx_data); -// -// // Sign the transaction -// let signed_tx = signer.sign_transaction(&tx_data)?; -// -// kiprintln!("PROCESS_LIB::send_eth signed_tx: {:?}", signed_tx); -// -// // Send the transaction -// let tx_hash = provider.send_raw_transaction(signed_tx.into())?; -// -// kiprintln!("lol PROCESS_LIB::send_eth tx_hash: {}", tx_hash); -// -// // Return the receipt with transaction details -// Ok(TxReceipt { -// hash: tx_hash, -// details: format!("Sent {} to {}", amount.to_string(), to), -// }) -//} -// -//// Helper function to calculate EIP-1559 gas parameters with network-specific values -//fn calculate_eip1559_gas( -// provider: &Provider, -// buffer_fraction: u128, -// priority_fee: u128 -//) -> Result<(u128, u128), WalletError> { -// kiprintln!("PROCESS_LIB::calculate_eip1559_gas provider\n", ); -// // Get latest block -// let latest_block = provider.get_block_by_number(BlockNumberOrTag::Latest, false)? -// .ok_or_else(|| WalletError::TransactionError("Failed to get latest block".into()))?; -// -// kiprintln!("PROCESS_LIB::calculate_eip1559_gas latest_block: {:#?}", latest_block); -// -// // Get base fee -// let base_fee = latest_block.header.inner.base_fee_per_gas -// .ok_or_else(|| WalletError::TransactionError("No base fee in block".into()))? -// as u128; -// -// kiprintln!("PROCESS_LIB::calculate_eip1559_gas base_fee: {}", base_fee); -// -// // Calculate max fee with the provided buffer fraction -// let max_fee = base_fee + (base_fee / buffer_fraction); -// -// kiprintln!("PROCESS_LIB::calculate_eip1559_gas max_fee: {}", max_fee); -// -// Ok((max_fee, priority_fee)) -//} -// -//// Network-specific gas calculation for Ethereum mainnet -//fn calculate_eth_mainnet_gas(provider: &Provider) -> Result<(u128, u128), WalletError> { -// // For mainnet: 50% buffer and 1.5 gwei priority fee -// calculate_eip1559_gas(provider, 2, 1_500_000_000u128) -//} -// -//fn calculate_base_gas(provider: &Provider) -> Result<(u128, u128), WalletError> { -// // Get the latest block to determine current gas conditions -// let latest_block = provider.get_block_by_number(BlockNumberOrTag::Latest, false)? -// .ok_or_else(|| WalletError::TransactionError("Failed to get latest block".into()))?; -// -// // Get base fee from the block -// let base_fee = latest_block.header.inner.base_fee_per_gas -// .ok_or_else(|| WalletError::TransactionError("No base fee in block".into()))? -// as u128; -// -// // Calculate max fee with a 33% buffer -// let max_fee = base_fee + (base_fee / 3); -// -// // Dynamic priority fee - 10% of base fee, but with a minimum and a maximum -// // Low minimum for Base which has very low gas prices -// let min_priority_fee = 100_000u128; // 0.0001 gwei minimum -// let max_priority_fee = max_fee / 2; // Never more than half the max fee -// -// let priority_fee = std::cmp::max( -// min_priority_fee, -// std::cmp::min(base_fee / 10, max_priority_fee) -// ); -// -// Ok((max_fee, priority_fee)) -//} -// -//// Gas calculation for Optimism network -//fn calculate_optimism_gas(provider: &Provider) -> Result<(u128, u128), WalletError> { -// // For Optimism: 25% buffer and 0.3 gwei priority fee -// calculate_eip1559_gas(provider, 4, 300_000_000u128) -//} -// -///// Get the ETH balance for an address or name -//pub fn get_balance( -// address_or_name: &str, -// chain_id: u64, -// provider: Provider, -//) -> Result { -// // Resolve name to address -// let address = resolve_name(address_or_name, chain_id)?; -// -// // Query balance -// let balance = provider.get_balance(address, None)?; -// -// // Return formatted amount -// Ok(EthAmount { -// wei_value: balance, -// }) -//} -// -//pub fn wait_for_transaction( -// tx_hash: TxHash, -// provider: Provider, -// confirmations: u64, -// timeout_secs: u64 -//) -> Result { -// let start_time = std::time::Instant::now(); -// let timeout = std::time::Duration::from_secs(timeout_secs); -// -// loop { -// // Check if we've exceeded the timeout -// if start_time.elapsed() > timeout { -// return Err(WalletError::TransactionError( -// format!("Transaction confirmation timeout after {} seconds", timeout_secs) -// )); -// } -// -// // Try to get the receipt -// if let Ok(Some(receipt)) = provider.get_transaction_receipt(tx_hash) { -// // Check if we have enough confirmations -// let latest_block = provider.get_block_number()?; -// let receipt_block = receipt.block_number.unwrap_or(0) as u64; -// -// if latest_block >= receipt_block + confirmations { -// return Ok(receipt); -// } -// } -// -// // Wait a bit before checking again -// std::thread::sleep(std::time::Duration::from_secs(2)); -// } -//} -// -////// Extract error information from RPC errors -////fn extract_rpc_error(error: &EthError) -> WalletError { -//// match error { -//// EthError::RpcError(value) => { -//// // Try to parse the error message -//// if let Some(message) = value.get("message").and_then(|m| m.as_str()) { -//// if message.contains("insufficient funds") { -//// return WalletError::InsufficientFunds(message.to_string()); -//// } else if message.contains("underpriced") { -//// return WalletError::TransactionUnderpriced; -//// } else if message.contains("nonce too low") { -//// return WalletError::TransactionNonceTooLow; -//// } -//// // Add more error patterns as needed -//// } -//// WalletError::TransactionError(format!("RPC error: {:?}", value)) -//// }, -//// _ => WalletError::TransactionError(format!("Ethereum error: {:?}", error)) -//// } -////} -// -//// ERC-20 Read Function Selectors (_ = NOT CHECKED) -//const ERC20_BALANCE_OF: [u8; 4] = [0x70, 0xa0, 0x82, 0x31]; // balanceOf(address) -//const ERC20_DECIMALS: [u8; 4] = [0x31, 0x3c, 0xe5, 0x67]; // decimals() -//const _ERC20_ALLOWANCE: [u8; 4] = [0xdd, 0x62, 0xed, 0x3e]; // allowance(address,address) -//const _ERC20_TOTAL_SUPPLY: [u8; 4] = [0x18, 0x16, 0x0d, 0xdd]; // totalSupply() -//const _ERC20_SYMBOL: [u8; 4] = [0x95, 0xd8, 0x9b, 0x41]; // symbol() -//const _ERC20_NAME: [u8; 4] = [0x06, 0xfd, 0xde, 0x03]; // name() -// -//// ERC-20 Write Function Selectors -//const _ERC20_TRANSFER: [u8; 4] = [0xa9, 0x05, 0x9c, 0xbb]; // transfer(address,uint256) -//const _ERC20_TRANSFER_FROM: [u8; 4] = [0x23, 0xb8, 0x72, 0xdd]; // transferFrom(address,address,uint256) -//const _ERC20_APPROVE: [u8; 4] = [0x09, 0x5e, 0xa7, 0xb3]; // approve(address,uint256) -// -///// Get the balance of ERC20 tokens for an address -//pub fn erc20_balance_of( -// token_address: &str, -// owner_address: &str, -// provider: Provider -//) -> Result { -// kiprintln!("PROCESS_LIB::erc20_balance_of token_address: {}", token_address); -// // Resolve addresses -// let token: EthAddress = resolve_name(&token_address, provider.chain_id)?; -// kiprintln!("PROCESS_LIB::erc20_balance_of token: {}", token); -// let owner = resolve_name(&owner_address, provider.chain_id)?; -// kiprintln!("PROCESS_LIB::erc20_balance_of owner: {}", owner); -// -// // The ERC20 balanceOf function selector: keccak256("balanceOf(address)")[0:4] -// //let selector = [0xa9, 0x05, 0x9c, 0xbb]; -// let selector = ERC20_BALANCE_OF; -// kiprintln!("PROCESS_LIB::erc20_balance_of selector: {:?}", selector); -// -// // Encode the owner address parameter (padded to 32 bytes) -// let mut call_data = Vec::with_capacity(4 + 32); -// call_data.extend_from_slice(&selector); -// call_data.extend_from_slice(&[0u8; 12]); // 12 bytes of padding -// call_data.extend_from_slice(owner.as_slice()); -// kiprintln!("PROCESS_LIB::erc20_balance_of call_data: {:?}", call_data); -// -// // Create the transaction request for eth_call -// let tx = TransactionRequest { -// to: Some(TxKind::Call(token)), -// input: call_data.into(), -// ..Default::default() -// }; -// -// kiprintln!("PROCESS_LIB::erc20_balance_of tx: {:#?}", tx); -// -// // Call the contract -// let result = provider.call(tx, None)?; -// -// kiprintln!("PROCESS_LIB::erc20_balance_of result: {:?}", result); -// -// // Parse the result (a uint256 value) -// if result.len() < 32 { -// kiprintln!("PROCESS_LIB::erc20_balance_of Invalid result length"); -// return Err(WalletError::TransactionError("Invalid result length".into())); -// } -// -// // Convert the bytes to a U256 -// let balance = U256::from_be_bytes::<32>(result[0..32].try_into().unwrap()); -// -// // TODO: This should be based on the contract's decimals, fix later with a LUT -// //Ok(EthAmount { wei_value: balance }) -// -// let decimals = erc20_decimals(token, &provider)?; -// let balance_u128 = balance.to::(); -// let balance_float = balance_u128 as f64 / 10f64.powi(decimals as i32); -// Ok(balance_float) // Returns balance in full tokens (e.g., 390.159112 USDC) -// -//} -// -//pub fn erc20_decimals(token_address: EthAddress, provider: &Provider) -> Result { -// kiprintln!("PROCESS_LIB::erc20_decimals token_address: {}", token_address); -// let token = token_address; -// kiprintln!("PROCESS_LIB::erc20_decimals token: {}", token); -// let selector = ERC20_DECIMALS; -// let call_data = selector.to_vec(); // ✅ Ensure it's exactly 4 bytes -// -// kiprintln!("PROCESS_LIB::erc20_decimals selector: {:?}", selector); -// -// let tx = TransactionRequest { -// to: Some(TxKind::Call(token)), -// input: call_data.into(), -// ..Default::default() -// }; -// -// let result = provider.call(tx, None)?; -// kiprintln!("PROCESS_LIB::erc20_decimals result: {:?}", result); -// -// if result.len() < 32 { -// return Err(WalletError::TransactionError("Invalid decimals response".into())); -// } -// kiprintln!("PROCESS_LIB::erc20_decimals done", ); -// -// Ok(result[31]) // Decimals are stored in the last byte of the 32-byte response -//} -// -// -/////// Transfer ERC20 tokens to another address -////pub fn erc20_transfer( -//// token_address: &str, -//// to_address: &str, -//// amount: EthAmount, -//// provider: Provider, -//// signer: &S -////) -> Result { -//// // Resolve addresses -//// let token = resolve_name(token_address, provider.chain_id())?; -//// let to = resolve_name(to_address, provider.chain_id())?; -//// -//// // The ERC20 transfer function selector: keccak256("transfer(address,uint256)")[0:4] -//// let selector = [0xa9, 0x05, 0x9c, 0xbb]; -//// -//// // Encode the parameters: address recipient, uint256 amount -//// let mut call_data = Vec::with_capacity(4 + 32 + 32); -//// call_data.extend_from_slice(&selector); -//// -//// // Recipient address (padded to 32 bytes) -//// call_data.extend_from_slice(&[0u8; 12]); // 12 bytes of padding -//// call_data.extend_from_slice(to.as_slice()); -//// -//// // Amount (padded to 32 bytes) -//// let amount_bytes = amount.as_wei().to_be_bytes::<32>(); -//// call_data.extend_from_slice(&amount_bytes); -//// -//// // Get the current nonce -//// let from_address = signer.address(); -//// let nonce = provider.get_transaction_count(from_address, None)?.to::(); -//// -//// // Estimate gas for the token transfer (usually around 60k for ERC20 transfers) -//// let tx_req = TransactionRequest { -//// from: Some(from_address), -//// to: Some(token), -//// data: Some(call_data.clone().into()), -//// ..Default::default() -//// }; -//// -//// let gas_limit = provider.estimate_gas(tx_req, None)? -//// .to::() -//// .saturating_mul(12).saturating_div(10); // Add 20% buffer -//// -//// // Calculate gas price based on the chain -//// let (gas_price, priority_fee) = match signer.chain_id() { -//// // Use your existing gas calculation functions -//// 1 => calculate_eth_mainnet_gas(&provider)?, -//// 8453 => calculate_base_gas(&provider)?, -//// _ => { -//// let base_fee = provider.get_gas_price()?.to::(); -//// let adjusted_fee = (base_fee * 130) / 100; -//// (adjusted_fee, adjusted_fee / 10) -//// } -//// }; -//// -//// // Create transaction data -//// let tx_data = TransactionData { -//// to: token, -//// value: U256::ZERO, // No ETH sent with token transfers -//// data: Some(call_data), -//// nonce, -//// gas_limit, -//// gas_price, -//// max_priority_fee: Some(priority_fee), -//// chain_id: signer.chain_id(), -//// }; -//// -//// // Sign and send transaction -//// let signed_tx = signer.sign_transaction(&tx_data)?; -//// let tx_hash = provider.send_raw_transaction(signed_tx.into())?; -//// -//// Ok(TxReceipt { -//// hash: tx_hash, -//// details: format!("Sent {} tokens to {}", amount.to_string(), to_address), -//// }) -////} -// -//// THE HYPERMAP stuff -// -///// Result type for Hypermap transactions -//#[derive(Debug, Clone)] -//pub struct HypermapTxReceipt { -// /// Transaction hash -// pub hash: TxHash, -// /// Description of the operation -// pub description: String, -//} -// -///// Create a note (mutable data) on a Hypermap namespace entry -///// -///// # Parameters -///// - `parent_entry`: The namespace entry (e.g. "mynode.hypr") where the note will be created -///// - `note_key`: The note key to create (must start with '~') -///// - `data`: The data to store in the note -///// - `provider`: The Ethereum provider to use -///// - `signer`: The signer to use for signing the transaction -///// -///// # Returns -///// A result containing a HypermapTxReceipt or a WalletError -//pub fn create_note( -// parent_entry: &str, -// note_key: &str, -// data: Vec, -// provider: Provider, -// signer: &S, -//) -> Result { -// // Verify the note key is valid -// if !valid_note(note_key) { -// return Err(WalletError::NameResolutionError( -// format!("Invalid note key '{}'. Must start with '~' and contain only lowercase letters, numbers, and hyphens", note_key) -// )); -// } -// -// // Get the parent TBA address -// let hypermap = provider.hypermap(); -// let parent_hash_str = namehash(parent_entry); -// -// println!("Parent entry: {}", parent_entry); -// println!("Parent hash: {}", parent_hash_str); -// -// let (tba, owner, _) = hypermap.get_hash(&parent_hash_str)?; -// -// println!("TBA address (parent): {}", tba); -// println!("Owner address: {}", owner); -// -// // Check that the signer is the owner of the parent entry -// let signer_address = signer.address(); -// println!("Signer address: {}", signer_address); -// -// if signer_address != owner { -// return Err(WalletError::PermissionDenied( -// format!("Signer address {} does not own the parent entry {}", signer_address, parent_entry) -// )); -// } -// -// // Get the hypermap contract address -// let hypermap_address = *hypermap.address(); -// println!("Hypermap contract address: {}", hypermap_address); -// -// // Create the note function call data -// let note_function = hypermap::contract::noteCall { -// note: Bytes::from(note_key.as_bytes().to_vec()), -// data: Bytes::from(data), -// }; -// let note_call_data = note_function.abi_encode(); -// -// // ?? Bytes::from(note_call_data) or note_call_data? -// // Now create an ERC-6551 execute call to send from the wallet to the TBA -// let execute_call_data = create_execute_calldata( -// hypermap_address, -// U256::ZERO, -// Bytes::from(note_call_data), -// 0 // CALL operation -// ); -// -// // Send the transaction from the wallet to the TBA -// let (tx_hash, tx_data) = send_transaction( -// tba, -// execute_call_data, -// U256::ZERO, -// provider, -// signer -// )?; -// -// // Return the receipt with transaction details -// Ok(HypermapTxReceipt { -// hash: tx_hash, -// description: format!("Created note '{}' on '{}'", note_key, parent_entry), -// }) -//} -// -///// Helper function to create calldata for the TBA's execute function -///// -///// The ERC-6551 execute function has this signature: -///// function execute(address to, uint256 value, bytes calldata data, uint8 operation) -///// -///// Parameters: -///// - to: The target contract to call (the Hypermap contract) -///// - value: Amount of ETH to send (usually 0) -///// - data: The calldata for the target function -///// - operation: The type of operation (0 = CALL, 1 = DELEGATECALL, etc.) -//fn create_execute_calldata( -// to: EthAddress, -// value: U256, -// data: Bytes, -// operation: u8 -//) -> Bytes { -// // Function selector for execute(address,uint256,bytes,uint8) -// // keccak256("execute(address,uint256,bytes,uint8)")[0:4] -// let selector = [0x44, 0xc0, 0x28, 0xfe]; -// -// // Encode to address (padded to 32 bytes) -// let mut to_bytes = vec![0u8; 32]; -// to_bytes[12..32].copy_from_slice(to.as_slice()); -// -// // Encode value (uint256) -// let value_bytes = value.to_be_bytes::<32>(); -// -// // Calculate offset for the dynamic bytes data -// // This is the offset in 32-byte words to where the bytes data starts -// // 3 fixed params (address, uint256, uint256 offset) + 1 more fixed param after = 4 * 32 = 128 bytes -// let offset = U256::from(128); -// let offset_bytes = offset.to_be_bytes::<32>(); -// -// // Encode operation (padded to 32 bytes) -// let mut operation_bytes = vec![0u8; 32]; -// operation_bytes[31] = operation; -// -// // Encode bytes length -// let data_len = U256::from(data.len()); -// let data_len_bytes = data_len.to_be_bytes::<32>(); -// -// // Encode bytes data (with padding to 32-byte boundary) -// let mut padded_data = data.to_vec(); -// if padded_data.len() % 32 != 0 { -// let padding = vec![0u8; 32 - (padded_data.len() % 32)]; -// padded_data.extend_from_slice(&padding); -// } -// -// // Combine everything into final calldata -// let mut result = Vec::new(); -// result.extend_from_slice(&selector); // Function selector (4 bytes) -// result.extend_from_slice(&to_bytes); // To address (32 bytes) -// result.extend_from_slice(&value_bytes); // Value (32 bytes) -// result.extend_from_slice(&offset_bytes); // Data offset (32 bytes) -// result.extend_from_slice(&operation_bytes); // Operation (32 bytes) -// result.extend_from_slice(&data_len_bytes); // Data length (32 bytes) -// result.extend_from_slice(&padded_data); // Data (padded) -// -// Bytes::from(result) -//} -// -///// Send a transaction to the token-bound account -//fn send_transaction( -// to: EthAddress, -// data: Bytes, -// value: U256, -// provider: Provider, -// signer: &S, -//) -> Result<(TxHash, Vec), WalletError> { -// let chain_id = signer.chain_id(); -// -// kiprintln!("PROCESS_LIB::send_transaction starting"); -// kiprintln!("PROCESS_LIB::send_transaction chain_id: {}", chain_id); -// -// // Get gas estimates - use 50% buffer for Base to ensure acceptance -// let base_fee = provider.get_gas_price()?.to::(); -// let gas_price = (base_fee * 150) / 100; // 50% buffer -// let priority_fee = gas_price / 5; // 20% of gas price -// -// kiprintln!("PROCESS_LIB::send_transaction base_fee: {}, priority_fee: {}", base_fee, priority_fee); -// kiprintln!("PROCESS_LIB::send_transaction gas_price: {}", gas_price); -// -// // Get the current nonce for the signer's address -// let from_address = signer.address(); -// let nonce = provider.get_transaction_count(from_address, None)? -// .to::(); -// -// kiprintln!("PROCESS_LIB::send_transaction nonce: {}", nonce); -// -// // For ERC-6551 account operations, use a higher gas limit -// // The ERC-6551 execute function is complex and gas-intensive -// let estimated_gas = 500_000; // Start high for ERC-6551 -// -// // Add 50% buffer to estimated gas since this is a complex operation -// let gas_limit = (estimated_gas * 150) / 100; -// -// kiprintln!("PROCESS_LIB::send_transaction estimated_gas: {}", estimated_gas); -// kiprintln!("PROCESS_LIB::send_transaction gas_limit: {}", gas_limit); -// -// // Prepare transaction data -// let tx_data = TransactionData { -// to, -// value, -// data: Some(data.to_vec()), -// nonce, -// gas_limit, -// gas_price, -// max_priority_fee: Some(priority_fee), -// chain_id, -// }; -// -// // Sign the transaction -// let signed_tx = signer.sign_transaction(&tx_data)?; -// kiprintln!("PROCESS_LIB::send_transaction signed"); -// -// // Send the transaction -// let tx_hash = provider.send_raw_transaction(signed_tx.clone().into())?; -// kiprintln!("PROCESS_LIB::send_transaction tx_hash: {}", tx_hash); -// -// // Return both the hash and the raw transaction data -// Ok((tx_hash, signed_tx)) -//} -// -/////// A simple test function to create a note -////pub fn test_create_note( -//// parent_entry: &str, // e.g., "lazybonesitis.os" -//// provider: Provider, -//// signer: &S, -////) -> Result<(), WalletError> { -//// println!("=== TESTING NOTE CREATION ==="); -//// println!("Parent entry: {}", parent_entry); -//// -//// // Simple test note -//// let note_key = "~test-note"; -//// let data = "This is a test note created at ".to_string() + &chrono::Utc::now().to_rfc3339(); -//// -//// println!("Creating note: {}", note_key); -//// println!("Data: {}", data); -//// -//// match create_note( -//// parent_entry, -//// note_key, -//// data.as_bytes().to_vec(), -//// provider, -//// signer -//// ) { -//// Ok(receipt) => { -//// println!("Success! Transaction hash: {}", receipt.hash); -//// println!("Description: {}", receipt.description); -//// Ok(()) -//// }, -//// Err(e) => { -//// println!("Error creating note: {:?}", e); -//// Err(e) -//// } -//// } -////} -// -/////// Create a note (mutable data) on a Hypermap namespace entry -/////// -/////// # Parameters -/////// - `parent_entry`: The namespace entry (e.g. "mynode.hypr") where the note will be created -/////// - `note_key`: The note key to create (must start with '~') -/////// - `data`: The data to store in the note -/////// - `provider`: The Ethereum provider to use -/////// - `signer`: The signer to use for signing the transaction -/////// -/////// # Returns -/////// A result containing a HypermapTxReceipt or a WalletError -////pub fn create_note( -//// parent_entry: &str, -//// note_key: &str, -//// data: Vec, -//// provider: Provider, -//// signer: &S, -////) -> Result { -//// // Verify the note key is valid -//// if !valid_note(note_key) { -//// return Err(WalletError::NameResolutionError( -//// format!("Invalid note key '{}'. Must start with '~' and contain only lowercase letters, numbers, and hyphens", note_key) -//// )); -//// } -//// -//// // Get the parent TBA address -//// let hypermap = provider.hypermap(); -//// let parent_hash_str = namehash(parent_entry); -//// let (tba, owner, _) = hypermap.get_hash(&parent_hash_str)?; -//// -//// // Check that the signer is the owner of the parent entry -//// let signer_address = signer.address(); -//// if signer_address != owner { -//// return Err(WalletError::PermissionDenied( -//// format!("Signer address {} does not own the parent entry {}", signer_address, parent_entry) -//// )); -//// } -//// -//// // Create the note call data -//// let note_function = hypermap::contract::noteCall { -//// note: Bytes::from(note_key.as_bytes().to_vec()), -//// data: Bytes::from(data), -//// }; -//// let call_data = note_function.abi_encode(); -//// -//// // Prepare and send the transaction -//// send_tba_transaction( -//// tba, -//// call_data.into(), -//// U256::ZERO, // No ETH value to send -//// provider, -//// signer, -//// format!("Created note '{}' on '{}'", note_key, parent_entry), -//// ) -////} -// -///// Create a fact (immutable data) on a Hypermap namespace entry -///// -///// # Parameters -///// - `parent_entry`: The namespace entry (e.g. "mynode.hypr") where the fact will be created -///// - `fact_key`: The fact key to create (must start with '!') -///// - `data`: The data to store in the fact -///// - `provider`: The Ethereum provider to use -///// - `signer`: The signer to use for signing the transaction -///// -///// # Returns -///// A result containing a HypermapTxReceipt or a WalletError -//pub fn create_fact( -// parent_entry: &str, -// fact_key: &str, -// data: Vec, -// provider: Provider, -// signer: &S, -//) -> Result { -// // Verify the fact key is valid -// if !valid_fact(fact_key) { -// return Err(WalletError::NameResolutionError( -// format!("Invalid fact key '{}'. Must start with '!' and contain only lowercase letters, numbers, and hyphens", fact_key) -// )); -// } -// -// // Get the parent TBA address -// let hypermap = provider.hypermap(); -// let parent_hash_str = namehash(parent_entry); -// let (tba, owner, _) = hypermap.get_hash(&parent_hash_str)?; -// -// // Check that the signer is the owner of the parent entry -// let signer_address = signer.address(); -// if signer_address != owner { -// return Err(WalletError::PermissionDenied( -// format!("Signer address {} does not own the parent entry {}", signer_address, parent_entry) -// )); -// } -// -// // Create the fact call data -// let fact_function = hypermap::contract::factCall { -// fact: Bytes::from(fact_key.as_bytes().to_vec()), -// data: Bytes::from(data), -// }; -// let call_data = fact_function.abi_encode(); -// -// // Prepare and send the transaction -// send_tba_transaction( -// tba, -// call_data.into(), -// U256::ZERO, // No ETH value to send -// provider, -// signer, -// format!("Created fact '{}' on '{}'", fact_key, parent_entry), -// ) -//} -// -///// Mint a new namespace entry under a parent entry -///// -///// # Parameters -///// - `parent_entry`: The parent namespace entry (e.g. "mynode.hypr") -///// - `label`: The new label to mint (without prefix or parent) -///// - `recipient`: The address that will own the new entry -///// - `implementation`: The address of the token-bound account implementation -///// - `provider`: The Ethereum provider to use -///// - `signer`: The signer to use for signing the transaction -///// -///// # Returns -///// A result containing a HypermapTxReceipt or a WalletError -//pub fn mint_entry( -// parent_entry: &str, -// label: &str, -// recipient: &str, -// implementation: &str, -// provider: Provider, -// signer: &S, -//) -> Result { -// // Verify the label is valid -// if !valid_name(label) { -// return Err(WalletError::NameResolutionError( -// format!("Invalid label '{}'. Must contain only lowercase letters, numbers, and hyphens", label) -// )); -// } -// -// // Get the parent TBA address -// let hypermap = provider.hypermap(); -// let parent_hash_str = namehash(parent_entry); -// kiprintln!("PROCESS_LIB::mint_entry parent_hash_str: {}", parent_hash_str); -// let (tba, owner, _) = hypermap.get_hash(&parent_hash_str)?; -// -// // Check that the signer is the owner of the parent entry -// let signer_address = signer.address(); -// if signer_address != owner { -// return Err(WalletError::PermissionDenied( -// format!("Signer address {} does not own the parent entry {}", signer_address, parent_entry) -// )); -// } -// -// // Resolve recipient address -// let recipient_address = resolve_name(recipient, provider.chain_id)?; -// -// // Resolve implementation address -// let implementation_address = resolve_name(implementation, provider.chain_id)?; -// -// // Create the mint call data -// let mint_function = hypermap::contract::mintCall { -// who: recipient_address, -// label: Bytes::from(label.as_bytes().to_vec()), -// initialization: Bytes::default(), // No initialization data -// erc721Data: Bytes::default(), // No ERC721 data -// implementation: implementation_address, -// }; -// let call_data = mint_function.abi_encode(); -// -// kiprintln!("Parent entry: {}", parent_entry); -// kiprintln!("Parent hash: {}", parent_hash_str); -// kiprintln!("TBA address: {}", tba); -// kiprintln!("Owner address: {}", owner); -// kiprintln!("Signer address: {}", signer_address); -// -// // Prepare and send the transaction -// send_tba_transaction( -// tba, -// call_data.into(), -// U256::ZERO, // No ETH value to send -// provider, -// signer, -// format!("Minted new entry '{}' under '{}'", label, parent_entry), -// ) -//} -// -///// Set the gene for a namespace entry -///// -///// # Parameters -///// - `entry`: The namespace entry (e.g. "mynode.hypr") to set the gene for -///// - `gene_implementation`: The address of the token-bound account implementation to use as the gene -///// - `provider`: The Ethereum provider to use -///// - `signer`: The signer to use for signing the transaction -///// -///// # Returns -///// A result containing a HypermapTxReceipt or a WalletError -//pub fn set_gene( -// entry: &str, -// gene_implementation: &str, -// provider: Provider, -// signer: &S, -//) -> Result { -// // Get the entry's TBA address -// let hypermap = provider.hypermap(); -// let entry_hash_str = namehash(entry); -// let (tba, owner, _) = hypermap.get_hash(&entry_hash_str)?; -// -// // Check that the signer is the owner of the entry -// let signer_address = signer.address(); -// if signer_address != owner { -// return Err(WalletError::PermissionDenied( -// format!("Signer address {} does not own the entry {}", signer_address, entry) -// )); -// } -// -// // Resolve gene implementation address -// let gene_address = resolve_name(gene_implementation, provider.chain_id)?; -// -// // Create the gene call data -// let gene_function = hypermap::contract::geneCall { -// _gene: gene_address, -// }; -// let call_data = gene_function.abi_encode(); -// -// // Prepare and send the transaction -// send_tba_transaction( -// tba, -// call_data.into(), -// U256::ZERO, // No ETH value to send -// provider, -// signer, -// format!("Set gene for '{}' to '{}'", entry, gene_implementation), -// ) -//} -// -///// Helper function to send a transaction to a token-bound account -//fn send_tba_transaction( -// tba: EthAddress, -// data: Bytes, -// value: U256, -// provider: Provider, -// signer: &S, -// description: String, -//) -> Result { -// kiprintln!("PROCESS_LIB::send_tba_transaction starting"); -// -// let chain_id = signer.chain_id(); -// kiprintln!("PROCESS_LIB::send_tba_transaction chain_id: {}", chain_id); -// -// // TODO: change from hardcoded base to dynamic -// let (base_fee, priority_fee) = calculate_base_gas(&provider)?; -// kiprintln!("PROCESS_LIB::send_tba_transaction base_fee: {}, priority_fee: {}", base_fee, priority_fee); -// -// let gas_price = (base_fee * 180) / 100; -// kiprintln!("PROCESS_LIB::send_tba_transaction gas_price: {}", gas_price); -// -// // Get the current nonce for the signer's address -// let from_address = signer.address(); -// let nonce = provider.get_transaction_count(from_address, None)? -// .to::(); -// kiprintln!("PROCESS_LIB::send_tba_transaction nonce: {}", nonce); -// -// // Estimate gas limit for the transaction -// let tx_req = TransactionRequest::default() -// .from(from_address) -// .to(tba) -// .input(data.clone().into()) -// .value(value); -// -// let estimated_gas = match provider.estimate_gas(tx_req, None) { -// Ok(gas) => gas.to::(), -// Err(_) => { -// // If estimation fails, use a conservative gas limit -// // This might happen for new contracts or complex interactions -// 300_000 -// } -// }; -// kiprintln!("PROCESS_LIB::send_tba_transaction estimated_gas: {}", estimated_gas); -// -// // Add 120% buffer to estimated gas -// let gas_limit = (estimated_gas * 220) / 100; -// kiprintln!("PROCESS_LIB::send_tba_transaction gas_limit: {}", gas_limit); -// -// // Prepare transaction data -// let tx_data = TransactionData { -// to: tba, -// value, -// data: Some(data.to_vec()), -// nonce, -// gas_limit, -// gas_price, -// max_priority_fee: Some(priority_fee), -// chain_id, -// }; -// //kiprintln!("PROCESS_LIB::send_tba_transaction tx_data: {:#?}", tx_data); -// -// // Sign the transaction -// let signed_tx = signer.sign_transaction(&tx_data)?; -// kiprintln!("PROCESS_LIB::send_tba_transaction signed"); -// -// // Send the transaction -// let tx_hash = provider.send_raw_transaction(signed_tx.into())?; -// kiprintln!("PROCESS_LIB::send_tba_transaction tx_hash: {}", tx_hash); -// -// // Return the receipt with transaction details -// Ok(HypermapTxReceipt { -// hash: tx_hash, -// description, -// }) -//} -// -// -// -// -// - - +} \ No newline at end of file From 517056efb90cc67fe552fceccda966903a497135 Mon Sep 17 00:00:00 2001 From: Hallmane Date: Wed, 2 Apr 2025 14:44:32 +0200 Subject: [PATCH 07/96] bla --- src/wallet.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/wallet.rs b/src/wallet.rs index 19e3fda..7d80419 100644 --- a/src/wallet.rs +++ b/src/wallet.rs @@ -17,9 +17,7 @@ use crate::hypermap::{ valid_name, }; use crate::hypermap; -use crate::{ - kiprintln -}; +use crate::kiprintln; use thiserror::Error; use alloy_primitives::{ @@ -1366,4 +1364,4 @@ pub fn set_gene( provider, signer ) -} \ No newline at end of file +} From 0aad3c81c5b6ed2ad6bbaff0b2ab85fa5d37d386 Mon Sep 17 00:00:00 2001 From: Hallmane Date: Thu, 3 Apr 2025 11:21:03 +0200 Subject: [PATCH 08/96] removed cursor shit --- Cargo.lock | 4 ---- 1 file changed, 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 89a4c64..2ea1c38 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1583,11 +1583,7 @@ dependencies = [ [[package]] name = "hyperware_process_lib" -<<<<<<< HEAD -version = "1.0.4" -======= version = "1.0.6" ->>>>>>> main dependencies = [ "alloy", "alloy-primitives", From 98e5aad9adf3bd7b30038d2164017a946448ea8e Mon Sep 17 00:00:00 2001 From: Hallmane Date: Tue, 8 Apr 2025 13:28:54 +0200 Subject: [PATCH 09/96] untested stuff that i've used in gotland wallet app --- src/signer.rs | 95 +++++++---- src/wallet.rs | 425 ++++++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 466 insertions(+), 54 deletions(-) diff --git a/src/signer.rs b/src/signer.rs index c146383..ad6197f 100644 --- a/src/signer.rs +++ b/src/signer.rs @@ -126,16 +126,79 @@ pub struct LocalSigner { pub private_key_hex: String, } +// Manual implementation of Serialize for LocalSigner +impl Serialize for LocalSigner { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + + // Serialize only the fields we need + let mut state = serializer.serialize_struct("LocalSigner", 3)?; + state.serialize_field("address", &self.address)?; + state.serialize_field("chain_id", &self.chain_id)?; + state.serialize_field("private_key_hex", &self.private_key_hex)?; + state.end() + } +} + +// Manual implementation of Deserialize for LocalSigner +impl<'de> Deserialize<'de> for LocalSigner { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + #[derive(Deserialize)] + struct LocalSignerData { + address: EthAddress, + chain_id: u64, + private_key_hex: String, + } + + let data = LocalSignerData::deserialize(deserializer)?; + + // Reconstruct the LocalSigner from the private key + match LocalSigner::from_private_key(&data.private_key_hex, data.chain_id) { + Ok(signer) => Ok(signer), + Err(e) => Err(serde::de::Error::custom(format!("Failed to reconstruct signer: {}", e))), + } + } +} + impl LocalSigner { /// Create a new signer with a randomly generated private key pub fn new_random(chain_id: u64) -> Result { - // Generate a secure random private key - let inner = PrivateKeySigner::random(); + // Generate a secure random private key directly + let mut rng = thread_rng(); + let mut private_key_bytes = [0u8; 32]; + rng.fill_bytes(&mut private_key_bytes); - let address = inner.address(); + // Make sure the private key is valid (less than curve order) + // TODO: This is a simplification + let max_scalar = hex::decode("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364140") + .map_err(|_| SignerError::RandomGenerationError("Failed to decode max scalar".to_string()))?; + + // Simple check: if our random bytes are >= max_scalar, regenerate + // This is a simplified approach - production code would use more sophisticated comparison + if private_key_bytes.as_slice().cmp(max_scalar.as_slice()) != std::cmp::Ordering::Less { + // Try again with a new random value + rng.fill_bytes(&mut private_key_bytes); + } - // Extract the private key for storage - let private_key_hex = Self::extract_private_key(&inner)?; + // Convert to B256 for the PrivateKeySigner + let key = B256::from_slice(&private_key_bytes); + + // Store the private key hex string for later use + let private_key_hex = format!("0x{}", hex::encode(private_key_bytes)); + + // Create the PrivateKeySigner + let inner = match PrivateKeySigner::from_bytes(&key) { + Ok(signer) => signer, + Err(e) => return Err(SignerError::InvalidPrivateKey(e.to_string())), + }; + + let address = inner.address(); Ok(Self { inner, @@ -190,28 +253,6 @@ impl LocalSigner { }) } - // jank - /// Extract the private key hex string from an Alloy PrivateKeySigner - fn extract_private_key(wallet: &PrivateKeySigner) -> Result { - // This is a placeholder implementation until we find a better way to extract - // the private key from PrivateKeySigner - let debug_str = format!("{:?}", wallet); - - // Parse the private key from the debug output - let start_marker = "private_key: PrivateKey("; - let end_marker = ")"; - - if let Some(start_idx) = debug_str.find(start_marker) { - let key_start = start_idx + start_marker.len(); - if let Some(end_idx) = debug_str[key_start..].find(end_marker) { - let key_hex = &debug_str[key_start..key_start + end_idx]; - return Ok(format!("0x{}", key_hex.trim_start_matches("0x"))); - } - } - - Err(SignerError::InvalidPrivateKey("Failed to extract private key".into())) - } - /// Encrypt this signer using a password pub fn encrypt(&self, password: &str) -> Result { // Extract the private key hex (without 0x prefix) diff --git a/src/wallet.rs b/src/wallet.rs index 7d80419..f5eed13 100644 --- a/src/wallet.rs +++ b/src/wallet.rs @@ -1,3 +1,13 @@ +//! ## (unfinished, unpolished and not fully tested) Ethereum wallet functionality for Hyperware. +//! +//! This module provides high-level wallet functionality for Ethereum, +//! including transaction signing, contract interaction, and account management. +//! It provides a simple interface for sending ETH and interacting with ERC20, +//! ERC721, and ERC1155 tokens. +//! +//! ERC6551 + the hypermap is not supported yet. +//! + use crate::eth::{ Provider, EthError, @@ -27,13 +37,13 @@ use alloy_primitives::{ Bytes }; use alloy::rpc::types::{ - TransactionReceipt, - TransactionRequest + Block, BlockId, Filter, FilterBlockOption, FilterSet, Log, Transaction, + TransactionReceipt, request::TransactionRequest, }; use alloy_primitives::TxKind; use std::str::FromStr; use alloy_sol_types::{sol, SolCall}; - +use serde::{Serialize, Deserialize}; sol! { interface IERC20 { function balanceOf(address who) external view returns (uint256); @@ -116,6 +126,56 @@ pub enum KeyStorage { Encrypted(EncryptedSignerData), } +// Manual implementation of Serialize for KeyStorage +impl Serialize for KeyStorage { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + + match self { + KeyStorage::Decrypted(signer) => { + let mut state = serializer.serialize_struct("KeyStorage", 2)?; + state.serialize_field("type", "Decrypted")?; + state.serialize_field("signer", signer)?; + state.end() + }, + KeyStorage::Encrypted(data) => { + let mut state = serializer.serialize_struct("KeyStorage", 2)?; + state.serialize_field("type", "Encrypted")?; + state.serialize_field("data", data)?; + state.end() + } + } + } +} + +// Manual implementation of Deserialize for KeyStorage +impl<'de> Deserialize<'de> for KeyStorage { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + #[derive(Deserialize)] + #[serde(tag = "type")] + enum KeyStorageData { + #[serde(rename = "Decrypted")] + Decrypted { signer: LocalSigner }, + + #[serde(rename = "Encrypted")] + Encrypted { data: EncryptedSignerData }, + } + + let data = KeyStorageData::deserialize(deserializer)?; + + match data { + KeyStorageData::Decrypted { signer } => Ok(KeyStorage::Decrypted(signer)), + KeyStorageData::Encrypted { data } => Ok(KeyStorage::Encrypted(data)), + } + } +} + impl KeyStorage { /// Get the encrypted data if this is an encrypted key storage pub fn get_encrypted_data(&self) -> Option> { @@ -191,6 +251,19 @@ impl EthAmount { /// Get a human-readable string representation pub fn to_string(&self) -> String { + // Just return the numerical value without denomination + if self.wei_value >= U256::from(100_000_000_000_000u128) { + // Convert to u128 first (safe since ETH total supply fits in u128) then to f64 + let wei_u128 = self.wei_value.to::(); + let eth_value = wei_u128 as f64 / 1_000_000_000_000_000_000.0; + format!("{:.6}", eth_value) + } else { + format!("{}", self.wei_value) + } + } + + /// Get a formatted string with denomination (for display purposes) + pub fn to_display_string(&self) -> String { // For values over 0.0001 ETH, show in ETH, otherwise in wei if self.wei_value >= U256::from(100_000_000_000_000u128) { // Convert to u128 first (safe since ETH total supply fits in u128) then to f64 @@ -254,7 +327,6 @@ fn call_view_function( /// Calculate gas parameters based on network type fn calculate_gas_params(provider: &Provider, chain_id: u64) -> Result<(u128, u128), WalletError> { - kiprintln!("PL:: Calculating gas parameters for chain ID: {}", chain_id); match chain_id { 1 => { // Mainnet: 50% buffer and 1.5 gwei priority fee @@ -268,28 +340,21 @@ fn calculate_gas_params(provider: &Provider, chain_id: u64) -> Result<(u128, u12 Ok((base_fee + (base_fee / 2), 1_500_000_000u128)) }, 8453 => { // Base - kiprintln!("PL:: Calculating gas parameters for Base"); let latest_block = provider.get_block_by_number(BlockNumberOrTag::Latest, false)? .ok_or_else(|| WalletError::TransactionError("Failed to get latest block".into()))?; - kiprintln!("PL:: Got latest block"); let base_fee = latest_block.header.inner.base_fee_per_gas .ok_or_else(|| WalletError::TransactionError("No base fee in block".into()))? as u128; - kiprintln!("PL:: Got base fee: {}", base_fee); let max_fee = base_fee + (base_fee / 3); - kiprintln!("PL:: max fee: {}", max_fee); let min_priority_fee = 100_000u128; - kiprintln!("PL:: min priority fee: {}", min_priority_fee); let max_priority_fee = max_fee / 2; - kiprintln!("PL:: max priority fee: {}", max_priority_fee); let priority_fee = std::cmp::max(min_priority_fee, std::cmp::min(base_fee / 10, max_priority_fee)); - kiprintln!("PL:: priority fee: {}", priority_fee); Ok((max_fee, priority_fee)) }, @@ -325,28 +390,21 @@ fn prepare_and_send_tx( format_receipt: F ) -> Result where F: FnOnce(TxHash) -> String { - kiprintln!("PL:: Preparing transaction..."); // Get the current nonce for the signer's address let signer_address = signer.address(); let nonce = provider.get_transaction_count(signer_address, None)? .to::(); - kiprintln!("PL:: Got nonce: {}", nonce); // Calculate gas parameters based on chain ID let (gas_price, priority_fee) = calculate_gas_params(provider, signer.chain_id())?; - kiprintln!("PL:: Calculated gas params - price: {}, priority fee: {}", gas_price, priority_fee); // Use provided gas limit or estimate it with 20% buffer let gas_limit = match gas_limit { - Some(limit) => { - kiprintln!("PL:: Using provided gas limit: {}", limit); - limit - }, + Some(limit) => limit, None => { - kiprintln!("PL:: Estimating gas limit..."); let tx_req = TransactionRequest { from: Some(signer_address), to: Some(TxKind::Call(to)), @@ -357,11 +415,9 @@ where F: FnOnce(TxHash) -> String { match provider.estimate_gas(tx_req, None) { Ok(gas) => { let limit = (gas.to::() * 120) / 100; // Add 20% buffer - kiprintln!("PL:: Estimated gas limit with buffer: {}", limit); limit }, Err(_) => { - kiprintln!("PL:: Gas estimation failed, using default: 100,000"); 100_000 // Default value if estimation fails } } @@ -380,12 +436,10 @@ where F: FnOnce(TxHash) -> String { chain_id: signer.chain_id(), }; - kiprintln!("PL:: Signing transaction..."); // Sign and send transaction let signed_tx = signer.sign_transaction(&tx_data)?; - kiprintln!("PL:: Sending transaction..."); let tx_hash = provider.send_raw_transaction(signed_tx.into())?; kiprintln!("PL:: Transaction sent with hash: {}", tx_hash); @@ -456,7 +510,7 @@ where F: FnOnce() -> String { // NAME RESOLUTION // -// Resolve a .hypr name to an Ethereum address using Hypermap +// Resolve a .hypr/.os/future tlzs names to an Ethereum address using Hypermap pub fn resolve_name(name: &str, chain_id: u64) -> Result { // If it's already an address, just parse it if name.starts_with("0x") && name.len() == 42 { @@ -573,6 +627,22 @@ pub fn resolve_token_symbol(token: &str, chain_id: u64) -> Result { // Sepolia Testnet + match token_upper.as_str() { + // Common tokens on Sepolia testnet + "USDC" => EthAddress::from_str("0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238") + .map_err(|_| WalletError::NameResolutionError("Invalid USDC address format".to_string())), + "USDT" => EthAddress::from_str("0x8267cF9254734C6Eb452a7bb9AAF97B392258b21") + .map_err(|_| WalletError::NameResolutionError("Invalid USDT address format".to_string())), + "DAI" => EthAddress::from_str("0x3e622317f8C93f7328350cF0B56d9eD4C620C5d6") + .map_err(|_| WalletError::NameResolutionError("Invalid DAI address format".to_string())), + "WETH" => EthAddress::from_str("0x7b79995e5f793A07Bc00c21412e50Ecae098E7f9") + .map_err(|_| WalletError::NameResolutionError("Invalid WETH address format".to_string())), + _ => Err(WalletError::NameResolutionError( + format!("Token '{}' not recognized on Sepolia testnet. Please use a contract address.", token) + )), + } + }, // Test networks 31337 | 1337 => { return Err(WalletError::NameResolutionError( @@ -810,8 +880,6 @@ pub fn erc20_transfer( let token_symbol = erc20_symbol(token_address, provider).unwrap_or_else(|_| "tokens".to_string()); let token_decimals = erc20_decimals(token_address, provider).unwrap_or(18); - kiprintln!("PL:: Token symbol: {}", token_symbol); - kiprintln!("PL:: Token decimals: {}", token_decimals); // Format receipt message let format_receipt = move |_| { @@ -819,7 +887,6 @@ pub fn erc20_transfer( format!("Transferred {:.6} {} to {}", amount_float, token_symbol, to_address) }; - kiprintln!("PL:: Sending ERC20 transfer transaction..."); // Prepare and send transaction prepare_and_send_tx( @@ -1365,3 +1432,307 @@ pub fn set_gene( signer ) } + + + +// TODO: TEST +// +// +// +// +// +// + +/// Transaction details in a more user-friendly format +#[derive(Debug, Clone)] +pub struct TransactionDetails { + pub hash: TxHash, + pub from: EthAddress, + pub to: Option, + pub value: EthAmount, + pub block_number: Option, + pub timestamp: Option, + pub gas_used: Option, + pub gas_price: Option, + pub success: Option, + pub direction: TransactionDirection, +} + +/// Direction of the transaction relative to the address +#[derive(Debug, Clone, PartialEq)] +pub enum TransactionDirection { + Incoming, + Outgoing, + SelfTransfer, +} + +/// Get transactions for an address - simplified version that works with Alloy +pub fn get_address_transactions( + address_or_name: &str, + provider: &Provider, + max_blocks_back: Option +) -> Result, WalletError> { + let target_address = resolve_name(address_or_name, provider.chain_id)?; + + // Get block range + let latest_block = provider.get_block_number()?; + let blocks_back = max_blocks_back.unwrap_or(1000); + let start_block = if latest_block > blocks_back { + latest_block - blocks_back + } else { + 0 + }; + + // Create filter to find logs involving our address + let filter = Filter { + block_option: FilterBlockOption::Range { + from_block: Some(start_block.into()), + to_block: Some(latest_block.into()), + }, + address: FilterSet::from(vec![target_address]), + topics: Default::default(), + }; + + // Get logs matching our filter + let logs = provider.get_logs(&filter)?; + kiprintln!("Found {} logs involving address {}", logs.len(), target_address); + + // Extract unique transaction hashes + let mut tx_hashes = Vec::new(); + for log in logs { + if let Some(hash) = log.transaction_hash { + if !tx_hashes.contains(&hash) { + tx_hashes.push(hash); + } + } + } + + // Create transaction details objects for each tx hash + let mut transactions = Vec::new(); + + for tx_hash in tx_hashes { + // For each transaction, create a basic TransactionDetails object + // with just the transaction hash and basic direction + let mut tx_detail = TransactionDetails { + hash: tx_hash, + from: EthAddress::default(), // We'll update this if we can get the transaction + to: None, + value: EthAmount { wei_value: U256::ZERO }, + block_number: None, + timestamp: None, + gas_used: None, + gas_price: None, + success: None, + direction: TransactionDirection::Incoming, // Default, will update if needed + }; + + // Try to get transaction receipt for more details + if let Ok(Some(receipt)) = provider.get_transaction_receipt(tx_hash) { + // Update from receipt fields that we know exist + tx_detail.block_number = receipt.block_number; + + // Get transaction success status if available + let status = receipt.status(); + if status { + tx_detail.success = Some(true); + } else { + tx_detail.success = Some(false); + } + + // Try to get original transaction for more details + if let Ok(Some(tx)) = provider.get_transaction_by_hash(tx_hash) { + // Update from transaction fields + tx_detail.from = tx.from; + + // Set direction based on compared addresses + if tx.from == target_address { + tx_detail.direction = TransactionDirection::Outgoing; + } else { + tx_detail.direction = TransactionDirection::Incoming; + } + + // Try to get block timestamp if we have block number + if let Some(block_num) = tx_detail.block_number { + if let Ok(Some(block)) = provider.get_block_by_number(BlockNumberOrTag::Number(block_num), false) { + // Block header timestamp is a u64, not an Option + tx_detail.timestamp = Some(block.header.timestamp); + } + } + } + } + + transactions.push(tx_detail); + } + + // Sort by block number (descending) + transactions.sort_by(|a, b| { + match (b.block_number, a.block_number) { + (Some(b_num), Some(a_num)) => b_num.cmp(&a_num), + (Some(_), None) => std::cmp::Ordering::Less, + (None, Some(_)) => std::cmp::Ordering::Greater, + (None, None) => std::cmp::Ordering::Equal, + } + }); + + Ok(transactions) +} + +/// Format transaction details for display +pub fn format_transaction_details(tx: &TransactionDetails) -> String { + // Symbol to represent transaction direction + let direction_symbol = match tx.direction { + TransactionDirection::Incoming => "←", + TransactionDirection::Outgoing => "→", + TransactionDirection::SelfTransfer => "↻", + }; + + // Transaction status + let status = match tx.success { + Some(true) => "Succeeded", + Some(false) => "Failed", + None => "Unknown", + }; + + // Format value + let value = tx.value.to_string(); + + // Format timestamp without external dependencies + let timestamp = match tx.timestamp { + Some(ts) => format_timestamp(ts), + None => "Pending".to_string(), + }; + + // Format to and from addresses + let from_addr = format!("{:.8}...{}", + tx.from.to_string()[0..10].to_string(), + tx.from.to_string()[34..].to_string()); + + let to_addr = tx.to.map_or("Contract Creation".to_string(), |addr| + format!("{:.8}...{}", + addr.to_string()[0..10].to_string(), + addr.to_string()[34..].to_string()) + ); + + // Format final output + format!( + "TX: {} [{}]\n {} {} {}\n Block: {}, Status: {}, Value: {}, Time: {}", + tx.hash, + status, + from_addr, + direction_symbol, + to_addr, + tx.block_number.map_or("Pending".to_string(), |b| b.to_string()), + status, + value, + timestamp + ) +} +/// Format a Unix timestamp without external dependencies +fn format_timestamp(timestamp: u64) -> String { + // Simple timestamp formatting + // We'll use a very basic approach that doesn't rely on date/time libraries + + // Convert to seconds, minutes, hours, days since epoch + let secs = timestamp % 60; + let mins = (timestamp / 60) % 60; + let hours = (timestamp / 3600) % 24; + let days_since_epoch = timestamp / 86400; + + // Very rough estimation - doesn't account for leap years properly + let years_since_epoch = days_since_epoch / 365; + let days_this_year = days_since_epoch % 365; + + // Rough month calculation + let month_days = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]; + let mut month = 0; + let mut day = days_this_year as u32; + + // Adjust for leap years in a very rough way + let is_leap_year = (1970 + years_since_epoch as u32) % 4 == 0; + let month_days = if is_leap_year { + let mut md = month_days.to_vec(); + md[1] = 29; // February has 29 days in leap years + md + } else { + month_days.to_vec() + }; + + // Find month and day + for (i, &days_in_month) in month_days.iter().enumerate() { + if day < days_in_month { + month = i; + break; + } + day -= days_in_month; + } + + // Adjust to 1-based + day += 1; + month += 1; + + // Format the date + format!( + "{:04}-{:02}-{:02} {:02}:{:02}:{:02}", + 1970 + years_since_epoch, + month, + day, + hours, + mins, + secs + ) +} + +// New structured type to hold all ERC20 token information +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct TokenDetails { + pub address: String, + pub symbol: String, + pub name: String, + pub decimals: u8, + pub total_supply: String, + pub balance: String, + pub formatted_balance: String, +} + +/// Get all relevant details for an ERC20 token in one call +/// This consolidates multiple calls into a single function for frontend use +pub fn get_token_details( + token_address: &str, + wallet_address: &str, + provider: &Provider +) -> Result { + // First resolve the token address (could be a symbol or address) + let token = match resolve_token_symbol(token_address, provider.chain_id) { + Ok(addr) => addr, + Err(_) => resolve_name(token_address, provider.chain_id)?, + }; + + // Get basic token information + let token_str = token.to_string(); + let symbol = erc20_symbol(token_address, provider)?; + let name = erc20_name(token_address, provider)?; + let decimals = erc20_decimals(token_address, provider)?; + + // Get total supply + let total_supply = erc20_total_supply(token_address, provider)?; + let total_supply_float = total_supply.to::() as f64 / 10f64.powi(decimals as i32); + let formatted_total_supply = format!("{:.2}", total_supply_float); + + // Get balance if wallet address is provided + let (balance, formatted_balance) = if !wallet_address.is_empty() { + let balance = erc20_balance_of(token_address, wallet_address, provider)?; + (balance.to_string(), format!("{:.6}", balance)) + } else { + ("0".to_string(), "0.000000".to_string()) + }; + + Ok(TokenDetails { + address: token_str, + symbol, + name, + decimals, + total_supply: formatted_total_supply, + balance, + formatted_balance, + }) +} From cace061b6c2e6474c461e2a291b532b599fc9e10 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 8 Apr 2025 11:38:05 +0000 Subject: [PATCH 10/96] Format Rust code using rustfmt --- src/eth.rs | 2 +- src/signer.rs | 46 ++-- src/wallet.rs | 664 +++++++++++++++++++++++++++----------------------- 3 files changed, 388 insertions(+), 324 deletions(-) diff --git a/src/eth.rs b/src/eth.rs index a5c72df..321bb4d 100644 --- a/src/eth.rs +++ b/src/eth.rs @@ -705,7 +705,7 @@ impl Provider { // NOTE: tx must be encased by a tuple to be serialized correctly params: serde_json::to_value((tx,)).unwrap(), }; - //TODO: remove + //TODO: remove kiprintln!("PROCESS_LIB::send_raw_transaction action: {:#?}", action); self.send_request_and_parse_response::(action) diff --git a/src/signer.rs b/src/signer.rs index c8191fd..b906797 100644 --- a/src/signer.rs +++ b/src/signer.rs @@ -122,7 +122,7 @@ impl Serialize for LocalSigner { S: serde::Serializer, { use serde::ser::SerializeStruct; - + // Serialize only the fields we need let mut state = serializer.serialize_struct("LocalSigner", 3)?; state.serialize_field("address", &self.address)?; @@ -144,13 +144,16 @@ impl<'de> Deserialize<'de> for LocalSigner { chain_id: u64, private_key_hex: String, } - + let data = LocalSignerData::deserialize(deserializer)?; - + // Reconstruct the LocalSigner from the private key match LocalSigner::from_private_key(&data.private_key_hex, data.chain_id) { Ok(signer) => Ok(signer), - Err(e) => Err(serde::de::Error::custom(format!("Failed to reconstruct signer: {}", e))), + Err(e) => Err(serde::de::Error::custom(format!( + "Failed to reconstruct signer: {}", + e + ))), } } } @@ -162,33 +165,36 @@ impl LocalSigner { let mut rng = thread_rng(); let mut private_key_bytes = [0u8; 32]; rng.fill_bytes(&mut private_key_bytes); - + // Make sure the private key is valid (less than curve order) - // TODO: This is a simplification - let max_scalar = hex::decode("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364140") - .map_err(|_| SignerError::RandomGenerationError("Failed to decode max scalar".to_string()))?; - + // TODO: This is a simplification + let max_scalar = + hex::decode("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364140") + .map_err(|_| { + SignerError::RandomGenerationError("Failed to decode max scalar".to_string()) + })?; + // Simple check: if our random bytes are >= max_scalar, regenerate // This is a simplified approach - production code would use more sophisticated comparison if private_key_bytes.as_slice().cmp(max_scalar.as_slice()) != std::cmp::Ordering::Less { // Try again with a new random value rng.fill_bytes(&mut private_key_bytes); } - + // Convert to B256 for the PrivateKeySigner let key = B256::from_slice(&private_key_bytes); - + // Store the private key hex string for later use let private_key_hex = format!("0x{}", hex::encode(private_key_bytes)); - + // Create the PrivateKeySigner let inner = match PrivateKeySigner::from_bytes(&key) { Ok(signer) => signer, Err(e) => return Err(SignerError::InvalidPrivateKey(e.to_string())), }; - + let address = inner.address(); - + Ok(Self { inner, address, @@ -245,7 +251,7 @@ impl LocalSigner { private_key_hex, }) } - + /// Encrypt this signer using a password pub fn encrypt(&self, password: &str) -> Result { // Extract the private key hex (without 0x prefix) @@ -302,12 +308,12 @@ impl Signer for LocalSigner { actual: tx_data.chain_id, }); } - - // Convert hyperware types to alloy types + + // Convert hyperware types to alloy types let to_str = tx_data.to.to_string(); let to = alloy_primitives::Address::from_str(&to_str) .map_err(|e| SignerError::SigningError(format!("Invalid contract address: {}", e)))?; - + // Create transaction based on chain type // Both Ethereum mainnet and Base use EIP-1559 transactions let mut tx = TxEip1559 { @@ -317,7 +323,7 @@ impl Signer for LocalSigner { gas_limit: tx_data.gas_limit, max_fee_per_gas: tx_data.gas_price, // Use provided priority fee or calculate a reasonable default based on the chain - max_priority_fee_per_gas: tx_data.max_priority_fee.unwrap_or_else(|| + max_priority_fee_per_gas: tx_data.max_priority_fee.unwrap_or_else(|| { match tx_data.chain_id { // Ethereum mainnet (1) 1 => tx_data.gas_price / 10, @@ -326,7 +332,7 @@ impl Signer for LocalSigner { // Default fallback for other networks _ => tx_data.gas_price / 10, } - ), + }), input: tx_data.data.clone().unwrap_or_default().into(), value: tx_data.value, ..Default::default() diff --git a/src/wallet.rs b/src/wallet.rs index a2d9b95..0746347 100644 --- a/src/wallet.rs +++ b/src/wallet.rs @@ -6,44 +6,24 @@ //! ERC721, and ERC1155 tokens. //! //! ERC6551 + the hypermap is not supported yet. -//! +//! -use crate::eth::{ - Provider, - EthError, - BlockNumberOrTag -}; -use crate::signer::{ - Signer, - LocalSigner, - TransactionData, - SignerError, - EncryptedSignerData -}; -use crate::hypermap::{ - namehash, - valid_note, - valid_fact, - valid_name, -}; +use crate::eth::{BlockNumberOrTag, EthError, Provider}; use crate::hypermap; +use crate::hypermap::{namehash, valid_fact, valid_name, valid_note}; use crate::kiprintln; +use crate::signer::{EncryptedSignerData, LocalSigner, Signer, SignerError, TransactionData}; -use thiserror::Error; -use alloy_primitives::{ - Address as EthAddress, - TxHash, - U256, - Bytes -}; use alloy::rpc::types::{ - Block, BlockId, Filter, FilterBlockOption, FilterSet, Log, Transaction, - TransactionReceipt, request::TransactionRequest, + request::TransactionRequest, Block, BlockId, Filter, FilterBlockOption, FilterSet, Log, + Transaction, TransactionReceipt, }; use alloy_primitives::TxKind; -use std::str::FromStr; +use alloy_primitives::{Address as EthAddress, Bytes, TxHash, U256}; use alloy_sol_types::{sol, SolCall}; -use serde::{Serialize, Deserialize}; +use serde::{Deserialize, Serialize}; +use std::str::FromStr; +use thiserror::Error; sol! { interface IERC20 { function balanceOf(address who) external view returns (uint256); @@ -101,19 +81,19 @@ pub enum WalletError { #[error("gas estimation error: {0}")] GasEstimationError(String), - + #[error("insufficient funds: {0}")] InsufficientFunds(String), - + #[error("network congestion: {0}")] NetworkCongestion(String), - + #[error("transaction underpriced")] TransactionUnderpriced, - + #[error("transaction nonce too low")] TransactionNonceTooLow, - + #[error("permission denied: {0}")] PermissionDenied(String), } @@ -133,14 +113,14 @@ impl Serialize for KeyStorage { S: serde::Serializer, { use serde::ser::SerializeStruct; - + match self { KeyStorage::Decrypted(signer) => { let mut state = serializer.serialize_struct("KeyStorage", 2)?; state.serialize_field("type", "Decrypted")?; state.serialize_field("signer", signer)?; state.end() - }, + } KeyStorage::Encrypted(data) => { let mut state = serializer.serialize_struct("KeyStorage", 2)?; state.serialize_field("type", "Encrypted")?; @@ -162,13 +142,13 @@ impl<'de> Deserialize<'de> for KeyStorage { enum KeyStorageData { #[serde(rename = "Decrypted")] Decrypted { signer: LocalSigner }, - + #[serde(rename = "Encrypted")] Encrypted { data: EncryptedSignerData }, } - + let data = KeyStorageData::deserialize(deserializer)?; - + match data { KeyStorageData::Decrypted { signer } => Ok(KeyStorage::Decrypted(signer)), KeyStorageData::Encrypted { data } => Ok(KeyStorage::Encrypted(data)), @@ -309,7 +289,7 @@ pub struct HypermapTxReceipt { fn call_view_function( contract: EthAddress, call: T, - provider: &Provider + provider: &Provider, ) -> Result { let call_data = call.abi_encode(); let tx = TransactionRequest { @@ -317,68 +297,95 @@ fn call_view_function( input: call_data.into(), ..Default::default() }; - + let result = provider.call(tx, None)?; - + if result.is_empty() { - return Err(WalletError::TransactionError("Empty result from call".into())); + return Err(WalletError::TransactionError( + "Empty result from call".into(), + )); } - + match T::abi_decode_returns(&result, true) { Ok(decoded) => Ok(decoded), - Err(e) => Err(WalletError::TransactionError( - format!("Failed to decode result: {}", e) - )) + Err(e) => Err(WalletError::TransactionError(format!( + "Failed to decode result: {}", + e + ))), } } /// Calculate gas parameters based on network type fn calculate_gas_params(provider: &Provider, chain_id: u64) -> Result<(u128, u128), WalletError> { - match chain_id { - 1 => { // Mainnet: 50% buffer and 1.5 gwei priority fee - let latest_block = provider.get_block_by_number(BlockNumberOrTag::Latest, false)? - .ok_or_else(|| WalletError::TransactionError("Failed to get latest block".into()))?; - - let base_fee = latest_block.header.inner.base_fee_per_gas + 1 => { + // Mainnet: 50% buffer and 1.5 gwei priority fee + let latest_block = provider + .get_block_by_number(BlockNumberOrTag::Latest, false)? + .ok_or_else(|| { + WalletError::TransactionError("Failed to get latest block".into()) + })?; + + let base_fee = latest_block + .header + .inner + .base_fee_per_gas .ok_or_else(|| WalletError::TransactionError("No base fee in block".into()))? as u128; - + Ok((base_fee + (base_fee / 2), 1_500_000_000u128)) - }, - 8453 => { // Base - let latest_block = provider.get_block_by_number(BlockNumberOrTag::Latest, false)? - .ok_or_else(|| WalletError::TransactionError("Failed to get latest block".into()))?; - - let base_fee = latest_block.header.inner.base_fee_per_gas + } + 8453 => { + // Base + let latest_block = provider + .get_block_by_number(BlockNumberOrTag::Latest, false)? + .ok_or_else(|| { + WalletError::TransactionError("Failed to get latest block".into()) + })?; + + let base_fee = latest_block + .header + .inner + .base_fee_per_gas .ok_or_else(|| WalletError::TransactionError("No base fee in block".into()))? as u128; - - + let max_fee = base_fee + (base_fee / 3); - + let min_priority_fee = 100_000u128; - + let max_priority_fee = max_fee / 2; - - let priority_fee = std::cmp::max(min_priority_fee, std::cmp::min(base_fee / 10, max_priority_fee)); + + let priority_fee = std::cmp::max( + min_priority_fee, + std::cmp::min(base_fee / 10, max_priority_fee), + ); Ok((max_fee, priority_fee)) - }, - 10 => { // Optimism: 25% buffer and 0.3 gwei priority fee - let latest_block = provider.get_block_by_number(BlockNumberOrTag::Latest, false)? - .ok_or_else(|| WalletError::TransactionError("Failed to get latest block".into()))?; - - let base_fee = latest_block.header.inner.base_fee_per_gas + } + 10 => { + // Optimism: 25% buffer and 0.3 gwei priority fee + let latest_block = provider + .get_block_by_number(BlockNumberOrTag::Latest, false)? + .ok_or_else(|| { + WalletError::TransactionError("Failed to get latest block".into()) + })?; + + let base_fee = latest_block + .header + .inner + .base_fee_per_gas .ok_or_else(|| WalletError::TransactionError("No base fee in block".into()))? as u128; - + Ok((base_fee + (base_fee / 4), 300_000_000u128)) - }, - 31337 | 1337 => { // Test networks + } + 31337 | 1337 => { + // Test networks Ok((2_000_000_000, 100_000_000)) - }, - _ => { // Default: 30% buffer + } + _ => { + // Default: 30% buffer let base_fee = provider.get_gas_price()?.to::(); let adjusted_fee = (base_fee * 130) / 100; Ok((adjusted_fee, adjusted_fee / 10)) @@ -394,20 +401,20 @@ fn prepare_and_send_tx( provider: &Provider, signer: &S, gas_limit: Option, - format_receipt: F + format_receipt: F, ) -> Result -where F: FnOnce(TxHash) -> String { - +where + F: FnOnce(TxHash) -> String, +{ // Get the current nonce for the signer's address let signer_address = signer.address(); - let nonce = provider.get_transaction_count(signer_address, None)? + let nonce = provider + .get_transaction_count(signer_address, None)? .to::(); - - + // Calculate gas parameters based on chain ID let (gas_price, priority_fee) = calculate_gas_params(provider, signer.chain_id())?; - - + // Use provided gas limit or estimate it with 20% buffer let gas_limit = match gas_limit { Some(limit) => limit, @@ -418,19 +425,19 @@ where F: FnOnce(TxHash) -> String { input: call_data.clone().into(), ..Default::default() }; - + match provider.estimate_gas(tx_req, None) { Ok(gas) => { let limit = (gas.to::() * 120) / 100; // Add 20% buffer limit - }, + } Err(_) => { 100_000 // Default value if estimation fails } } } }; - + // Prepare transaction data let tx_data = TransactionData { to, @@ -442,15 +449,14 @@ where F: FnOnce(TxHash) -> String { max_priority_fee: Some(priority_fee), chain_id: signer.chain_id(), }; - - + // Sign and send transaction let signed_tx = signer.sign_transaction(&tx_data)?; - + let tx_hash = provider.send_raw_transaction(signed_tx.into())?; - + kiprintln!("PL:: Transaction sent with hash: {}", tx_hash); - + // Return the receipt with formatted details Ok(TxReceipt { hash: tx_hash, @@ -464,22 +470,25 @@ fn create_hypermap_tx( hypermap_call_data: Bytes, description_fn: F, provider: Provider, - signer: &S + signer: &S, ) -> Result -where F: FnOnce() -> String { +where + F: FnOnce() -> String, +{ // Get the parent TBA address and verify ownership let hypermap = provider.hypermap(); let parent_hash_str = namehash(parent_entry); let (tba, owner, _) = hypermap.get_hash(&parent_hash_str)?; - + // Check that the signer is the owner of the parent entry let signer_address = signer.address(); if signer_address != owner { - return Err(WalletError::PermissionDenied( - format!("Signer address {} does not own the entry {}", signer_address, parent_entry) - )); + return Err(WalletError::PermissionDenied(format!( + "Signer address {} does not own the entry {}", + signer_address, parent_entry + ))); } - + // Create the ERC-6551 execute call let execute_call = IERC6551Account::executeCall { to: *hypermap.address(), @@ -487,14 +496,14 @@ where F: FnOnce() -> String { data: hypermap_call_data, operation: 0, // CALL operation }; - + // Format receipt message let description = description_fn(); let format_receipt = move |_| description.clone(); - + // For ERC-6551 operations we need a higher gas limit let gas_limit = Some(600_000); - + // Send the transaction let receipt = prepare_and_send_tx( tba, @@ -503,9 +512,9 @@ where F: FnOnce() -> String { &provider, signer, gas_limit, - format_receipt + format_receipt, )?; - + // Convert to Hypermap receipt Ok(HypermapTxReceipt { hash: receipt.hash, @@ -525,7 +534,7 @@ pub fn resolve_name(name: &str, chain_id: u64) -> Result Result Result { // If it's already an address, just parse it if token.starts_with("0x") && token.len() == 42 { - return EthAddress::from_str(token) - .map_err(|_| WalletError::NameResolutionError(format!("Invalid address format: {}", token))); + return EthAddress::from_str(token).map_err(|_| { + WalletError::NameResolutionError(format!("Invalid address format: {}", token)) + }); } - + // Convert to uppercase for case-insensitive comparison let token_upper = token.to_uppercase(); - + // Map of known token addresses by chain ID and symbol match chain_id { 1 => { // Ethereum Mainnet @@ -668,7 +678,6 @@ pub fn resolve_token_symbol(token: &str, chain_id: u64) -> Result( ) -> Result { // Resolve the name to an address let to_address = resolve_name(to, signer.chain_id())?; - + // Standard gas limit for ETH transfer is always 21000 let gas_limit = Some(21000); - + // Format receipt message let amount_str = amount.to_string(); - let format_receipt = move |_tx_hash| { - format!("Sent {} to {}", amount_str, to) - }; - + let format_receipt = move |_tx_hash| format!("Sent {} to {}", amount_str, to); + // For ETH transfers, we have no call data let empty_call_data = vec![]; - + // Use the helper function to prepare and send the transaction prepare_and_send_tx( to_address, @@ -703,7 +710,7 @@ pub fn send_eth( &provider, signer, gas_limit, - format_receipt + format_receipt, ) } @@ -720,40 +727,39 @@ pub fn get_eth_balance( let balance = provider.get_balance(address, None)?; // Return formatted amount - Ok(EthAmount { - wei_value: balance, - }) + Ok(EthAmount { wei_value: balance }) } /// Wait for a transaction to be confirmed pub fn wait_for_transaction( - tx_hash: TxHash, + tx_hash: TxHash, provider: Provider, confirmations: u64, - timeout_secs: u64 + timeout_secs: u64, ) -> Result { let start_time = std::time::Instant::now(); let timeout = std::time::Duration::from_secs(timeout_secs); - + loop { // Check if we've exceeded the timeout if start_time.elapsed() > timeout { - return Err(WalletError::TransactionError( - format!("Transaction confirmation timeout after {} seconds", timeout_secs) - )); + return Err(WalletError::TransactionError(format!( + "Transaction confirmation timeout after {} seconds", + timeout_secs + ))); } - + // Try to get the receipt if let Ok(Some(receipt)) = provider.get_transaction_receipt(tx_hash) { // Check if we have enough confirmations let latest_block = provider.get_block_number()?; let receipt_block = receipt.block_number.unwrap_or(0) as u64; - + if latest_block >= receipt_block + confirmations { return Ok(receipt); } } - + // Wait a bit before checking again std::thread::sleep(std::time::Duration::from_secs(2)); } @@ -768,22 +774,22 @@ pub fn wait_for_transaction( pub fn erc20_balance_of( token_address: &str, owner_address: &str, - provider: &Provider + provider: &Provider, ) -> Result { // First try to resolve the token as a symbol let token = match resolve_token_symbol(token_address, provider.chain_id) { Ok(addr) => addr, Err(_) => resolve_name(token_address, provider.chain_id)?, // Fall back to regular name resolution }; - + let owner = resolve_name(owner_address, provider.chain_id)?; - + let call = IERC20::balanceOfCall { who: owner }; let balance = call_view_function(token, call, provider)?; - + let decimals = erc20_decimals(token_address, provider)?; let balance_float = balance._0.to::() as f64 / 10f64.powi(decimals as i32); - + Ok(balance_float) } @@ -793,7 +799,7 @@ pub fn erc20_decimals(token_address: &str, provider: &Provider) -> Result addr, Err(_) => resolve_name(token_address, provider.chain_id)?, }; - + let call = IERC20::decimalsCall {}; let decimals = call_view_function(token, call, provider)?; Ok(decimals._0) @@ -805,7 +811,7 @@ pub fn erc20_symbol(token_address: &str, provider: &Provider) -> Result addr, Err(_) => resolve_name(token_address, provider.chain_id)?, }; - + let call = IERC20::symbolCall {}; let symbol = call_view_function(token, call, provider)?; Ok(symbol._0) @@ -817,7 +823,7 @@ pub fn erc20_name(token_address: &str, provider: &Provider) -> Result addr, Err(_) => resolve_name(token_address, provider.chain_id)?, }; - + let call = IERC20::nameCall {}; let name = call_view_function(token, call, provider)?; Ok(name._0) @@ -829,7 +835,7 @@ pub fn erc20_total_supply(token_address: &str, provider: &Provider) -> Result addr, Err(_) => resolve_name(token_address, provider.chain_id)?, }; - + let call = IERC20::totalSupplyCall {}; let total_supply = call_view_function(token, call, provider)?; Ok(total_supply._0) @@ -840,16 +846,16 @@ pub fn erc20_allowance( token_address: &str, owner_address: &str, spender_address: &str, - provider: &Provider + provider: &Provider, ) -> Result { let token = match resolve_token_symbol(token_address, provider.chain_id) { Ok(addr) => addr, Err(_) => resolve_name(token_address, provider.chain_id)?, }; - + let owner = resolve_name(owner_address, provider.chain_id)?; let spender = resolve_name(spender_address, provider.chain_id)?; - + let call = IERC20::allowanceCall { owner, spender }; let allowance = call_view_function(token, call, provider)?; Ok(allowance._0) @@ -861,47 +867,54 @@ pub fn erc20_transfer( to_address: &str, amount: U256, provider: &Provider, - signer: &S + signer: &S, ) -> Result { - kiprintln!("PL:: Transferring {} tokens to {} on {}", amount, to_address, provider.chain_id); + kiprintln!( + "PL:: Transferring {} tokens to {} on {}", + amount, + to_address, + provider.chain_id + ); // Resolve token address (could be a symbol like "USDC") let token = match resolve_token_symbol(token_address, provider.chain_id) { Ok(addr) => addr, Err(_) => resolve_name(token_address, provider.chain_id)?, }; - + kiprintln!("PL:: Resolved token address: {}", token); - + // Resolve recipient address let to = resolve_name(to_address, provider.chain_id)?; kiprintln!("PL:: Resolved recipient address: {}", to); - + // Create the call let call = IERC20::transferCall { to, value: amount }; let call_data = call.abi_encode(); - + // Get token details for receipt formatting - let token_symbol = erc20_symbol(token_address, provider).unwrap_or_else(|_| "tokens".to_string()); + let token_symbol = + erc20_symbol(token_address, provider).unwrap_or_else(|_| "tokens".to_string()); let token_decimals = erc20_decimals(token_address, provider).unwrap_or(18); - - + // Format receipt message let format_receipt = move |_| { let amount_float = amount.to::() as f64 / 10f64.powi(token_decimals as i32); - format!("Transferred {:.6} {} to {}", amount_float, token_symbol, to_address) + format!( + "Transferred {:.6} {} to {}", + amount_float, token_symbol, to_address + ) }; - - + // Prepare and send transaction prepare_and_send_tx( - token, - call_data, - U256::ZERO, + token, + call_data, + U256::ZERO, provider, signer, Some(100_000), // Default gas limit for ERC20 transfers - format_receipt + format_receipt, ) } @@ -911,26 +924,33 @@ pub fn erc20_approve( spender_address: &str, amount: U256, provider: &Provider, - signer: &S + signer: &S, ) -> Result { // Resolve addresses let token = resolve_name(token_address, provider.chain_id)?; let spender = resolve_name(spender_address, provider.chain_id)?; - + // Create the call - let call = IERC20::approveCall { spender, value: amount }; + let call = IERC20::approveCall { + spender, + value: amount, + }; let call_data = call.abi_encode(); - + // Get token details for receipt formatting - let token_symbol = erc20_symbol(token_address, provider).unwrap_or_else(|_| "tokens".to_string()); + let token_symbol = + erc20_symbol(token_address, provider).unwrap_or_else(|_| "tokens".to_string()); let token_decimals = erc20_decimals(token_address, provider).unwrap_or(18); - + // Format receipt message let format_receipt = move |_| { let amount_float = amount.to::() as f64 / 10f64.powi(token_decimals as i32); - format!("Approved {:.6} {} to be spent by {}", amount_float, token_symbol, spender_address) + format!( + "Approved {:.6} {} to be spent by {}", + amount_float, token_symbol, spender_address + ) }; - + // Prepare and send transaction prepare_and_send_tx( token, @@ -939,7 +959,7 @@ pub fn erc20_approve( provider, signer, Some(60_000), // Default gas limit for approvals - format_receipt + format_receipt, ) } @@ -950,28 +970,35 @@ pub fn erc20_transfer_from( to_address: &str, amount: U256, provider: &Provider, - signer: &S + signer: &S, ) -> Result { // Resolve addresses let token = resolve_name(token_address, provider.chain_id)?; let from = resolve_name(from_address, provider.chain_id)?; let to = resolve_name(to_address, provider.chain_id)?; - + // Create the call - let call = IERC20::transferFromCall { from, to, value: amount }; + let call = IERC20::transferFromCall { + from, + to, + value: amount, + }; let call_data = call.abi_encode(); - + // Get token details for receipt formatting - let token_symbol = erc20_symbol(token_address, provider).unwrap_or_else(|_| "tokens".to_string()); + let token_symbol = + erc20_symbol(token_address, provider).unwrap_or_else(|_| "tokens".to_string()); let token_decimals = erc20_decimals(token_address, provider).unwrap_or(18); - + // Format receipt message let format_receipt = move |_| { let amount_float = amount.to::() as f64 / 10f64.powi(token_decimals as i32); - format!("Transferred {:.6} {} from {} to {}", - amount_float, token_symbol, from_address, to_address) + format!( + "Transferred {:.6} {} from {} to {}", + amount_float, token_symbol, from_address, to_address + ) }; - + // Prepare and send transaction prepare_and_send_tx( token, @@ -980,7 +1007,7 @@ pub fn erc20_transfer_from( provider, signer, Some(100_000), // Default gas limit - format_receipt + format_receipt, ) } @@ -992,11 +1019,11 @@ pub fn erc20_transfer_from( pub fn erc721_balance_of( token_address: &str, owner_address: &str, - provider: &Provider + provider: &Provider, ) -> Result { let token = resolve_name(token_address, provider.chain_id)?; let owner = resolve_name(owner_address, provider.chain_id)?; - + let call = IERC721::balanceOfCall { owner }; let balance = call_view_function(token, call, provider)?; Ok(balance._0) @@ -1006,7 +1033,7 @@ pub fn erc721_balance_of( pub fn erc721_owner_of( token_address: &str, token_id: U256, - provider: &Provider + provider: &Provider, ) -> Result { let token = resolve_name(token_address, provider.chain_id)?; let call = IERC721::ownerOfCall { tokenId: token_id }; @@ -1019,12 +1046,12 @@ pub fn erc721_is_approved_for_all( token_address: &str, owner_address: &str, operator_address: &str, - provider: &Provider + provider: &Provider, ) -> Result { let token = resolve_name(token_address, provider.chain_id)?; let owner = resolve_name(owner_address, provider.chain_id)?; let operator = resolve_name(operator_address, provider.chain_id)?; - + let call = IERC721::isApprovedForAllCall { owner, operator }; let is_approved = call_view_function(token, call, provider)?; Ok(is_approved._0) @@ -1037,22 +1064,29 @@ pub fn erc721_safe_transfer_from( to_address: &str, token_id: U256, provider: &Provider, - signer: &S + signer: &S, ) -> Result { // Resolve addresses let token = resolve_name(token_address, provider.chain_id)?; let from = resolve_name(from_address, provider.chain_id)?; let to = resolve_name(to_address, provider.chain_id)?; - + // Create the call - let call = IERC721::safeTransferFromCall { from, to, tokenId: token_id }; + let call = IERC721::safeTransferFromCall { + from, + to, + tokenId: token_id, + }; let call_data = call.abi_encode(); - + // Format receipt message let format_receipt = move |_| { - format!("Safely transferred NFT ID {} from {} to {}", token_id, from_address, to_address) + format!( + "Safely transferred NFT ID {} from {} to {}", + token_id, from_address, to_address + ) }; - + // Prepare and send transaction prepare_and_send_tx( token, @@ -1061,7 +1095,7 @@ pub fn erc721_safe_transfer_from( provider, signer, Some(200_000), // Higher gas limit for NFT transfers - format_receipt + format_receipt, ) } @@ -1071,26 +1105,30 @@ pub fn erc721_set_approval_for_all( operator_address: &str, approved: bool, provider: &Provider, - signer: &S + signer: &S, ) -> Result { // Resolve addresses let token = resolve_name(token_address, provider.chain_id)?; let operator = resolve_name(operator_address, provider.chain_id)?; - + // Create the call let call = IERC721::setApprovalForAllCall { operator, approved }; let call_data = call.abi_encode(); - + // Format receipt message let format_receipt = move |_| { format!( - "{} operator {} to manage all of your NFTs in contract {}", - if approved { "Approved" } else { "Revoked approval for" }, + "{} operator {} to manage all of your NFTs in contract {}", + if approved { + "Approved" + } else { + "Revoked approval for" + }, operator_address, token_address ) }; - + // Prepare and send transaction prepare_and_send_tx( token, @@ -1099,7 +1137,7 @@ pub fn erc721_set_approval_for_all( provider, signer, Some(60_000), // Default gas limit for approvals - format_receipt + format_receipt, ) } @@ -1112,12 +1150,15 @@ pub fn erc1155_balance_of( token_address: &str, account_address: &str, token_id: U256, - provider: &Provider + provider: &Provider, ) -> Result { let token = resolve_name(token_address, provider.chain_id)?; let account = resolve_name(account_address, provider.chain_id)?; - - let call = IERC1155::balanceOfCall { account, id: token_id }; + + let call = IERC1155::balanceOfCall { + account, + id: token_id, + }; let balance = call_view_function(token, call, provider)?; Ok(balance._0) } @@ -1127,25 +1168,28 @@ pub fn erc1155_balance_of_batch( token_address: &str, account_addresses: Vec<&str>, token_ids: Vec, - provider: &Provider + provider: &Provider, ) -> Result, WalletError> { // Check that arrays are same length if account_addresses.len() != token_ids.len() { return Err(WalletError::TransactionError( - "Arrays of accounts and token IDs must have the same length".into() + "Arrays of accounts and token IDs must have the same length".into(), )); } - + // Resolve token address let token = resolve_name(token_address, provider.chain_id)?; - + // Resolve all account addresses let mut accounts = Vec::with_capacity(account_addresses.len()); for addr in account_addresses { accounts.push(resolve_name(addr, provider.chain_id)?); } - - let call = IERC1155::balanceOfBatchCall { accounts, ids: token_ids }; + + let call = IERC1155::balanceOfBatchCall { + accounts, + ids: token_ids, + }; let balances = call_view_function(token, call, provider)?; Ok(balances._0) } @@ -1155,12 +1199,12 @@ pub fn erc1155_is_approved_for_all( token_address: &str, account_address: &str, operator_address: &str, - provider: &Provider + provider: &Provider, ) -> Result { let token = resolve_name(token_address, provider.chain_id)?; let account = resolve_name(account_address, provider.chain_id)?; let operator = resolve_name(operator_address, provider.chain_id)?; - + let call = IERC1155::isApprovedForAllCall { account, operator }; let is_approved = call_view_function(token, call, provider)?; Ok(is_approved._0) @@ -1172,26 +1216,30 @@ pub fn erc1155_set_approval_for_all( operator_address: &str, approved: bool, provider: &Provider, - signer: &S + signer: &S, ) -> Result { // Resolve addresses let token = resolve_name(token_address, provider.chain_id)?; let operator = resolve_name(operator_address, provider.chain_id)?; - + // Create the call let call = IERC1155::setApprovalForAllCall { operator, approved }; let call_data = call.abi_encode(); - + // Format receipt message let format_receipt = move |_| { format!( - "{} operator {} to manage all of your ERC1155 tokens in contract {}", - if approved { "Approved" } else { "Revoked approval for" }, + "{} operator {} to manage all of your ERC1155 tokens in contract {}", + if approved { + "Approved" + } else { + "Revoked approval for" + }, operator_address, token_address ) }; - + // Prepare and send transaction prepare_and_send_tx( token, @@ -1200,7 +1248,7 @@ pub fn erc1155_set_approval_for_all( provider, signer, Some(60_000), // Default gas limit for approvals - format_receipt + format_receipt, ) } @@ -1213,31 +1261,31 @@ pub fn erc1155_safe_transfer_from( amount: U256, data: Vec, provider: &Provider, - signer: &S + signer: &S, ) -> Result { // Resolve addresses let token = resolve_name(token_address, provider.chain_id)?; let from = resolve_name(from_address, provider.chain_id)?; let to = resolve_name(to_address, provider.chain_id)?; - + // Create the call - let call = IERC1155::safeTransferFromCall { - from, - to, + let call = IERC1155::safeTransferFromCall { + from, + to, id: token_id, amount, - data: Bytes::from(data) + data: Bytes::from(data), }; let call_data = call.abi_encode(); - + // Format receipt message let format_receipt = move |_| { format!( - "Transferred {} of token ID {} from {} to {}", + "Transferred {} of token ID {} from {} to {}", amount, token_id, from_address, to_address ) }; - + // Prepare and send transaction prepare_and_send_tx( token, @@ -1246,7 +1294,7 @@ pub fn erc1155_safe_transfer_from( provider, signer, Some(150_000), // Default gas limit for ERC1155 transfers - format_receipt + format_receipt, ) } @@ -1259,43 +1307,43 @@ pub fn erc1155_safe_batch_transfer_from( amounts: Vec, data: Vec, provider: &Provider, - signer: &S + signer: &S, ) -> Result { // Check that arrays are same length if token_ids.len() != amounts.len() { return Err(WalletError::TransactionError( - "Arrays of token IDs and amounts must have the same length".into() + "Arrays of token IDs and amounts must have the same length".into(), )); } - + // Resolve addresses let token = resolve_name(token_address, provider.chain_id)?; let from = resolve_name(from_address, provider.chain_id)?; let to = resolve_name(to_address, provider.chain_id)?; - + // Create the call - let call = IERC1155::safeBatchTransferFromCall { - from, - to, + let call = IERC1155::safeBatchTransferFromCall { + from, + to, ids: token_ids.clone(), amounts: amounts.clone(), - data: Bytes::from(data) + data: Bytes::from(data), }; let call_data = call.abi_encode(); - + // For batch transfers, gas estimation is tricky - use a formula that scales with token count let token_count = token_ids.len(); // Base gas (200,000) + extra per token (50,000 each) let default_gas = Some(200_000 + (token_count as u64 * 50_000)); - + // Format receipt message let format_receipt = move |_| { format!( - "Batch transferred {} token types from {} to {}", + "Batch transferred {} token types from {} to {}", token_count, from_address, to_address ) }; - + // Prepare and send transaction prepare_and_send_tx( token, @@ -1304,7 +1352,7 @@ pub fn erc1155_safe_batch_transfer_from( provider, signer, default_gas, - format_receipt + format_receipt, ) } @@ -1332,14 +1380,14 @@ pub fn create_note( note: Bytes::from(note_key.as_bytes().to_vec()), data: Bytes::from(data), }; - + // Create the hypermap transaction create_hypermap_tx( parent_entry, Bytes::from(note_function.abi_encode()), || format!("Created note '{}' on '{}'", note_key, parent_entry), provider, - signer + signer, ) } @@ -1363,14 +1411,14 @@ pub fn create_fact( fact: Bytes::from(fact_key.as_bytes().to_vec()), data: Bytes::from(data), }; - + // Create the hypermap transaction create_hypermap_tx( parent_entry, Bytes::from(fact_function.abi_encode()), || format!("Created fact '{}' on '{}'", fact_key, parent_entry), provider, - signer + signer, ) } @@ -1385,9 +1433,10 @@ pub fn mint_entry( ) -> Result { // Verify the label is valid if !valid_name(label) { - return Err(WalletError::NameResolutionError( - format!("Invalid label '{}'. Must contain only lowercase letters, numbers, and hyphens", label) - )); + return Err(WalletError::NameResolutionError(format!( + "Invalid label '{}'. Must contain only lowercase letters, numbers, and hyphens", + label + ))); } // Resolve addresses @@ -1399,17 +1448,17 @@ pub fn mint_entry( who: recipient_address, label: Bytes::from(label.as_bytes().to_vec()), initialization: Bytes::default(), // No initialization data - erc721Data: Bytes::default(), // No ERC721 data + erc721Data: Bytes::default(), // No ERC721 data implementation: implementation_address, }; - + // Create the hypermap transaction create_hypermap_tx( parent_entry, Bytes::from(mint_function.abi_encode()), || format!("Minted new entry '{}' under '{}'", label, parent_entry), provider, - signer + signer, ) } @@ -1427,19 +1476,17 @@ pub fn set_gene( let gene_function = hypermap::contract::geneCall { _gene: gene_address, }; - + // Create the hypermap transaction create_hypermap_tx( entry, Bytes::from(gene_function.abi_encode()), || format!("Set gene for '{}' to '{}'", entry, gene_implementation), provider, - signer + signer, ) } - - // TODO: TEST // // @@ -1475,10 +1522,10 @@ pub enum TransactionDirection { pub fn get_address_transactions( address_or_name: &str, provider: &Provider, - max_blocks_back: Option + max_blocks_back: Option, ) -> Result, WalletError> { let target_address = resolve_name(address_or_name, provider.chain_id)?; - + // Get block range let latest_block = provider.get_block_number()?; let blocks_back = max_blocks_back.unwrap_or(1000); @@ -1487,7 +1534,7 @@ pub fn get_address_transactions( } else { 0 }; - + // Create filter to find logs involving our address let filter = Filter { block_option: FilterBlockOption::Range { @@ -1497,11 +1544,15 @@ pub fn get_address_transactions( address: FilterSet::from(vec![target_address]), topics: Default::default(), }; - + // Get logs matching our filter let logs = provider.get_logs(&filter)?; - kiprintln!("Found {} logs involving address {}", logs.len(), target_address); - + kiprintln!( + "Found {} logs involving address {}", + logs.len(), + target_address + ); + // Extract unique transaction hashes let mut tx_hashes = Vec::new(); for log in logs { @@ -1511,10 +1562,10 @@ pub fn get_address_transactions( } } } - + // Create transaction details objects for each tx hash let mut transactions = Vec::new(); - + for tx_hash in tx_hashes { // For each transaction, create a basic TransactionDetails object // with just the transaction hash and basic direction @@ -1522,7 +1573,9 @@ pub fn get_address_transactions( hash: tx_hash, from: EthAddress::default(), // We'll update this if we can get the transaction to: None, - value: EthAmount { wei_value: U256::ZERO }, + value: EthAmount { + wei_value: U256::ZERO, + }, block_number: None, timestamp: None, gas_used: None, @@ -1530,12 +1583,12 @@ pub fn get_address_transactions( success: None, direction: TransactionDirection::Incoming, // Default, will update if needed }; - + // Try to get transaction receipt for more details if let Ok(Some(receipt)) = provider.get_transaction_receipt(tx_hash) { // Update from receipt fields that we know exist tx_detail.block_number = receipt.block_number; - + // Get transaction success status if available let status = receipt.status(); if status { @@ -1543,42 +1596,42 @@ pub fn get_address_transactions( } else { tx_detail.success = Some(false); } - + // Try to get original transaction for more details if let Ok(Some(tx)) = provider.get_transaction_by_hash(tx_hash) { // Update from transaction fields tx_detail.from = tx.from; - + // Set direction based on compared addresses if tx.from == target_address { tx_detail.direction = TransactionDirection::Outgoing; } else { tx_detail.direction = TransactionDirection::Incoming; } - + // Try to get block timestamp if we have block number if let Some(block_num) = tx_detail.block_number { - if let Ok(Some(block)) = provider.get_block_by_number(BlockNumberOrTag::Number(block_num), false) { + if let Ok(Some(block)) = + provider.get_block_by_number(BlockNumberOrTag::Number(block_num), false) + { // Block header timestamp is a u64, not an Option tx_detail.timestamp = Some(block.header.timestamp); } } } } - + transactions.push(tx_detail); } - + // Sort by block number (descending) - transactions.sort_by(|a, b| { - match (b.block_number, a.block_number) { - (Some(b_num), Some(a_num)) => b_num.cmp(&a_num), - (Some(_), None) => std::cmp::Ordering::Less, - (None, Some(_)) => std::cmp::Ordering::Greater, - (None, None) => std::cmp::Ordering::Equal, - } + transactions.sort_by(|a, b| match (b.block_number, a.block_number) { + (Some(b_num), Some(a_num)) => b_num.cmp(&a_num), + (Some(_), None) => std::cmp::Ordering::Less, + (None, Some(_)) => std::cmp::Ordering::Greater, + (None, None) => std::cmp::Ordering::Equal, }); - + Ok(transactions) } @@ -1590,34 +1643,38 @@ pub fn format_transaction_details(tx: &TransactionDetails) -> String { TransactionDirection::Outgoing => "→", TransactionDirection::SelfTransfer => "↻", }; - + // Transaction status let status = match tx.success { Some(true) => "Succeeded", Some(false) => "Failed", None => "Unknown", }; - + // Format value let value = tx.value.to_string(); - + // Format timestamp without external dependencies let timestamp = match tx.timestamp { Some(ts) => format_timestamp(ts), None => "Pending".to_string(), }; - + // Format to and from addresses - let from_addr = format!("{:.8}...{}", + let from_addr = format!( + "{:.8}...{}", tx.from.to_string()[0..10].to_string(), - tx.from.to_string()[34..].to_string()); - - let to_addr = tx.to.map_or("Contract Creation".to_string(), |addr| - format!("{:.8}...{}", - addr.to_string()[0..10].to_string(), - addr.to_string()[34..].to_string()) + tx.from.to_string()[34..].to_string() ); - + + let to_addr = tx.to.map_or("Contract Creation".to_string(), |addr| { + format!( + "{:.8}...{}", + addr.to_string()[0..10].to_string(), + addr.to_string()[34..].to_string() + ) + }); + // Format final output format!( "TX: {} [{}]\n {} {} {}\n Block: {}, Status: {}, Value: {}, Time: {}", @@ -1626,7 +1683,8 @@ pub fn format_transaction_details(tx: &TransactionDetails) -> String { from_addr, direction_symbol, to_addr, - tx.block_number.map_or("Pending".to_string(), |b| b.to_string()), + tx.block_number + .map_or("Pending".to_string(), |b| b.to_string()), status, value, timestamp @@ -1636,22 +1694,22 @@ pub fn format_transaction_details(tx: &TransactionDetails) -> String { fn format_timestamp(timestamp: u64) -> String { // Simple timestamp formatting // We'll use a very basic approach that doesn't rely on date/time libraries - + // Convert to seconds, minutes, hours, days since epoch let secs = timestamp % 60; let mins = (timestamp / 60) % 60; let hours = (timestamp / 3600) % 24; let days_since_epoch = timestamp / 86400; - + // Very rough estimation - doesn't account for leap years properly let years_since_epoch = days_since_epoch / 365; let days_this_year = days_since_epoch % 365; - + // Rough month calculation let month_days = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]; let mut month = 0; let mut day = days_this_year as u32; - + // Adjust for leap years in a very rough way let is_leap_year = (1970 + years_since_epoch as u32) % 4 == 0; let month_days = if is_leap_year { @@ -1661,7 +1719,7 @@ fn format_timestamp(timestamp: u64) -> String { } else { month_days.to_vec() }; - + // Find month and day for (i, &days_in_month) in month_days.iter().enumerate() { if day < days_in_month { @@ -1670,11 +1728,11 @@ fn format_timestamp(timestamp: u64) -> String { } day -= days_in_month; } - + // Adjust to 1-based day += 1; month += 1; - + // Format the date format!( "{:04}-{:02}-{:02} {:02}:{:02}:{:02}", @@ -1704,25 +1762,25 @@ pub struct TokenDetails { pub fn get_token_details( token_address: &str, wallet_address: &str, - provider: &Provider + provider: &Provider, ) -> Result { // First resolve the token address (could be a symbol or address) let token = match resolve_token_symbol(token_address, provider.chain_id) { Ok(addr) => addr, Err(_) => resolve_name(token_address, provider.chain_id)?, }; - + // Get basic token information let token_str = token.to_string(); let symbol = erc20_symbol(token_address, provider)?; let name = erc20_name(token_address, provider)?; let decimals = erc20_decimals(token_address, provider)?; - + // Get total supply let total_supply = erc20_total_supply(token_address, provider)?; let total_supply_float = total_supply.to::() as f64 / 10f64.powi(decimals as i32); let formatted_total_supply = format!("{:.2}", total_supply_float); - + // Get balance if wallet address is provided let (balance, formatted_balance) = if !wallet_address.is_empty() { let balance = erc20_balance_of(token_address, wallet_address, provider)?; @@ -1730,7 +1788,7 @@ pub fn get_token_details( } else { ("0".to_string(), "0.000000".to_string()) }; - + Ok(TokenDetails { address: token_str, symbol, From 8159f2de76bc95c63c0ea1a716a4857fc0dbd1ee Mon Sep 17 00:00:00 2001 From: Hallmane Date: Tue, 8 Apr 2025 13:42:44 +0200 Subject: [PATCH 11/96] removed eth_utils --- src/eth_utils.rs | 298 ----------------------------------------------- 1 file changed, 298 deletions(-) delete mode 100644 src/eth_utils.rs diff --git a/src/eth_utils.rs b/src/eth_utils.rs deleted file mode 100644 index 9cc3f0e..0000000 --- a/src/eth_utils.rs +++ /dev/null @@ -1,298 +0,0 @@ -////! Higher-level Ethereum utilities for common operations in Hyperware. -////! -////! This module provides utility functions for common Ethereum operations in the Hyperware -////! ecosystem, particularly focusing on integrating with Hypermap for name resolution. -////! -////! The main goals of this module are: -////! 1. Provide simple, developer-friendly functions for common operations -////! 2. Abstract away the complexity of blockchain interactions -////! 3. Integrate with Hypermap for human-readable addressing -////! -////! ## Usage Examples -////! -////! ```rust -////! use hyperware_process_lib::eth_utils; -////! -////! // Send ETH to a Hypermap name -////! let tx_hash = eth_utils::send_eth("alice.hypr", 1.3)?; -////! -////! // Check if a wallet owns an NFT -////! let has_token = eth_utils::has_nft(contract_address, token_id, wallet_address)?; -////! -////! // Get a token balance -////! let balance = eth_utils::get_token_balance(token_address, wallet_address)?; -////! -////! // Send tokens to a Hypermap name -////! let tx_hash = eth_utils::send_token_to_name(token_address, "bob.hypr", amount)?; -////! ``` -// -//use crate::eth::{ -// Address, -// EthError, -// TxHash, -// U256 -//}; -//use crate::hypermap::{Hypermap, HYPERMAP_ADDRESS}; -//use crate::wallet::{Wallet, WalletError}; -//use std::str::FromStr; -//use thiserror::Error; -//use sha3::Digest; -// -///// Default chain ID to use for operations if not specified. -///// Currently set to Base (Coinbase L2). -//pub const DEFAULT_CHAIN_ID: u64 = crate::hypermap::HYPERMAP_CHAIN_ID; -// -///// Default timeout (in milliseconds) for Ethereum RPC operations. -//pub const DEFAULT_TIMEOUT_MS: u64 = 60_000; // 60 seconds -// -///// Errors that can occur in Ethereum utility operations -//#[derive(Debug, Error)] -//pub enum EthUtilsError { -// #[error("Ethereum RPC error: {0}")] -// Eth(#[from] EthError), -// -// #[error("Wallet error: {0}")] -// Wallet(#[from] WalletError), -// -// #[error("Name resolution error: {0}")] -// NameResolution(String), -// -// #[error("Transaction error: {0}")] -// Transaction(String), -//} -// -///// Send Ether to an address -///// -///// This function creates, signs, and sends a transaction to send ETH to an address. -///// -///// # Parameters -///// - `provider`: The Ethereum provider to use -///// - `wallet`: The wallet to send from -///// - `to`: The recipient address -///// - `amount_wei`: The amount to send in wei -///// - `gas_limit`: Optional gas limit (defaults to 21000) -///// - `gas_price`: Optional gas price (defaults to auto-estimation) -///// -///// # Returns -///// A `Result` representing the transaction hash if successful -///// -///// # Example -///// ```rust -///// use hyperware_process_lib::{eth_utils, wallet, eth}; -///// use alloy_primitives::{Address, U256}; -///// use std::str::FromStr; -///// -///// // Create wallet and provider -///// let wallet = wallet::Wallet::from_private_key( -///// "0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef", -///// 8453 // Base chain ID -///// )?; -///// let provider = eth::Provider::new(8453, 60000); -///// let to = Address::from_str("0x7E5F4552091A69125d5DfCb7b8C2659029395Bdf")?; -///// let amount = U256::from(1000000000000000000u64); // 1 ETH -///// -///// // Send ETH -///// let tx_hash = eth_utils::send_eth(&provider, &wallet, to, amount, None, None)?; -///// println!("Transaction hash: {}", tx_hash); -///// ``` -//pub fn send_eth( -// provider: &crate::eth::Provider, -// wallet: &Wallet, -// to: Address, -// amount_wei: U256, -// gas_limit: Option, -// gas_price: Option -//) -> Result { -// // Create RLP-encoded transaction -// let nonce = provider.get_transaction_count(wallet.address(), None)?; -// let nonce_u64 = u64::try_from(nonce).unwrap_or(0); -// -// // Get gas price if not provided -// let gas_price_value = if let Some(price) = gas_price { -// price -// } else { -// let current_gas_price = provider.get_gas_price()?; -// u128::try_from(current_gas_price).unwrap_or(20000000000) -// }; -// -// // Get gas limit -// let gas_limit_value = gas_limit.unwrap_or(21000); -// -// // Create and sign a transaction manually -// // First, construct the RLP-encoded transaction -// let mut rlp_data = Vec::new(); -// rlp_data.extend_from_slice(to.as_slice()); -// rlp_data.extend_from_slice(&amount_wei.to_be_bytes::<32>()); -// rlp_data.extend_from_slice(&nonce_u64.to_be_bytes()); -// rlp_data.extend_from_slice(&gas_limit_value.to_be_bytes()); -// rlp_data.extend_from_slice(&gas_price_value.to_be_bytes()); -// -// // Hash the transaction data with keccak256 -// let mut hasher = sha3::Keccak256::new(); -// hasher.update(&rlp_data); -// let tx_hash = hasher.finalize(); -// -// // Sign the transaction hash -// let signed_tx = wallet.sign_transaction_hash(&tx_hash)?; -// -// // Send raw transaction -// let tx_hash = provider.send_raw_transaction(signed_tx)?; -// -// Ok(tx_hash) -//} -// -///// Sends Ether to the owner of the specified Hypermap name. -///// -///// This function first resolves the name to its owner address using Hypermap, -///// then sends the specified amount of Ether to that address. -///// -///// # Parameters -///// - `provider`: The Ethereum provider to use -///// - `wallet`: The wallet to send from -///// - `name`: The Hypermap name (e.g., "alice.hypr") -///// - `amount_eth`: The amount of Ether to send (as a f64) -///// -///// # Returns -///// A `Result` representing the transaction hash if successful -///// -///// # Example -///// ```rust -///// use hyperware_process_lib::{eth_utils, wallet, eth}; -///// -///// // Create wallet and provider -///// let wallet = wallet::Wallet::from_private_key( -///// "0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef", -///// 8453 // Base chain ID -///// )?; -///// let provider = eth::Provider::new(8453, 60000); -///// -///// // Send 1.3 ETH to alice.hypr -///// let tx_hash = eth_utils::send_eth_to_name(&provider, &wallet, "alice.hypr", 1.3)?; -///// println!("Transaction hash: {}", tx_hash); -///// ``` -//pub fn send_eth_to_name( -// provider: &crate::eth::Provider, -// wallet: &Wallet, -// name: &str, -// amount_eth: f64 -//) -> Result { -// // Get Hypermap instance using our provider -// let hypermap = Hypermap::new(provider.clone(), -// Address::from_str(HYPERMAP_ADDRESS).unwrap()); -// -// // Format the name if needed (add .hypr if missing) -// let formatted_name = format_hypermap_name(name); -// -// // Resolve name to owner address -// let (_, owner, _) = hypermap.get(&formatted_name) -// .map_err(|e| EthUtilsError::NameResolution(format!("Failed to resolve name '{}': {}", formatted_name, e)))?; -// -// // Convert amount to wei (1 ETH = 10^18 wei) -// let amount_wei = (amount_eth * 1e18) as u128; -// let amount_in_wei = U256::from(amount_wei); -// -// // Send ETH to the resolved address -// send_eth(provider, wallet, owner, amount_in_wei, None, None) -//} -// -///// Format a name for Hypermap resolution -///// -///// If the name already contains a dot (.), it's returned as is. -///// Otherwise, ".hypr" is appended to the name. -///// -///// # Parameters -///// - `name`: The name to format -///// -///// # Returns -///// A formatted name suitable for Hypermap resolution -//fn format_hypermap_name(name: &str) -> String { -// // If name already has a domain extension, return as is -// if name.contains('.') { -// return name.to_string(); -// } -// -// // Otherwise, add the default .hypr extension -// format!("{}.hypr", name) -//} -// -///// Resolve a Hypermap name to its owner's Ethereum address -///// -///// # Parameters -///// - `name`: The Hypermap name to resolve -///// - `chain_id`: Optional chain ID to use (defaults to Base chain) -///// - `timeout_ms`: Optional timeout in milliseconds (defaults to 60 seconds) -///// -///// # Returns -///// A `Result` representing the owner's Ethereum address -///// -///// # Example -///// ```rust -///// use hyperware_process_lib::eth_utils; -///// -///// let owner = eth_utils::resolve_name("alice.hypr", None, None)?; -///// println!("Owner address: {}", owner); -///// ``` -//pub fn resolve_name( -// name: &str, -// chain_id: Option, -// timeout_ms: Option -//) -> Result { -// // Use provided chain ID or default -// let chain_id = chain_id.unwrap_or(DEFAULT_CHAIN_ID); -// let timeout = timeout_ms.unwrap_or(DEFAULT_TIMEOUT_MS); -// -// // Create provider -// let provider = crate::eth::Provider::new(chain_id, timeout); -// -// // Get Hypermap instance using our provider -// let hypermap = Hypermap::new(provider, -// Address::from_str(HYPERMAP_ADDRESS).unwrap()); -// -// // Format the name if needed (add .hypr if missing) -// let formatted_name = format_hypermap_name(name); -// -// // Resolve name to owner address -// let (_, owner, _) = hypermap.get(&formatted_name)?; -// -// Ok(owner) -//} -// -//#[cfg(test)] -//mod tests { -// use super::*; -// use alloy_primitives::{Address, U256}; -// use std::str::FromStr; -// -// #[test] -// fn test_format_hypermap_name() { -// // Test with name that already has dot -// let name_with_dot = "test.hypr"; -// assert_eq!(format_hypermap_name(name_with_dot), name_with_dot); -// -// // Test with name that doesn't have dot -// let name_without_dot = "test"; -// assert_eq!(format_hypermap_name(name_without_dot), "test.hypr"); -// } -// -// // Note: These tests would need real providers and wallets to run -// // We'll implement placeholders that describe what should be tested -// -// #[test] -// #[ignore] // Ignore this test since it requires network connectivity -// fn test_resolve_name() { -// // This would test name resolution with real provider and chain -// // let name = "test.hypr"; -// // let result = resolve_name(name, Some(DEFAULT_CHAIN_ID), Some(DEFAULT_TIMEOUT_MS)); -// // assert!(result.is_ok()); -// } -// -// #[test] -// #[ignore] // Ignore this test since it requires network connectivity -// fn test_send_eth_to_name() { -// // This would test ETH sending with real provider and wallet -// // let wallet = Wallet::new_random(DEFAULT_CHAIN_ID).unwrap(); -// // let provider = crate::eth::Provider::new(DEFAULT_CHAIN_ID, DEFAULT_TIMEOUT_MS); -// // let result = send_eth_to_name(&provider, &wallet, "test.hypr", 0.001); -// // assert!(result.is_ok()); -// } -//} From 98a012dd653bf4fbd197af0a462332f8c9a479b8 Mon Sep 17 00:00:00 2001 From: Hallmane Date: Tue, 8 Apr 2025 14:01:38 +0200 Subject: [PATCH 12/96] removed non-existing eth_utils dependency from lib.rs --- src/lib.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 031b6a2..b63fe18 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -24,8 +24,6 @@ wit_bindgen::generate!({ /// Interact with the eth provider module. pub mod eth; -/// High-level Ethereum utilities for common operations. -pub mod eth_utils; /// Your process must have the [`Capability`] to message /// `homepage:homepage:sys` to use this module. pub mod homepage; From 82815140f0a0c1deb87bd344893aa08b5950d0f7 Mon Sep 17 00:00:00 2001 From: Hallmane Date: Wed, 30 Apr 2025 15:46:21 +0200 Subject: [PATCH 13/96] proxy execute w tba wip --- src/eth.rs | 53 ++++++-- src/signer.rs | 2 +- src/wallet.rs | 371 ++++++++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 402 insertions(+), 24 deletions(-) diff --git a/src/eth.rs b/src/eth.rs index 321bb4d..a063c18 100644 --- a/src/eth.rs +++ b/src/eth.rs @@ -14,9 +14,6 @@ use std::collections::{HashMap, HashSet}; use std::error::Error; use std::fmt; -// TODO: remove this -use crate::kiprintln; - /// Subscription kind. Pulled directly from alloy (https://github.com/alloy-rs/alloy). /// Why? Because alloy is not yet 1.0 and the types in this interface must be stable. /// If alloy SubscriptionKind changes, we can implement a transition function in runtime @@ -338,9 +335,6 @@ impl Provider { .unwrap() .map_err(|_| EthError::RpcTimeout)?; - //TODO: remove - //kiprintln!("PROCESS_LIB::send_request_and_parse_response resp: {:#?}", resp); - match resp { Message::Response { body, .. } => match serde_json::from_slice::(&body) { Ok(EthResponse::Response(value)) => { @@ -705,8 +699,6 @@ impl Provider { // NOTE: tx must be encased by a tuple to be serialized correctly params: serde_json::to_value((tx,)).unwrap(), }; - //TODO: remove - kiprintln!("PROCESS_LIB::send_raw_transaction action: {:#?}", action); self.send_request_and_parse_response::(action) } @@ -764,21 +756,54 @@ impl Provider { print_verbosity_success: u8, print_verbosity_error: u8, ) { + let mut delay_secs = 5; // Initial delay + const MAX_DELAY_SECS: u64 = 60; // Maximum delay + loop { match self.subscribe(sub_id, filter.clone()) { - Ok(()) => break, - Err(_) => { + Ok(()) => break, // Success, exit loop + Err(e) => { // Log the actual error crate::print_to_terminal( print_verbosity_error, - "failed to subscribe to chain! trying again in 5s...", + &format!( + "Failed to subscribe to chain (sub_id {}): {:?}. Retrying in {}s...", + sub_id, e, delay_secs + ), ); - std::thread::sleep(std::time::Duration::from_secs(5)); - continue; + std::thread::sleep(std::time::Duration::from_secs(delay_secs)); + // Increase delay for next attempt, capped at maximum + delay_secs = (delay_secs * 2).min(MAX_DELAY_SECS); + continue; // Retry } } } - crate::print_to_terminal(print_verbosity_success, "subscribed to logs successfully"); + crate::print_to_terminal( + print_verbosity_success, + &format!("Subscribed successfully (sub_id {})", sub_id), + ); } + //pub fn subscribe_loop( + // &self, + // sub_id: u64, + // filter: Filter, + // print_verbosity_success: u8, + // print_verbosity_error: u8, + //) { + // loop { + // match self.subscribe(sub_id, filter.clone()) { + // Ok(()) => break, + // Err(_) => { + // crate::print_to_terminal( + // print_verbosity_error, + // "failed to subscribe to chain! trying again in 5s...", + // ); + // std::thread::sleep(std::time::Duration::from_secs(5)); + // continue; + // } + // } + // } + // crate::print_to_terminal(print_verbosity_success, "subscribed to logs successfully"); + //} /// Unsubscribes from a previously created subscription. /// diff --git a/src/signer.rs b/src/signer.rs index b906797..a09d86c 100644 --- a/src/signer.rs +++ b/src/signer.rs @@ -140,7 +140,7 @@ impl<'de> Deserialize<'de> for LocalSigner { { #[derive(Deserialize)] struct LocalSignerData { - address: EthAddress, + _address: EthAddress, chain_id: u64, private_key_hex: String, } diff --git a/src/wallet.rs b/src/wallet.rs index 0746347..dbe70b3 100644 --- a/src/wallet.rs +++ b/src/wallet.rs @@ -1,4 +1,4 @@ -//! ## (unfinished, unpolished and not fully tested) Ethereum wallet functionality for Hyperware. +//! ## (unfinished, unpolished and not fully tested) EVM wallet functionality for Hyperware. //! //! This module provides high-level wallet functionality for Ethereum, //! including transaction signing, contract interaction, and account management. @@ -11,15 +11,15 @@ use crate::eth::{BlockNumberOrTag, EthError, Provider}; use crate::hypermap; use crate::hypermap::{namehash, valid_fact, valid_name, valid_note}; -use crate::kiprintln; use crate::signer::{EncryptedSignerData, LocalSigner, Signer, SignerError, TransactionData}; +use crate::println as kiprintln; use alloy::rpc::types::{ - request::TransactionRequest, Block, BlockId, Filter, FilterBlockOption, FilterSet, Log, - Transaction, TransactionReceipt, + request::TransactionRequest, Filter, FilterBlockOption, FilterSet, + TransactionReceipt, }; use alloy_primitives::TxKind; -use alloy_primitives::{Address as EthAddress, Bytes, TxHash, U256}; +use alloy_primitives::{Address as EthAddress, Bytes, TxHash, U256, B256}; use alloy_sol_types::{sol, SolCall}; use serde::{Deserialize, Serialize}; use std::str::FromStr; @@ -58,7 +58,10 @@ sol! { } interface IERC6551Account { - function execute(address to, uint256 value, bytes calldata data, uint8 operation) external returns (bytes); + function execute(address to, uint256 value, bytes calldata data, uint8 operation) external payable returns (bytes memory); + function isValidSigner(address signer, bytes calldata data) external view returns (bytes4 magicValue); + function token() external view returns (uint256 chainId, address tokenContract, uint256 tokenId); + function setSignerDataKey(bytes32 signerDataKey) external; } } @@ -1487,14 +1490,364 @@ pub fn set_gene( ) } -// TODO: TEST -// -// // +// TOKEN BOUND ACCOUNT (ERC-6551) FUNCTIONS // + +/// Executes a call through a Token Bound Account (TBA) using a specific signer. +/// This function is designed for scenarios where the signer might be an authorized delegate +/// (e.g., via Hypermap notes like ~access-list) rather than the direct owner of the underlying NFT. +/// The TBA's own `execute` implementation is responsible for verifying the signer's authorization. +pub fn execute_via_tba_with_signer( + tba_address_or_name: &str, // Address or name of the TBA to execute through + hot_wallet_signer: &S, // The signer (e.g., hot wallet) authorized to call execute + target_address_or_name: &str, // Address or name of the contract to call via the TBA + call_data: Vec, // ABI-encoded data for the call to the target contract + value: U256, // ETH value to send with the call to the target contract + provider: &Provider, + operation: Option, // ERC-6551 operation type (0=CALL, 1=DELEGATECALL, etc.). Defaults to 0. + gas_limit: Option, // Optional gas limit override. Defaults to 500,000. +) -> Result { + // Resolve addresses + let tba = resolve_name(tba_address_or_name, provider.chain_id)?; + let target = resolve_name(target_address_or_name, provider.chain_id)?; + let op = operation.unwrap_or(0); // Default to CALL operation + + kiprintln!( + "PL:: Executing via TBA {} (signer: {}) -> Target {} (Op: {}, Value: {})", + tba, + hot_wallet_signer.address(), + target, + op, + value + ); + + + // Create the outer execute call directed at the TBA + let execute_call = IERC6551Account::executeCall { + to: target, + value, // This value is sent from the TBA to the target + data: Bytes::from(call_data), + operation: op, + }; + let execute_call_data = execute_call.abi_encode(); + + // Format receipt message + let format_receipt = move |_| { + format!( + "Execute via TBA {} to target {} (Signer: {})", + tba_address_or_name, target_address_or_name, hot_wallet_signer.address() + ) + }; + + // Prepare and send the transaction *to* the TBA, signed by the hot_wallet_signer. + // The `value` field in `prepare_and_send_tx` is U256::ZERO because the ETH transfer + // happens *inside* the TBA's execution context, funded by the TBA itself. + prepare_and_send_tx( + tba, // Transaction is sent TO the TBA address + execute_call_data, // Data is the ABI-encoded `execute` call + U256::ZERO, // Outer transaction sends no ETH directly to the TBA + provider, + hot_wallet_signer, // Signed by the provided (potentially delegated) signer + gas_limit.or(Some(500_000)), // Default gas limit for TBA executions + format_receipt, + ) +} + + +/// Executes a call through a Token Bound Account (TBA) +/// Assumes the provided signer is the owner/controller authorized by the standard ERC6551 implementation. +pub fn tba_execute( + tba_address: &str, + target_address: &str, + call_data: Vec, + value: U256, + operation: u8, // 0 for CALL, 1 for DELEGATECALL, etc. + provider: &Provider, + signer: &S, +) -> Result { + // Resolve addresses + let tba = resolve_name(tba_address, provider.chain_id)?; + let target = resolve_name(target_address, provider.chain_id)?; + + // Create the execute call + let execute_call = IERC6551Account::executeCall { + to: target, + value, + data: Bytes::from(call_data), + operation, + }; + + // Format receipt message + let format_receipt = move |_| { + format!( + "Executed call via TBA {} to target {}", + tba_address, target_address + ) + }; + + // Prepare and send the transaction - Use a higher default gas limit for TBA executions + prepare_and_send_tx( + tba, + execute_call.abi_encode(), + value, // Pass the value intended for the target call + provider, + signer, + Some(500_000), // Higher gas limit for potential complex TBA logic + target call + format_receipt, + ) +} + +/// Checks if an address is a valid signer for a given TBA. +/// This checks against the standard ERC-6551 `isValidSigner` which returns a magic value. +pub fn tba_is_valid_signer( + tba_address: &str, + signer_address: &str, + provider: &Provider, +) -> Result { + // Resolve addresses + let tba = resolve_name(tba_address, provider.chain_id)?; + let signer_addr = resolve_name(signer_address, provider.chain_id)?; + + // Create the isValidSigner call + let call = IERC6551Account::isValidSignerCall { + signer: signer_addr, + data: Bytes::default(), // No extra data needed for standard check + }; + let call_data = call.abi_encode(); + + // Perform the raw eth_call + let tx = TransactionRequest { + to: Some(TxKind::Call(tba)), + input: call_data.into(), + ..Default::default() + }; + let result_bytes = provider.call(tx, None)?; + + // Expected magic value for valid signer (IERC6551Account.isValidSigner.selector) + const ERC6551_IS_VALID_SIGNER: [u8; 4] = [0x52, 0x3e, 0x32, 0x60]; + + // Check if the returned bytes match the magic value + Ok(result_bytes.len() >= 4 && result_bytes[..4] == ERC6551_IS_VALID_SIGNER) +} + +/// Retrieves the token information (chainId, contract address, tokenId) associated with a TBA. +pub fn tba_get_token_info( + tba_address: &str, + provider: &Provider, +) -> Result<(u64, EthAddress, U256), WalletError> { + // Resolve TBA address + let tba = resolve_name(tba_address, provider.chain_id)?; + + // Create the token() call + let call = IERC6551Account::tokenCall {}; + + // Use the view function helper + let result = call_view_function(tba, call, provider)?; + + // Use named fields from the generated struct + let chain_id_u256 = result.chainId; + let token_contract = result.tokenContract; + let token_id = result.tokenId; + + // Convert chainId U256 to u64 (potential truncation, but unlikely for real chain IDs) + let chain_id = chain_id_u256.to::(); + + Ok((chain_id, token_contract, token_id)) +} + +/// Sets the signer data key for a HypermapSignerAccount TBA. +/// This links the TBA's alternative signer validation mechanism to a specific Hypermap note hash. +pub fn tba_set_signer_data_key( + tba_address: &str, + data_key_hash: B256, // Use B256 for bytes32 + provider: &Provider, + signer: &S, // Must be called by the current owner/controller of the TBA +) -> Result { + // Resolve TBA address + let tba = resolve_name(tba_address, provider.chain_id)?; + + // Create the setSignerDataKey call + let set_key_call = IERC6551Account::setSignerDataKeyCall { + signerDataKey: data_key_hash, + }; + + // Format receipt message + let format_receipt = move |_| { + format!( + "Set signer data key for TBA {} to hash {}", + tba_address, data_key_hash + ) + }; + + // Prepare and send the transaction + prepare_and_send_tx( + tba, + set_key_call.abi_encode(), + U256::ZERO, + provider, + signer, + Some(100_000), // Gas limit for a simple storage set + format_receipt, + ) +} + // +// HYPERMAP + TBA HELPER FUNCTIONS // +/// Creates a note (~allowed-signer) on a Hypermap entry containing an alternative signer's address, +/// and then configures the entry's TBA to use this note for alternative signature validation. +/// Requires the signature of the *owner* of the Hypermap entry. +pub fn setup_alternative_signer( + entry_name: &str, // e.g., "username.hypr" + alt_signer_address: EthAddress, // The address allowed to sign alternatively + provider: &Provider, + owner_signer: &S, // Signer holding the key that owns 'entry_name' +) -> Result { + // 1. Calculate the Hypermap entry hash + let entry_hash = hypermap::namehash(entry_name); + + // 2. Get the TBA associated with this entry and verify ownership + let hypermap_contract = provider.hypermap(); + let (tba, owner, _) = hypermap_contract.get_hash(&entry_hash)?; + + // 3. Check if the provided signer is the owner + if owner_signer.address() != owner { + return Err(WalletError::PermissionDenied(format!( + "Signer {} does not own the entry {}", + owner_signer.address(), + entry_name + ))); + } + + // 4. Define the note key for the allowed signer + let note_key = "~allowed-signer"; + + // 5. Create the note in Hypermap storing the alternative signer's address + // This requires a transaction signed by the owner_signer, executed via the parent TBA (owner's TBA) + kiprintln!( + "PL:: Creating note '{}' on '{}' with data {}", + note_key, + entry_name, + alt_signer_address + ); + let create_note_receipt = create_note( + entry_name, + note_key, + alt_signer_address.to_vec(), // Store address as bytes + provider.clone(), + owner_signer, + )?; + kiprintln!( + "PL:: Create note transaction sent: {}", + create_note_receipt.hash + ); + // Note: We might want to wait for this tx to confirm before proceeding + + // 6. Calculate the namehash of the specific note + // Combine note key and entry name, then hash + let note_full_name = format!("{}.{}", note_key, entry_name); + let note_hash_str = hypermap::namehash(¬e_full_name); + // Convert the String hash to B256 + let note_hash = B256::from_str(¬e_hash_str.trim_start_matches("0x")) + .map_err(|_| WalletError::TransactionError("Failed to parse note hash string".into()))?; + + kiprintln!( + "PL:: Calculated note hash for '{}': {}", + note_full_name, + note_hash + ); + + // 7. Set this note hash as the signer data key in the TBA + // This also requires a transaction signed by the owner_signer, sent directly to the TBA + kiprintln!( + "PL:: Setting signer data key on TBA {} to note hash {}", + tba, + note_hash + ); + let set_key_receipt = tba_set_signer_data_key( + &tba.to_string(), // Use the TBA address resolved earlier + note_hash, + provider, + owner_signer, // Owner signer makes this call directly to the TBA + )?; + kiprintln!( + "PL:: Set signer data key transaction sent: {}", + set_key_receipt.hash + ); + + // Return the receipt of the *second* transaction (setting the key) + // Ideally, we'd return both or confirm the first one succeeded. + Ok(TxReceipt { + hash: set_key_receipt.hash, + details: format!( + "Set up alternative signer {} for entry {} (Note Tx: {}, SetKey Tx: {})", + alt_signer_address, entry_name, create_note_receipt.hash, set_key_receipt.hash + ), + }) +} + +/// Retrieves the alternative signer address stored in the '~allowed-signer' note +/// associated with a given Hypermap entry. +pub fn get_alternative_signer( + entry_name: &str, + provider: &Provider, +) -> Result, WalletError> { + // 1. Calculate the Hypermap entry hash + let _entry_hash = hypermap::namehash(entry_name); + + // 2. Define the note key + let note_key = "~allowed-signer"; + + // 3. Calculate the specific note hash + let hypermap_contract = provider.hypermap(); + let note_full_name = format!("{}.{}", note_key, entry_name); + let note_hash = hypermap::namehash(¬e_full_name); + + // 4. Get the data stored at this specific note hash from Hypermap + match hypermap_contract.get_hash(¬e_hash) { + Ok((_tba, _owner, data_option)) => { + // Handle the Option + match data_option { + Some(data_bytes) => { + // 5. If data is present and exactly 20 bytes long, parse it as an address + if data_bytes.len() == 20 { + Ok(Some(EthAddress::from_slice(data_bytes.as_ref()))) + } else { + // Data exists but is the wrong length - use TransactionError + Err(WalletError::TransactionError(format!( + "Data at note hash {} for entry {} has incorrect length ({}) for an address", + note_hash, + entry_name, + data_bytes.len() + ))) + } + } + None => { + // Note exists (or get_hash succeeded) but has no data + Ok(None) + } + } + } + Err(EthError::RpcError(msg)) => { + // Convert potential JSON error message to string for checking + let msg_str = msg.to_string(); + if msg_str.contains("Execution reverted") || msg_str.contains("invalid opcode") { + // This likely means the note hash doesn't exist or isn't registered + Ok(None) + } else { + // Propagate other RPC errors + Err(WalletError::EthError(EthError::RpcError(msg))) + } + } + Err(e) => Err(WalletError::EthError(e)), // Propagate other errors + } +} + +// TODO: TEST +// ... existing test section ... + /// Transaction details in a more user-friendly format #[derive(Debug, Clone)] pub struct TransactionDetails { From a235fe88ef7f90fc12c6eebbcdc40ed2f7b82872 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 30 Apr 2025 13:46:48 +0000 Subject: [PATCH 14/96] Format Rust code using rustfmt --- src/eth.rs | 3 ++- src/wallet.rs | 35 +++++++++++++++++------------------ 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/src/eth.rs b/src/eth.rs index a063c18..6aaddd4 100644 --- a/src/eth.rs +++ b/src/eth.rs @@ -762,7 +762,8 @@ impl Provider { loop { match self.subscribe(sub_id, filter.clone()) { Ok(()) => break, // Success, exit loop - Err(e) => { // Log the actual error + Err(e) => { + // Log the actual error crate::print_to_terminal( print_verbosity_error, &format!( diff --git a/src/wallet.rs b/src/wallet.rs index dbe70b3..3757439 100644 --- a/src/wallet.rs +++ b/src/wallet.rs @@ -11,15 +11,14 @@ use crate::eth::{BlockNumberOrTag, EthError, Provider}; use crate::hypermap; use crate::hypermap::{namehash, valid_fact, valid_name, valid_note}; -use crate::signer::{EncryptedSignerData, LocalSigner, Signer, SignerError, TransactionData}; use crate::println as kiprintln; +use crate::signer::{EncryptedSignerData, LocalSigner, Signer, SignerError, TransactionData}; use alloy::rpc::types::{ - request::TransactionRequest, Filter, FilterBlockOption, FilterSet, - TransactionReceipt, + request::TransactionRequest, Filter, FilterBlockOption, FilterSet, TransactionReceipt, }; use alloy_primitives::TxKind; -use alloy_primitives::{Address as EthAddress, Bytes, TxHash, U256, B256}; +use alloy_primitives::{Address as EthAddress, Bytes, TxHash, B256, U256}; use alloy_sol_types::{sol, SolCall}; use serde::{Deserialize, Serialize}; use std::str::FromStr; @@ -1499,14 +1498,14 @@ pub fn set_gene( /// (e.g., via Hypermap notes like ~access-list) rather than the direct owner of the underlying NFT. /// The TBA's own `execute` implementation is responsible for verifying the signer's authorization. pub fn execute_via_tba_with_signer( - tba_address_or_name: &str, // Address or name of the TBA to execute through - hot_wallet_signer: &S, // The signer (e.g., hot wallet) authorized to call execute + tba_address_or_name: &str, // Address or name of the TBA to execute through + hot_wallet_signer: &S, // The signer (e.g., hot wallet) authorized to call execute target_address_or_name: &str, // Address or name of the contract to call via the TBA - call_data: Vec, // ABI-encoded data for the call to the target contract - value: U256, // ETH value to send with the call to the target contract + call_data: Vec, // ABI-encoded data for the call to the target contract + value: U256, // ETH value to send with the call to the target contract provider: &Provider, - operation: Option, // ERC-6551 operation type (0=CALL, 1=DELEGATECALL, etc.). Defaults to 0. - gas_limit: Option, // Optional gas limit override. Defaults to 500,000. + operation: Option, // ERC-6551 operation type (0=CALL, 1=DELEGATECALL, etc.). Defaults to 0. + gas_limit: Option, // Optional gas limit override. Defaults to 500,000. ) -> Result { // Resolve addresses let tba = resolve_name(tba_address_or_name, provider.chain_id)?; @@ -1522,7 +1521,6 @@ pub fn execute_via_tba_with_signer( value ); - // Create the outer execute call directed at the TBA let execute_call = IERC6551Account::executeCall { to: target, @@ -1536,7 +1534,9 @@ pub fn execute_via_tba_with_signer( let format_receipt = move |_| { format!( "Execute via TBA {} to target {} (Signer: {})", - tba_address_or_name, target_address_or_name, hot_wallet_signer.address() + tba_address_or_name, + target_address_or_name, + hot_wallet_signer.address() ) }; @@ -1544,17 +1544,16 @@ pub fn execute_via_tba_with_signer( // The `value` field in `prepare_and_send_tx` is U256::ZERO because the ETH transfer // happens *inside* the TBA's execution context, funded by the TBA itself. prepare_and_send_tx( - tba, // Transaction is sent TO the TBA address - execute_call_data, // Data is the ABI-encoded `execute` call - U256::ZERO, // Outer transaction sends no ETH directly to the TBA + tba, // Transaction is sent TO the TBA address + execute_call_data, // Data is the ABI-encoded `execute` call + U256::ZERO, // Outer transaction sends no ETH directly to the TBA provider, - hot_wallet_signer, // Signed by the provided (potentially delegated) signer + hot_wallet_signer, // Signed by the provided (potentially delegated) signer gas_limit.or(Some(500_000)), // Default gas limit for TBA executions format_receipt, ) } - /// Executes a call through a Token Bound Account (TBA) /// Assumes the provided signer is the owner/controller authorized by the standard ERC6551 implementation. pub fn tba_execute( @@ -1700,7 +1699,7 @@ pub fn tba_set_signer_data_key( /// and then configures the entry's TBA to use this note for alternative signature validation. /// Requires the signature of the *owner* of the Hypermap entry. pub fn setup_alternative_signer( - entry_name: &str, // e.g., "username.hypr" + entry_name: &str, // e.g., "username.hypr" alt_signer_address: EthAddress, // The address allowed to sign alternatively provider: &Provider, owner_signer: &S, // Signer holding the key that owns 'entry_name' From a237fadda032996d2ed377a7e505f819e2919928 Mon Sep 17 00:00:00 2001 From: Hallmane Date: Wed, 30 Apr 2025 16:42:25 +0200 Subject: [PATCH 15/96] access-list getter --- src/hypermap.rs | 50 +++++++++++++++++++++++++++++++++++++++++++++++++ src/wallet.rs | 49 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 99 insertions(+) diff --git a/src/hypermap.rs b/src/hypermap.rs index a7c3c29..b1db1c0 100644 --- a/src/hypermap.rs +++ b/src/hypermap.rs @@ -619,4 +619,54 @@ impl Hypermap { .collect::>(), ) } + + /// Gets the list of delegate addresses stored via the standard Hypermap access control pattern. + /// This involves reading the `~access-list` note for the given `entry_path` to find the + /// namehash of the permissions note, then reading that permissions note to get the + /// ABI-encoded `Vec
` of delegates. + /// + /// # Arguments + /// * `entry_path` - The base hypermap path (e.g., "myname.hypr") for which to find delegates. + /// + /// # Returns + /// A `Result, EthError>` containing the list of delegate addresses if found + /// and decoded successfully, or an error otherwise. + pub fn get_access_list_delegates(&self, entry_path: &str) -> Result, EthError> { + // 1. Construct the path to the ~access-list note + let access_list_path = format!("~access-list.{}", entry_path); + + // 2. Get the ~access-list note data (expecting a B256 namehash) + let (_tba, _owner, access_list_data_opt) = self.get(&access_list_path)?; + + let access_list_data = access_list_data_opt.ok_or_else(|| { + // Note not found or has no data - considered a malformed/unexpected response + EthError::RpcMalformedResponse + })?; + + // 3. Decode the data as the permissions note hash (B256) + // We expect the raw bytes stored in the note to be exactly 32 bytes. + if access_list_data.len() != 32 { + // Invalid data length - malformed response + return Err(EthError::RpcMalformedResponse); + } + let perms_note_hash = B256::from_slice(access_list_data.as_ref()); + let perms_note_hash_str = format!("0x{}", hex::encode(perms_note_hash)); + + // 4. Get the permissions note using the hash + let (_perms_tba, _perms_owner, perms_data_opt) = + self.get_hash(&perms_note_hash_str)?; + + let perms_data = perms_data_opt.ok_or_else(|| { + // Permissions note not found or has no data - malformed/unexpected response + EthError::RpcMalformedResponse + })?; + + // 5. Decode the permissions data as Vec
+ let delegates = Vec::
::abi_decode(&perms_data, true).map_err(|_e| { + // Failed to decode Vec
- malformed response + EthError::RpcMalformedResponse + })?; + + Ok(delegates) + } } diff --git a/src/wallet.rs b/src/wallet.rs index dbe70b3..2c2aef5 100644 --- a/src/wallet.rs +++ b/src/wallet.rs @@ -2152,3 +2152,52 @@ pub fn get_token_details( formatted_balance, }) } + +// +// CALLDATA CREATION HELPERS +// + +/// Creates the ABI-encoded calldata for an ERC20 `transfer` call. +/// +/// # Arguments +/// * `recipient` - The address to transfer tokens to. +/// * `amount` - The amount of tokens to transfer (in the token's smallest unit, e.g., wei for ETH-like). +/// +/// # Returns +/// A `Vec` containing the ABI-encoded calldata. +pub fn create_erc20_transfer_calldata( + recipient: EthAddress, + amount: U256, +) -> Vec { + let call = IERC20::transferCall { to: recipient, value: amount }; + call.abi_encode() +} + +/// Creates the ABI-encoded calldata for a Hypermap `note` call. +/// Performs validation on the note key format. +/// +/// # Arguments +/// * `note_key` - The note key (e.g., "~my-note"). Must start with '~'. +/// * `data` - The byte data to store in the note. +/// +/// # Returns +/// A `Result, WalletError>` containing the ABI-encoded calldata on success, +/// or a `WalletError::NameResolutionError` if the note key format is invalid. +pub fn create_hypermap_note_calldata( + note_key: &str, + data: Vec, +) -> Result, WalletError> { + // Validate the note key format + if !hypermap::valid_note(note_key) { + return Err(WalletError::NameResolutionError(format!( + "Invalid note key format: '{}'. Must start with '~' and follow naming rules.", + note_key + ))); + } + + let call = hypermap::contract::noteCall { + note: Bytes::from(note_key.as_bytes().to_vec()), + data: Bytes::from(data), + }; + Ok(call.abi_encode()) +} From 09d0e5681275ce9542f1e707245c808cb7d68070 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 30 Apr 2025 14:43:55 +0000 Subject: [PATCH 16/96] Format Rust code using rustfmt --- src/hypermap.rs | 3 +-- src/wallet.rs | 10 +++++----- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/hypermap.rs b/src/hypermap.rs index b1db1c0..e5391de 100644 --- a/src/hypermap.rs +++ b/src/hypermap.rs @@ -653,8 +653,7 @@ impl Hypermap { let perms_note_hash_str = format!("0x{}", hex::encode(perms_note_hash)); // 4. Get the permissions note using the hash - let (_perms_tba, _perms_owner, perms_data_opt) = - self.get_hash(&perms_note_hash_str)?; + let (_perms_tba, _perms_owner, perms_data_opt) = self.get_hash(&perms_note_hash_str)?; let perms_data = perms_data_opt.ok_or_else(|| { // Permissions note not found or has no data - malformed/unexpected response diff --git a/src/wallet.rs b/src/wallet.rs index 429b0fc..6c6ac57 100644 --- a/src/wallet.rs +++ b/src/wallet.rs @@ -2164,11 +2164,11 @@ pub fn get_token_details( /// /// # Returns /// A `Vec` containing the ABI-encoded calldata. -pub fn create_erc20_transfer_calldata( - recipient: EthAddress, - amount: U256, -) -> Vec { - let call = IERC20::transferCall { to: recipient, value: amount }; +pub fn create_erc20_transfer_calldata(recipient: EthAddress, amount: U256) -> Vec { + let call = IERC20::transferCall { + to: recipient, + value: amount, + }; call.abi_encode() } From 2393f6e34ee01c6c187d0816eb33a255d84772f1 Mon Sep 17 00:00:00 2001 From: Hallmane Date: Thu, 1 May 2025 16:15:52 +0200 Subject: [PATCH 17/96] added erc6551 5 call method w gas param --- src/wallet.rs | 37 +++++++++++++++++++++---------------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/src/wallet.rs b/src/wallet.rs index 6c6ac57..cab58fa 100644 --- a/src/wallet.rs +++ b/src/wallet.rs @@ -58,6 +58,7 @@ sol! { interface IERC6551Account { function execute(address to, uint256 value, bytes calldata data, uint8 operation) external payable returns (bytes memory); + function execute(address to, uint256 value, bytes calldata data, uint8 operation, uint256 txGas) external payable returns (bytes memory); function isValidSigner(address signer, bytes calldata data) external view returns (bytes4 magicValue); function token() external view returns (uint256 chainId, address tokenContract, uint256 tokenId); function setSignerDataKey(bytes32 signerDataKey) external; @@ -492,7 +493,7 @@ where } // Create the ERC-6551 execute call - let execute_call = IERC6551Account::executeCall { + let execute_call = IERC6551Account::execute_0Call { to: *hypermap.address(), value: U256::ZERO, data: hypermap_call_data, @@ -1498,14 +1499,13 @@ pub fn set_gene( /// (e.g., via Hypermap notes like ~access-list) rather than the direct owner of the underlying NFT. /// The TBA's own `execute` implementation is responsible for verifying the signer's authorization. pub fn execute_via_tba_with_signer( - tba_address_or_name: &str, // Address or name of the TBA to execute through - hot_wallet_signer: &S, // The signer (e.g., hot wallet) authorized to call execute - target_address_or_name: &str, // Address or name of the contract to call via the TBA - call_data: Vec, // ABI-encoded data for the call to the target contract - value: U256, // ETH value to send with the call to the target contract + tba_address_or_name: &str, + hot_wallet_signer: &S, + target_address_or_name: &str, + call_data: Vec, + value: U256, provider: &Provider, - operation: Option, // ERC-6551 operation type (0=CALL, 1=DELEGATECALL, etc.). Defaults to 0. - gas_limit: Option, // Optional gas limit override. Defaults to 500,000. + operation: Option, ) -> Result { // Resolve addresses let tba = resolve_name(tba_address_or_name, provider.chain_id)?; @@ -1521,35 +1521,40 @@ pub fn execute_via_tba_with_signer( value ); - // Create the outer execute call directed at the TBA - let execute_call = IERC6551Account::executeCall { + // Create the outer execute call (with txGas) directed at the TBA + // Use the _1 suffix for the second defined execute function (5 args) + let internal_gas_limit = U256::from(500_000); // Explicitly set gas for the internal call + let execute_call = IERC6551Account::execute_1Call { // <-- Using _1 suffix now to: target, value, // This value is sent from the TBA to the target data: Bytes::from(call_data), operation: op, + txGas: internal_gas_limit, // Provide gas for the internal call }; let execute_call_data = execute_call.abi_encode(); // Format receipt message let format_receipt = move |_| { format!( - "Execute via TBA {} to target {} (Signer: {})", + "Execute via TBA {} to target {} (Signer: {}, Internal Gas: {})", // Updated log tba_address_or_name, target_address_or_name, - hot_wallet_signer.address() + hot_wallet_signer.address(), + internal_gas_limit // Log internal gas ) }; // Prepare and send the transaction *to* the TBA, signed by the hot_wallet_signer. - // The `value` field in `prepare_and_send_tx` is U256::ZERO because the ETH transfer + // The `value` field in the *outer* transaction is U256::ZERO because the ETH transfer // happens *inside* the TBA's execution context, funded by the TBA itself. + // prepare_and_send_tx will handle gas estimation for the outer transaction. prepare_and_send_tx( tba, // Transaction is sent TO the TBA address - execute_call_data, // Data is the ABI-encoded `execute` call + execute_call_data, // Data is the ABI-encoded `execute` call with internal gas limit U256::ZERO, // Outer transaction sends no ETH directly to the TBA provider, hot_wallet_signer, // Signed by the provided (potentially delegated) signer - gas_limit.or(Some(500_000)), // Default gas limit for TBA executions + None, // Let prepare_and_send_tx estimate outer gas limit format_receipt, ) } @@ -1570,7 +1575,7 @@ pub fn tba_execute( let target = resolve_name(target_address, provider.chain_id)?; // Create the execute call - let execute_call = IERC6551Account::executeCall { + let execute_call = IERC6551Account::execute_0Call { to: target, value, data: Bytes::from(call_data), From 17627e14dc277b242d30187be4a1230fe58aeba9 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 1 May 2025 14:16:18 +0000 Subject: [PATCH 18/96] Format Rust code using rustfmt --- src/wallet.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/wallet.rs b/src/wallet.rs index cab58fa..36dd077 100644 --- a/src/wallet.rs +++ b/src/wallet.rs @@ -1524,7 +1524,8 @@ pub fn execute_via_tba_with_signer( // Create the outer execute call (with txGas) directed at the TBA // Use the _1 suffix for the second defined execute function (5 args) let internal_gas_limit = U256::from(500_000); // Explicitly set gas for the internal call - let execute_call = IERC6551Account::execute_1Call { // <-- Using _1 suffix now + let execute_call = IERC6551Account::execute_1Call { + // <-- Using _1 suffix now to: target, value, // This value is sent from the TBA to the target data: Bytes::from(call_data), From 8e90e36d8558368bf90d139c4d2c305429552c00 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 3 Jun 2025 11:03:50 +0000 Subject: [PATCH 19/96] Format Rust code using rustfmt --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index f3783a7..0b0c223 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -54,9 +54,9 @@ pub mod logging; /// Your process must have the [`Capability`] to message and receive messages from /// `net:distro:sys` to use this module. pub mod net; +pub mod sign; /// Low-level Ethereum signing operations and key management. pub mod signer; -pub mod sign; /// Interact with the sqlite module /// /// Your process must have the [`Capability] to message and receive messages from From cfb8d2bd3193b9766a022a758fa62198cfb249ee Mon Sep 17 00:00:00 2001 From: Hallmane Date: Wed, 9 Jul 2025 19:40:41 +0200 Subject: [PATCH 20/96] 4337 wip --- .DS_Store | Bin 6148 -> 6148 bytes src/signer.rs | 2 +- src/wallet.rs | 396 ++++++++++++++++++++++++++++++++++++++++++-------- 3 files changed, 336 insertions(+), 62 deletions(-) diff --git a/.DS_Store b/.DS_Store index e7c51e47273afbdaef187cca6147f1855bbd28d8..09bf23a6998f704b3768c0c09d9d572dd5aaa4dd 100644 GIT binary patch delta 299 zcmZoMXffEJ$`Yq!ew2ZMfrUYjA)O(Up(Hoo#U&{xKM5$t;nJbIsqyzQM^yO~yz&JZ zhQZ1CxdlKy3=F0ZCO5O}Wzwsiyo^I;vm#``b<>n(yf(z@(1b{R$ zGZZrvF(l`tBN@haUV8F7R Deserialize<'de> for LocalSigner { { #[derive(Deserialize)] struct LocalSignerData { - _address: EthAddress, + address: EthAddress, chain_id: u64, private_key_hex: String, } diff --git a/src/wallet.rs b/src/wallet.rs index 36dd077..0628dc6 100644 --- a/src/wallet.rs +++ b/src/wallet.rs @@ -23,45 +23,103 @@ use alloy_sol_types::{sol, SolCall}; use serde::{Deserialize, Serialize}; use std::str::FromStr; use thiserror::Error; + +// Define UserOperation struct for ERC-4337 sol! { - interface IERC20 { - function balanceOf(address who) external view returns (uint256); - function transfer(address to, uint256 value) external returns (bool); - function approve(address spender, uint256 value) external returns (bool); - function transferFrom(address from, address to, uint256 value) external returns (bool); - function allowance(address owner, address spender) external view returns (uint256); - function totalSupply() external view returns (uint256); - function decimals() external view returns (uint8); - function symbol() external view returns (string); - function name() external view returns (string); + #[derive(Debug, Default, PartialEq, Eq)] + struct UserOperation { + address sender; + uint256 nonce; + bytes initCode; + bytes callData; + uint256 callGasLimit; + uint256 verificationGasLimit; + uint256 preVerificationGas; + uint256 maxFeePerGas; + uint256 maxPriorityFeePerGas; + bytes paymasterAndData; + bytes signature; } +} - interface IERC721 { - function balanceOf(address owner) external view returns (uint256); - function ownerOf(uint256 tokenId) external view returns (address); - function safeTransferFrom(address from, address to, uint256 tokenId) external; - function transferFrom(address from, address to, uint256 tokenId) external; - function approve(address to, uint256 tokenId) external; - function setApprovalForAll(address operator, bool approved) external; - function getApproved(uint256 tokenId) external view returns (address); - function isApprovedForAll(address owner, address operator) external view returns (bool); - } +// Define contract interfaces +pub mod contracts { + use alloy_sol_types::sol; + + sol! { + interface IERC20 { + function balanceOf(address who) external view returns (uint256); + function decimals() external view returns (uint8); + function symbol() external view returns (string); + function name() external view returns (string); + function totalSupply() external view returns (uint256); + function allowance(address owner, address spender) external view returns (uint256); + function transfer(address to, uint256 value) external returns (bool); + function approve(address spender, uint256 value) external returns (bool); + function transferFrom(address from, address to, uint256 value) external returns (bool); + } - interface IERC1155 { - function balanceOf(address account, uint256 id) external view returns (uint256); - function balanceOfBatch(address[] accounts, uint256[] ids) external view returns (uint256[]); - function setApprovalForAll(address operator, bool approved) external; - function isApprovedForAll(address account, address operator) external view returns (bool); - function safeTransferFrom(address from, address to, uint256 id, uint256 amount, bytes data) external; - function safeBatchTransferFrom(address from, address to, uint256[] ids, uint256[] amounts, bytes data) external; - } + interface IERC721 { + function balanceOf(address owner) external view returns (uint256); + function ownerOf(uint256 tokenId) external view returns (address); + function isApprovedForAll(address owner, address operator) external view returns (bool); + function safeTransferFrom(address from, address to, uint256 tokenId) external; + function setApprovalForAll(address operator, bool approved) external; + } + + interface IERC1155 { + function balanceOf(address account, uint256 id) external view returns (uint256); + function balanceOfBatch(address[] calldata accounts, uint256[] calldata ids) external view returns (uint256[] memory); + function isApprovedForAll(address account, address operator) external view returns (bool); + function setApprovalForAll(address operator, bool approved) external; + function safeTransferFrom(address from, address to, uint256 id, uint256 amount, bytes calldata data) external; + function safeBatchTransferFrom(address from, address to, uint256[] calldata ids, uint256[] calldata amounts, bytes calldata data) external; + } - interface IERC6551Account { - function execute(address to, uint256 value, bytes calldata data, uint8 operation) external payable returns (bytes memory); - function execute(address to, uint256 value, bytes calldata data, uint8 operation, uint256 txGas) external payable returns (bytes memory); - function isValidSigner(address signer, bytes calldata data) external view returns (bytes4 magicValue); - function token() external view returns (uint256 chainId, address tokenContract, uint256 tokenId); - function setSignerDataKey(bytes32 signerDataKey) external; + interface IERC6551Account { + function execute(address to, uint256 value, bytes calldata data, uint8 operation) external payable returns (bytes memory); + function execute(address to, uint256 value, bytes calldata data, uint8 operation, uint256 txGas) external payable returns (bytes memory); + function isValidSigner(address signer, bytes calldata data) external view returns (bytes4 magicValue); + function token() external view returns (uint256 chainId, address tokenContract, uint256 tokenId); + function setSignerDataKey(bytes32 signerDataKey) external; + } + + // ERC-4337 EntryPoint Interface + interface IEntryPoint { + function handleOps(bytes calldata packedOps, address payable beneficiary) external; + function getUserOpHash(bytes calldata packedUserOp) external view returns (bytes32); + function getNonce(address sender, uint192 key) external view returns (uint256); + } + + // ERC-4337 Account Interface + interface IAccount { + function validateUserOp( + bytes calldata packedUserOp, + bytes32 userOpHash, + uint256 missingAccountFunds + ) external returns (uint256 validationData); + } + + // ERC-4337 Paymaster Interface + interface IPaymaster { + enum PostOpMode { + opSucceeded, + opReverted, + postOpReverted + } + + function validatePaymasterUserOp( + bytes calldata packedUserOp, + bytes32 userOpHash, + uint256 maxCost + ) external returns (bytes memory context, uint256 validationData); + + function postOp( + PostOpMode mode, + bytes calldata context, + uint256 actualGasCost + ) external; + } } } @@ -493,7 +551,7 @@ where } // Create the ERC-6551 execute call - let execute_call = IERC6551Account::execute_0Call { + let execute_call = contracts::IERC6551Account::execute_0Call { to: *hypermap.address(), value: U256::ZERO, data: hypermap_call_data, @@ -787,7 +845,7 @@ pub fn erc20_balance_of( let owner = resolve_name(owner_address, provider.chain_id)?; - let call = IERC20::balanceOfCall { who: owner }; + let call = contracts::IERC20::balanceOfCall { who: owner }; let balance = call_view_function(token, call, provider)?; let decimals = erc20_decimals(token_address, provider)?; @@ -803,7 +861,7 @@ pub fn erc20_decimals(token_address: &str, provider: &Provider) -> Result resolve_name(token_address, provider.chain_id)?, }; - let call = IERC20::decimalsCall {}; + let call = contracts::IERC20::decimalsCall {}; let decimals = call_view_function(token, call, provider)?; Ok(decimals._0) } @@ -815,7 +873,7 @@ pub fn erc20_symbol(token_address: &str, provider: &Provider) -> Result resolve_name(token_address, provider.chain_id)?, }; - let call = IERC20::symbolCall {}; + let call = contracts::IERC20::symbolCall {}; let symbol = call_view_function(token, call, provider)?; Ok(symbol._0) } @@ -827,7 +885,7 @@ pub fn erc20_name(token_address: &str, provider: &Provider) -> Result resolve_name(token_address, provider.chain_id)?, }; - let call = IERC20::nameCall {}; + let call = contracts::IERC20::nameCall {}; let name = call_view_function(token, call, provider)?; Ok(name._0) } @@ -839,7 +897,7 @@ pub fn erc20_total_supply(token_address: &str, provider: &Provider) -> Result resolve_name(token_address, provider.chain_id)?, }; - let call = IERC20::totalSupplyCall {}; + let call = contracts::IERC20::totalSupplyCall {}; let total_supply = call_view_function(token, call, provider)?; Ok(total_supply._0) } @@ -859,7 +917,7 @@ pub fn erc20_allowance( let owner = resolve_name(owner_address, provider.chain_id)?; let spender = resolve_name(spender_address, provider.chain_id)?; - let call = IERC20::allowanceCall { owner, spender }; + let call = contracts::IERC20::allowanceCall { owner, spender }; let allowance = call_view_function(token, call, provider)?; Ok(allowance._0) } @@ -892,7 +950,7 @@ pub fn erc20_transfer( kiprintln!("PL:: Resolved recipient address: {}", to); // Create the call - let call = IERC20::transferCall { to, value: amount }; + let call = contracts::IERC20::transferCall { to, value: amount }; let call_data = call.abi_encode(); // Get token details for receipt formatting @@ -934,7 +992,7 @@ pub fn erc20_approve( let spender = resolve_name(spender_address, provider.chain_id)?; // Create the call - let call = IERC20::approveCall { + let call = contracts::IERC20::approveCall { spender, value: amount, }; @@ -981,7 +1039,7 @@ pub fn erc20_transfer_from( let to = resolve_name(to_address, provider.chain_id)?; // Create the call - let call = IERC20::transferFromCall { + let call = contracts::IERC20::transferFromCall { from, to, value: amount, @@ -1027,7 +1085,7 @@ pub fn erc721_balance_of( let token = resolve_name(token_address, provider.chain_id)?; let owner = resolve_name(owner_address, provider.chain_id)?; - let call = IERC721::balanceOfCall { owner }; + let call = contracts::IERC721::balanceOfCall { owner }; let balance = call_view_function(token, call, provider)?; Ok(balance._0) } @@ -1039,7 +1097,7 @@ pub fn erc721_owner_of( provider: &Provider, ) -> Result { let token = resolve_name(token_address, provider.chain_id)?; - let call = IERC721::ownerOfCall { tokenId: token_id }; + let call = contracts::IERC721::ownerOfCall { tokenId: token_id }; let owner = call_view_function(token, call, provider)?; Ok(owner._0) } @@ -1055,7 +1113,7 @@ pub fn erc721_is_approved_for_all( let owner = resolve_name(owner_address, provider.chain_id)?; let operator = resolve_name(operator_address, provider.chain_id)?; - let call = IERC721::isApprovedForAllCall { owner, operator }; + let call = contracts::IERC721::isApprovedForAllCall { owner, operator }; let is_approved = call_view_function(token, call, provider)?; Ok(is_approved._0) } @@ -1075,7 +1133,7 @@ pub fn erc721_safe_transfer_from( let to = resolve_name(to_address, provider.chain_id)?; // Create the call - let call = IERC721::safeTransferFromCall { + let call = contracts::IERC721::safeTransferFromCall { from, to, tokenId: token_id, @@ -1115,7 +1173,7 @@ pub fn erc721_set_approval_for_all( let operator = resolve_name(operator_address, provider.chain_id)?; // Create the call - let call = IERC721::setApprovalForAllCall { operator, approved }; + let call = contracts::IERC721::setApprovalForAllCall { operator, approved }; let call_data = call.abi_encode(); // Format receipt message @@ -1158,7 +1216,7 @@ pub fn erc1155_balance_of( let token = resolve_name(token_address, provider.chain_id)?; let account = resolve_name(account_address, provider.chain_id)?; - let call = IERC1155::balanceOfCall { + let call = contracts::IERC1155::balanceOfCall { account, id: token_id, }; @@ -1189,7 +1247,7 @@ pub fn erc1155_balance_of_batch( accounts.push(resolve_name(addr, provider.chain_id)?); } - let call = IERC1155::balanceOfBatchCall { + let call = contracts::IERC1155::balanceOfBatchCall { accounts, ids: token_ids, }; @@ -1208,7 +1266,7 @@ pub fn erc1155_is_approved_for_all( let account = resolve_name(account_address, provider.chain_id)?; let operator = resolve_name(operator_address, provider.chain_id)?; - let call = IERC1155::isApprovedForAllCall { account, operator }; + let call = contracts::IERC1155::isApprovedForAllCall { account, operator }; let is_approved = call_view_function(token, call, provider)?; Ok(is_approved._0) } @@ -1226,7 +1284,7 @@ pub fn erc1155_set_approval_for_all( let operator = resolve_name(operator_address, provider.chain_id)?; // Create the call - let call = IERC1155::setApprovalForAllCall { operator, approved }; + let call = contracts::IERC1155::setApprovalForAllCall { operator, approved }; let call_data = call.abi_encode(); // Format receipt message @@ -1272,7 +1330,7 @@ pub fn erc1155_safe_transfer_from( let to = resolve_name(to_address, provider.chain_id)?; // Create the call - let call = IERC1155::safeTransferFromCall { + let call = contracts::IERC1155::safeTransferFromCall { from, to, id: token_id, @@ -1325,7 +1383,7 @@ pub fn erc1155_safe_batch_transfer_from( let to = resolve_name(to_address, provider.chain_id)?; // Create the call - let call = IERC1155::safeBatchTransferFromCall { + let call = contracts::IERC1155::safeBatchTransferFromCall { from, to, ids: token_ids.clone(), @@ -1524,7 +1582,7 @@ pub fn execute_via_tba_with_signer( // Create the outer execute call (with txGas) directed at the TBA // Use the _1 suffix for the second defined execute function (5 args) let internal_gas_limit = U256::from(500_000); // Explicitly set gas for the internal call - let execute_call = IERC6551Account::execute_1Call { + let execute_call = contracts::IERC6551Account::execute_1Call { // <-- Using _1 suffix now to: target, value, // This value is sent from the TBA to the target @@ -1576,7 +1634,7 @@ pub fn tba_execute( let target = resolve_name(target_address, provider.chain_id)?; // Create the execute call - let execute_call = IERC6551Account::execute_0Call { + let execute_call = contracts::IERC6551Account::execute_0Call { to: target, value, data: Bytes::from(call_data), @@ -1615,7 +1673,7 @@ pub fn tba_is_valid_signer( let signer_addr = resolve_name(signer_address, provider.chain_id)?; // Create the isValidSigner call - let call = IERC6551Account::isValidSignerCall { + let call = contracts::IERC6551Account::isValidSignerCall { signer: signer_addr, data: Bytes::default(), // No extra data needed for standard check }; @@ -1645,7 +1703,7 @@ pub fn tba_get_token_info( let tba = resolve_name(tba_address, provider.chain_id)?; // Create the token() call - let call = IERC6551Account::tokenCall {}; + let call = contracts::IERC6551Account::tokenCall {}; // Use the view function helper let result = call_view_function(tba, call, provider)?; @@ -1673,7 +1731,7 @@ pub fn tba_set_signer_data_key( let tba = resolve_name(tba_address, provider.chain_id)?; // Create the setSignerDataKey call - let set_key_call = IERC6551Account::setSignerDataKeyCall { + let set_key_call = contracts::IERC6551Account::setSignerDataKeyCall { signerDataKey: data_key_hash, }; @@ -2171,7 +2229,7 @@ pub fn get_token_details( /// # Returns /// A `Vec` containing the ABI-encoded calldata. pub fn create_erc20_transfer_calldata(recipient: EthAddress, amount: U256) -> Vec { - let call = IERC20::transferCall { + let call = contracts::IERC20::transferCall { to: recipient, value: amount, }; @@ -2206,3 +2264,219 @@ pub fn create_hypermap_note_calldata( }; Ok(call.abi_encode()) } + + +/// UserOperation builder for ERC-4337 +#[derive(Debug, Clone)] +pub struct UserOperationBuilder { + pub sender: EthAddress, + pub nonce: U256, + pub init_code: Vec, + pub call_data: Vec, + pub call_gas_limit: U256, + pub verification_gas_limit: U256, + pub pre_verification_gas: U256, + pub max_fee_per_gas: U256, + pub max_priority_fee_per_gas: U256, + pub paymaster_and_data: Vec, + pub chain_id: u64, +} + +impl UserOperationBuilder { + /// Create a new UserOperation builder + pub fn new(sender: EthAddress, chain_id: u64) -> Self { + Self { + sender, + nonce: U256::ZERO, + init_code: vec![], + call_data: vec![], + call_gas_limit: U256::from(100_000), + verification_gas_limit: U256::from(150_000), + pre_verification_gas: U256::from(21_000), + max_fee_per_gas: U256::ZERO, + max_priority_fee_per_gas: U256::ZERO, + paymaster_and_data: vec![], + chain_id, + } + } + + /// Build and sign the UserOperation + pub fn build_and_sign( + self, + entry_point: EthAddress, + signer: &S, + ) -> Result { + // Create the UserOperation struct + let user_op = UserOperation { + sender: self.sender, + nonce: self.nonce, + initCode: Bytes::from(self.init_code.clone()), + callData: Bytes::from(self.call_data.clone()), + callGasLimit: self.call_gas_limit, + verificationGasLimit: self.verification_gas_limit, + preVerificationGas: self.pre_verification_gas, + maxFeePerGas: self.max_fee_per_gas, + maxPriorityFeePerGas: self.max_priority_fee_per_gas, + paymasterAndData: Bytes::from(self.paymaster_and_data.clone()), + signature: Bytes::default(), // Will be filled after signing + }; + + // Get the UserOp hash for signing + let user_op_hash = self.get_user_op_hash(&user_op, entry_point, self.chain_id); + + // Sign the hash + let signature = signer.sign_message(&user_op_hash)?; + + // Create final UserOp with signature + Ok(UserOperation { + sender: self.sender, + nonce: self.nonce, + initCode: Bytes::from(self.init_code), + callData: Bytes::from(self.call_data), + callGasLimit: self.call_gas_limit, + verificationGasLimit: self.verification_gas_limit, + preVerificationGas: self.pre_verification_gas, + maxFeePerGas: self.max_fee_per_gas, + maxPriorityFeePerGas: self.max_priority_fee_per_gas, + paymasterAndData: Bytes::from(self.paymaster_and_data), + signature: Bytes::from(signature), + }) + } + + /// Calculate the UserOp hash according to ERC-4337 spec + fn get_user_op_hash( + &self, + user_op: &UserOperation, + entry_point: EthAddress, + chain_id: u64, + ) -> Vec { + use sha3::{Digest, Keccak256}; + + // Pack the UserOp for hashing (without signature) + 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 + let mut hasher = Keccak256::new(); + hasher.update(user_op_hash); + hasher.update(entry_point.as_slice()); + hasher.update(&chain_id.to_be_bytes()); + + hasher.finalize().to_vec() + } + + /// Pack UserOp fields for hashing (ERC-4337 specification) + fn pack_user_op_for_hash(&self, user_op: &UserOperation) -> Vec { + use sha3::{Digest, Keccak256}; + + let mut packed = Vec::new(); + + // Pack all fields except signature + packed.extend_from_slice(user_op.sender.as_slice()); + packed.extend_from_slice(&user_op.nonce.to_be_bytes::<32>()); + + // For initCode and paymasterAndData, we hash them if non-empty + if !user_op.initCode.is_empty() { + let hash = Keccak256::digest(&user_op.initCode); + packed.extend_from_slice(&hash); + } else { + packed.extend_from_slice(&[0u8; 32]); + } + + if !user_op.callData.is_empty() { + let hash = Keccak256::digest(&user_op.callData); + packed.extend_from_slice(&hash); + } else { + packed.extend_from_slice(&[0u8; 32]); + } + + packed.extend_from_slice(&user_op.callGasLimit.to_be_bytes::<32>()); + packed.extend_from_slice(&user_op.verificationGasLimit.to_be_bytes::<32>()); + packed.extend_from_slice(&user_op.preVerificationGas.to_be_bytes::<32>()); + packed.extend_from_slice(&user_op.maxFeePerGas.to_be_bytes::<32>()); + packed.extend_from_slice(&user_op.maxPriorityFeePerGas.to_be_bytes::<32>()); + + if !user_op.paymasterAndData.is_empty() { + let hash = Keccak256::digest(&user_op.paymasterAndData); + packed.extend_from_slice(&hash); + } else { + packed.extend_from_slice(&[0u8; 32]); + } + + packed + } +} + +/// Helper to create calldata for TBA execute through UserOp +pub fn create_tba_userop_calldata( + target: EthAddress, + value: U256, + data: Vec, + operation: u8, +) -> Vec { + // Use existing IERC6551Account interface + let call = contracts::IERC6551Account::execute_0Call { + to: target, + value, + data: Bytes::from(data), + operation, + }; + call.abi_encode() +} + + + +/// Get the ERC-4337 EntryPoint address for a given chain +pub fn get_entry_point_address(chain_id: u64) -> Option { + match chain_id { + // v0.6.0 EntryPoint is deployed at the same address on most chains + 1 | 10 | 137 | 42161 | 8453 => { + EthAddress::from_str("0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789").ok() + } + // Sepolia testnet + 11155111 => { + EthAddress::from_str("0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789").ok() + } + _ => None, + } +} + +/// Known paymaster addresses by chain +pub fn get_known_paymaster(chain_id: u64) -> Option { + match chain_id { + 8453 => { // Base + // Circle's USDC paymaster on Base + EthAddress::from_str("0x0578cFB241215b77442a541325d6A4E6dFE700Ec").ok() + } + _ => None, + } +} + +/// Encode paymaster data for USDC payment +/// The format is: paymaster address (20 bytes) + paymaster-specific data +pub fn encode_usdc_paymaster_data( + paymaster: EthAddress, + token_address: EthAddress, + max_cost: U256, +) -> Vec { + // Start with paymaster address (20 bytes) + let mut data = Vec::new(); + data.extend_from_slice(paymaster.as_slice()); + + // Add paymaster-specific data + // For Circle's paymaster, this typically includes: + // 1. Token address (20 bytes) + // 2. Max token amount to pay (32 bytes) + // 3. Exchange rate data or validity period (varies by paymaster) + + // Token address + data.extend_from_slice(token_address.as_slice()); + + // Max cost in token units (32 bytes, big-endian) + data.extend_from_slice(&max_cost.to_be_bytes::<32>()); + + // Additional data would go here based on specific paymaster requirements + // For now, we'll leave it at the basic format + + data +} \ No newline at end of file From 49638cad1cc1972880578b5dfb32d382e71701ab Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 9 Jul 2025 17:41:10 +0000 Subject: [PATCH 21/96] Format Rust code using rustfmt --- src/wallet.rs | 64 ++++++++++++++++++++++++--------------------------- 1 file changed, 30 insertions(+), 34 deletions(-) diff --git a/src/wallet.rs b/src/wallet.rs index 0628dc6..e2aee9c 100644 --- a/src/wallet.rs +++ b/src/wallet.rs @@ -45,7 +45,7 @@ sol! { // Define contract interfaces pub mod contracts { use alloy_sol_types::sol; - + sol! { interface IERC20 { function balanceOf(address who) external view returns (uint256); @@ -90,7 +90,7 @@ pub mod contracts { function getUserOpHash(bytes calldata packedUserOp) external view returns (bytes32); function getNonce(address sender, uint192 key) external view returns (uint256); } - + // ERC-4337 Account Interface interface IAccount { function validateUserOp( @@ -99,7 +99,7 @@ pub mod contracts { uint256 missingAccountFunds ) external returns (uint256 validationData); } - + // ERC-4337 Paymaster Interface interface IPaymaster { enum PostOpMode { @@ -107,13 +107,13 @@ pub mod contracts { opReverted, postOpReverted } - + function validatePaymasterUserOp( bytes calldata packedUserOp, bytes32 userOpHash, uint256 maxCost ) external returns (bytes memory context, uint256 validationData); - + function postOp( PostOpMode mode, bytes calldata context, @@ -2265,7 +2265,6 @@ pub fn create_hypermap_note_calldata( Ok(call.abi_encode()) } - /// UserOperation builder for ERC-4337 #[derive(Debug, Clone)] pub struct UserOperationBuilder { @@ -2299,7 +2298,7 @@ impl UserOperationBuilder { chain_id, } } - + /// Build and sign the UserOperation pub fn build_and_sign( self, @@ -2320,13 +2319,13 @@ impl UserOperationBuilder { paymasterAndData: Bytes::from(self.paymaster_and_data.clone()), signature: Bytes::default(), // Will be filled after signing }; - + // Get the UserOp hash for signing let user_op_hash = self.get_user_op_hash(&user_op, entry_point, self.chain_id); - + // Sign the hash let signature = signer.sign_message(&user_op_hash)?; - + // Create final UserOp with signature Ok(UserOperation { sender: self.sender, @@ -2342,7 +2341,7 @@ impl UserOperationBuilder { signature: Bytes::from(signature), }) } - + /// Calculate the UserOp hash according to ERC-4337 spec fn get_user_op_hash( &self, @@ -2351,30 +2350,30 @@ impl UserOperationBuilder { chain_id: u64, ) -> Vec { use sha3::{Digest, Keccak256}; - + // Pack the UserOp for hashing (without signature) 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 let mut hasher = Keccak256::new(); hasher.update(user_op_hash); hasher.update(entry_point.as_slice()); hasher.update(&chain_id.to_be_bytes()); - + hasher.finalize().to_vec() } - + /// Pack UserOp fields for hashing (ERC-4337 specification) fn pack_user_op_for_hash(&self, user_op: &UserOperation) -> Vec { use sha3::{Digest, Keccak256}; - + let mut packed = Vec::new(); - + // Pack all fields except signature packed.extend_from_slice(user_op.sender.as_slice()); packed.extend_from_slice(&user_op.nonce.to_be_bytes::<32>()); - + // For initCode and paymasterAndData, we hash them if non-empty if !user_op.initCode.is_empty() { let hash = Keccak256::digest(&user_op.initCode); @@ -2382,27 +2381,27 @@ impl UserOperationBuilder { } else { packed.extend_from_slice(&[0u8; 32]); } - + if !user_op.callData.is_empty() { let hash = Keccak256::digest(&user_op.callData); packed.extend_from_slice(&hash); } else { packed.extend_from_slice(&[0u8; 32]); } - + packed.extend_from_slice(&user_op.callGasLimit.to_be_bytes::<32>()); packed.extend_from_slice(&user_op.verificationGasLimit.to_be_bytes::<32>()); packed.extend_from_slice(&user_op.preVerificationGas.to_be_bytes::<32>()); packed.extend_from_slice(&user_op.maxFeePerGas.to_be_bytes::<32>()); packed.extend_from_slice(&user_op.maxPriorityFeePerGas.to_be_bytes::<32>()); - + if !user_op.paymasterAndData.is_empty() { let hash = Keccak256::digest(&user_op.paymasterAndData); packed.extend_from_slice(&hash); } else { packed.extend_from_slice(&[0u8; 32]); } - + packed } } @@ -2424,8 +2423,6 @@ pub fn create_tba_userop_calldata( call.abi_encode() } - - /// Get the ERC-4337 EntryPoint address for a given chain pub fn get_entry_point_address(chain_id: u64) -> Option { match chain_id { @@ -2434,9 +2431,7 @@ pub fn get_entry_point_address(chain_id: u64) -> Option { EthAddress::from_str("0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789").ok() } // Sepolia testnet - 11155111 => { - EthAddress::from_str("0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789").ok() - } + 11155111 => EthAddress::from_str("0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789").ok(), _ => None, } } @@ -2444,7 +2439,8 @@ pub fn get_entry_point_address(chain_id: u64) -> Option { /// Known paymaster addresses by chain pub fn get_known_paymaster(chain_id: u64) -> Option { match chain_id { - 8453 => { // Base + 8453 => { + // Base // Circle's USDC paymaster on Base EthAddress::from_str("0x0578cFB241215b77442a541325d6A4E6dFE700Ec").ok() } @@ -2462,21 +2458,21 @@ pub fn encode_usdc_paymaster_data( // Start with paymaster address (20 bytes) let mut data = Vec::new(); data.extend_from_slice(paymaster.as_slice()); - + // Add paymaster-specific data // For Circle's paymaster, this typically includes: // 1. Token address (20 bytes) // 2. Max token amount to pay (32 bytes) // 3. Exchange rate data or validity period (varies by paymaster) - + // Token address data.extend_from_slice(token_address.as_slice()); - + // Max cost in token units (32 bytes, big-endian) data.extend_from_slice(&max_cost.to_be_bytes::<32>()); - + // Additional data would go here based on specific paymaster requirements // For now, we'll leave it at the basic format - + data -} \ No newline at end of file +} From c9edb5f5c608bc8ed334daa8ea99cbdba1dcf716 Mon Sep 17 00:00:00 2001 From: Hallmane Date: Thu, 10 Jul 2025 16:08:52 +0200 Subject: [PATCH 22/96] gas from ZERO to something --- src/wallet.rs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/wallet.rs b/src/wallet.rs index e2aee9c..22127f4 100644 --- a/src/wallet.rs +++ b/src/wallet.rs @@ -2282,19 +2282,20 @@ pub struct UserOperationBuilder { } impl UserOperationBuilder { - /// Create a new UserOperation builder + /// Create a new UserOperationBuilder with defaults pub fn new(sender: EthAddress, chain_id: u64) -> Self { Self { sender, nonce: U256::ZERO, - init_code: vec![], - call_data: vec![], + init_code: Vec::new(), + call_data: Vec::new(), call_gas_limit: U256::from(100_000), verification_gas_limit: U256::from(150_000), pre_verification_gas: U256::from(21_000), - max_fee_per_gas: U256::ZERO, - max_priority_fee_per_gas: U256::ZERO, - paymaster_and_data: vec![], + // Set reasonable gas prices for Base chain + max_fee_per_gas: U256::from(50_000_000), // 0.05 gwei + max_priority_fee_per_gas: U256::from(50_000_000), // 0.05 gwei + paymaster_and_data: Vec::new(), chain_id, } } From 08a6dd3294bbef00e07e99f232e9287bc642ab87 Mon Sep 17 00:00:00 2001 From: Hallmane Date: Thu, 10 Jul 2025 17:24:20 +0200 Subject: [PATCH 23/96] v0.8.0 entrypoint --- src/wallet.rs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/wallet.rs b/src/wallet.rs index 22127f4..30dbed1 100644 --- a/src/wallet.rs +++ b/src/wallet.rs @@ -2427,8 +2427,13 @@ pub fn create_tba_userop_calldata( /// Get the ERC-4337 EntryPoint address for a given chain pub fn get_entry_point_address(chain_id: u64) -> Option { match chain_id { - // v0.6.0 EntryPoint is deployed at the same address on most chains - 1 | 10 | 137 | 42161 | 8453 => { + // v0.8.0 EntryPoint is deployed at this address on Base and other chains + 8453 => { + // Base - use v0.8 EntryPoint + EthAddress::from_str("0x4337084D9E255Ff0702461CF8895CE9E3b5Ff108").ok() + } + // v0.6.0 EntryPoint for other chains (keeping for compatibility) + 1 | 10 | 137 | 42161 => { EthAddress::from_str("0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789").ok() } // Sepolia testnet From 38ed0794b69d954b01ec881b65d9e836d61469a5 Mon Sep 17 00:00:00 2001 From: Hallmane Date: Thu, 10 Jul 2025 17:42:12 +0200 Subject: [PATCH 24/96] v0.8.0 structs... --- src/wallet.rs | 156 ++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 126 insertions(+), 30 deletions(-) diff --git a/src/wallet.rs b/src/wallet.rs index 30dbed1..39b7b64 100644 --- a/src/wallet.rs +++ b/src/wallet.rs @@ -40,6 +40,20 @@ sol! { bytes paymasterAndData; bytes signature; } + + // v0.8 UserOperation struct with packed fields + #[derive(Debug, Default, PartialEq, Eq)] + struct PackedUserOperation { + address sender; + uint256 nonce; + bytes initCode; + bytes callData; + bytes32 accountGasLimits; // packed verificationGasLimit (16 bytes) and callGasLimit (16 bytes) + uint256 preVerificationGas; + bytes32 gasFees; // packed maxPriorityFeePerGas (16 bytes) and maxFeePerGas (16 bytes) + bytes paymasterAndData; + bytes signature; + } } // Define contract interfaces @@ -2305,42 +2319,20 @@ impl UserOperationBuilder { self, entry_point: EthAddress, signer: &S, - ) -> Result { - // Create the UserOperation struct - let user_op = UserOperation { - sender: self.sender, - nonce: self.nonce, - initCode: Bytes::from(self.init_code.clone()), - callData: Bytes::from(self.call_data.clone()), - callGasLimit: self.call_gas_limit, - verificationGasLimit: self.verification_gas_limit, - preVerificationGas: self.pre_verification_gas, - maxFeePerGas: self.max_fee_per_gas, - maxPriorityFeePerGas: self.max_priority_fee_per_gas, - paymasterAndData: Bytes::from(self.paymaster_and_data.clone()), - signature: Bytes::default(), // Will be filled after signing - }; + ) -> Result { + // Create the v0.8 PackedUserOperation struct + let mut packed_op = build_packed_user_operation(&self); // Get the UserOp hash for signing - let user_op_hash = self.get_user_op_hash(&user_op, entry_point, self.chain_id); + let user_op_hash = self.get_user_op_hash_v08(&packed_op, entry_point, self.chain_id); // Sign the hash let signature = signer.sign_message(&user_op_hash)?; - // Create final UserOp with signature - Ok(UserOperation { - sender: self.sender, - nonce: self.nonce, - initCode: Bytes::from(self.init_code), - callData: Bytes::from(self.call_data), - callGasLimit: self.call_gas_limit, - verificationGasLimit: self.verification_gas_limit, - preVerificationGas: self.pre_verification_gas, - maxFeePerGas: self.max_fee_per_gas, - maxPriorityFeePerGas: self.max_priority_fee_per_gas, - paymasterAndData: Bytes::from(self.paymaster_and_data), - signature: Bytes::from(signature), - }) + // Set the signature + packed_op.signature = Bytes::from(signature); + + Ok(packed_op) } /// Calculate the UserOp hash according to ERC-4337 spec @@ -2365,6 +2357,28 @@ impl UserOperationBuilder { hasher.finalize().to_vec() } + /// Calculate the UserOp hash for v0.8 according to ERC-4337 spec + fn get_user_op_hash_v08( + &self, + user_op: &PackedUserOperation, + entry_point: EthAddress, + chain_id: u64, + ) -> Vec { + use sha3::{Digest, Keccak256}; + + // Pack the UserOp for hashing (without signature) + let packed = self.pack_user_op_for_hash_v08(user_op); + let user_op_hash = Keccak256::digest(&packed); + + // Create the final hash with entry point and chain ID + let mut hasher = Keccak256::new(); + hasher.update(user_op_hash); + hasher.update(entry_point.as_slice()); + hasher.update(&chain_id.to_be_bytes()); + + hasher.finalize().to_vec() + } + /// Pack UserOp fields for hashing (ERC-4337 specification) fn pack_user_op_for_hash(&self, user_op: &UserOperation) -> Vec { use sha3::{Digest, Keccak256}; @@ -2405,6 +2419,46 @@ impl UserOperationBuilder { packed } + + /// Pack UserOp fields for hashing v0.8 (ERC-4337 specification) + fn pack_user_op_for_hash_v08(&self, user_op: &PackedUserOperation) -> Vec { + use sha3::{Digest, Keccak256}; + + let mut packed = Vec::new(); + + // Pack all fields except signature + packed.extend_from_slice(user_op.sender.as_slice()); + packed.extend_from_slice(&user_op.nonce.to_be_bytes::<32>()); + + // For initCode and paymasterAndData, we hash them if non-empty + if !user_op.initCode.is_empty() { + let hash = Keccak256::digest(&user_op.initCode); + packed.extend_from_slice(&hash); + } else { + packed.extend_from_slice(&[0u8; 32]); + } + + if !user_op.callData.is_empty() { + let hash = Keccak256::digest(&user_op.callData); + packed.extend_from_slice(&hash); + } else { + packed.extend_from_slice(&[0u8; 32]); + } + + // Pack the packed fields directly (accountGasLimits and gasFees) + packed.extend_from_slice(&user_op.accountGasLimits.0); + packed.extend_from_slice(&user_op.preVerificationGas.to_be_bytes::<32>()); + packed.extend_from_slice(&user_op.gasFees.0); + + if !user_op.paymasterAndData.is_empty() { + let hash = Keccak256::digest(&user_op.paymasterAndData); + packed.extend_from_slice(&hash); + } else { + packed.extend_from_slice(&[0u8; 32]); + } + + packed + } } /// Helper to create calldata for TBA execute through UserOp @@ -2424,6 +2478,48 @@ pub fn create_tba_userop_calldata( call.abi_encode() } +/// Pack two 16-byte values into a single bytes32 for v0.8 UserOperation +fn pack_gas_values(high: U256, low: U256) -> B256 { + let mut packed = [0u8; 32]; + // Take the lower 16 bytes of each value + let high_bytes = high.to_be_bytes::<32>(); + let low_bytes = low.to_be_bytes::<32>(); + + // Pack high value in first 16 bytes + packed[0..16].copy_from_slice(&high_bytes[16..32]); + // Pack low value in last 16 bytes + packed[16..32].copy_from_slice(&low_bytes[16..32]); + + B256::from(packed) +} + +/// Build a v0.8 PackedUserOperation from the builder values +pub fn build_packed_user_operation(builder: &UserOperationBuilder) -> PackedUserOperation { + // Pack gas limits: verificationGasLimit (high) and callGasLimit (low) + let account_gas_limits = pack_gas_values( + builder.verification_gas_limit, + builder.call_gas_limit + ); + + // Pack gas fees: maxPriorityFeePerGas (high) and maxFeePerGas (low) + let gas_fees = pack_gas_values( + builder.max_priority_fee_per_gas, + builder.max_fee_per_gas + ); + + PackedUserOperation { + sender: builder.sender, + nonce: builder.nonce, + initCode: Bytes::from(builder.init_code.clone()), + callData: Bytes::from(builder.call_data.clone()), + accountGasLimits: account_gas_limits, + preVerificationGas: builder.pre_verification_gas, + gasFees: gas_fees, + paymasterAndData: Bytes::from(builder.paymaster_and_data.clone()), + signature: Bytes::default(), + } +} + /// Get the ERC-4337 EntryPoint address for a given chain pub fn get_entry_point_address(chain_id: u64) -> Option { match chain_id { From ae41238d8141f66c200a780b214fd2acc13d2bab Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 10 Jul 2025 15:42:34 +0000 Subject: [PATCH 25/96] Format Rust code using rustfmt --- src/wallet.rs | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/src/wallet.rs b/src/wallet.rs index 39b7b64..5e561a9 100644 --- a/src/wallet.rs +++ b/src/wallet.rs @@ -40,7 +40,7 @@ sol! { bytes paymasterAndData; bytes signature; } - + // v0.8 UserOperation struct with packed fields #[derive(Debug, Default, PartialEq, Eq)] struct PackedUserOperation { @@ -2331,7 +2331,7 @@ impl UserOperationBuilder { // Set the signature packed_op.signature = Bytes::from(signature); - + Ok(packed_op) } @@ -2484,29 +2484,24 @@ fn pack_gas_values(high: U256, low: U256) -> B256 { // Take the lower 16 bytes of each value let high_bytes = high.to_be_bytes::<32>(); let low_bytes = low.to_be_bytes::<32>(); - + // Pack high value in first 16 bytes packed[0..16].copy_from_slice(&high_bytes[16..32]); // Pack low value in last 16 bytes packed[16..32].copy_from_slice(&low_bytes[16..32]); - + B256::from(packed) } /// Build a v0.8 PackedUserOperation from the builder values pub fn build_packed_user_operation(builder: &UserOperationBuilder) -> PackedUserOperation { // Pack gas limits: verificationGasLimit (high) and callGasLimit (low) - let account_gas_limits = pack_gas_values( - builder.verification_gas_limit, - builder.call_gas_limit - ); - + let account_gas_limits = + pack_gas_values(builder.verification_gas_limit, builder.call_gas_limit); + // Pack gas fees: maxPriorityFeePerGas (high) and maxFeePerGas (low) - let gas_fees = pack_gas_values( - builder.max_priority_fee_per_gas, - builder.max_fee_per_gas - ); - + let gas_fees = pack_gas_values(builder.max_priority_fee_per_gas, builder.max_fee_per_gas); + PackedUserOperation { sender: builder.sender, nonce: builder.nonce, From 9bb25e5f11c857c6128d9c0e04585651abb91d2c Mon Sep 17 00:00:00 2001 From: Hallmane Date: Thu, 10 Jul 2025 18:39:17 +0200 Subject: [PATCH 26/96] wip --- src/wallet.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/wallet.rs b/src/wallet.rs index 39b7b64..c496026 100644 --- a/src/wallet.rs +++ b/src/wallet.rs @@ -2307,8 +2307,8 @@ impl UserOperationBuilder { verification_gas_limit: U256::from(150_000), pre_verification_gas: U256::from(21_000), // Set reasonable gas prices for Base chain - max_fee_per_gas: U256::from(50_000_000), // 0.05 gwei - max_priority_fee_per_gas: U256::from(50_000_000), // 0.05 gwei + max_fee_per_gas: U256::from(1_000_000_000), // 1 gwei + max_priority_fee_per_gas: U256::from(1_000_000_000), // 1 gwei paymaster_and_data: Vec::new(), chain_id, } From ce717197f91995d9400bb43f28419825544b0b3a Mon Sep 17 00:00:00 2001 From: Hallmane Date: Thu, 10 Jul 2025 18:56:27 +0200 Subject: [PATCH 27/96] wup --- src/wallet.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/wallet.rs b/src/wallet.rs index 3f8889e..7a1c154 100644 --- a/src/wallet.rs +++ b/src/wallet.rs @@ -2303,9 +2303,9 @@ impl UserOperationBuilder { nonce: U256::ZERO, init_code: Vec::new(), call_data: Vec::new(), - call_gas_limit: U256::from(100_000), - verification_gas_limit: U256::from(150_000), - pre_verification_gas: U256::from(21_000), + call_gas_limit: U256::from(80_000), // Reduced from 100k + verification_gas_limit: U256::from(100_000), // Reduced from 150k + pre_verification_gas: U256::from(50_000), // Increased from 21k for L2 // Set reasonable gas prices for Base chain max_fee_per_gas: U256::from(1_000_000_000), // 1 gwei max_priority_fee_per_gas: U256::from(1_000_000_000), // 1 gwei From 12843be7a2982b38793399e6733f9184818bdef5 Mon Sep 17 00:00:00 2001 From: Hallmane Date: Thu, 10 Jul 2025 19:58:03 +0200 Subject: [PATCH 28/96] wip --- src/wallet.rs | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/src/wallet.rs b/src/wallet.rs index 7a1c154..35d9d9a 100644 --- a/src/wallet.rs +++ b/src/wallet.rs @@ -2557,19 +2557,14 @@ pub fn encode_usdc_paymaster_data( data.extend_from_slice(paymaster.as_slice()); // Add paymaster-specific data - // For Circle's paymaster, this typically includes: - // 1. Token address (20 bytes) - // 2. Max token amount to pay (32 bytes) - // 3. Exchange rate data or validity period (varies by paymaster) - - // Token address + // For Circle's paymaster on Base, we only include the token address + // The paymaster will calculate the gas cost internally + + // Token address (USDC) data.extend_from_slice(token_address.as_slice()); - - // Max cost in token units (32 bytes, big-endian) - data.extend_from_slice(&max_cost.to_be_bytes::<32>()); - - // Additional data would go here based on specific paymaster requirements - // For now, we'll leave it at the basic format + + // Note: Circle's paymaster may expect additional data, but based on + // the error we're getting, let's try with just the token address data } From aa42c9fcc0514fae774d56a2a6298e2e917ea896 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 10 Jul 2025 17:58:30 +0000 Subject: [PATCH 29/96] Format Rust code using rustfmt --- src/wallet.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/wallet.rs b/src/wallet.rs index 35d9d9a..38ae2b8 100644 --- a/src/wallet.rs +++ b/src/wallet.rs @@ -2559,10 +2559,10 @@ pub fn encode_usdc_paymaster_data( // Add paymaster-specific data // For Circle's paymaster on Base, we only include the token address // The paymaster will calculate the gas cost internally - + // Token address (USDC) data.extend_from_slice(token_address.as_slice()); - + // Note: Circle's paymaster may expect additional data, but based on // the error we're getting, let's try with just the token address From 4658f09f9403e2ea9411a6370fe966aff1f9331a Mon Sep 17 00:00:00 2001 From: Hallmane Date: Thu, 10 Jul 2025 21:48:19 +0200 Subject: [PATCH 30/96] wip --- src/wallet.rs | 33 +++++++++++++++++++++++++++------ 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/src/wallet.rs b/src/wallet.rs index 35d9d9a..3e15a2d 100644 --- a/src/wallet.rs +++ b/src/wallet.rs @@ -2556,15 +2556,36 @@ pub fn encode_usdc_paymaster_data( let mut data = Vec::new(); data.extend_from_slice(paymaster.as_slice()); - // Add paymaster-specific data - // For Circle's paymaster on Base, we only include the token address - // The paymaster will calculate the gas cost internally + // Add paymaster-specific data for Circle's TokenPaymaster v0.8 + // Format: encodePacked([uint8, address, uint256, bytes]) + // - uint8: mode (0 for permit mode) + // - address: USDC token address + // - uint256: permit amount + // - bytes: permit signature (empty for now, needs EIP-2612 implementation) + + // Mode byte (0 for permit mode) + data.push(0u8); // Token address (USDC) data.extend_from_slice(token_address.as_slice()); - // Note: Circle's paymaster may expect additional data, but based on - // the error we're getting, let's try with just the token address - + // Permit amount - use a reasonable amount for gas payment (10 USDC worth) + let permit_amount = U256::from(10_000_000u64); // 10 USDC (6 decimals) + data.extend_from_slice(&permit_amount.to_be_bytes::<32>()); + + // TODO: Implement EIP-2612 permit signature + // For now, we'll try with an empty signature to see if that changes the error + // In production, this would need to be a valid permit signature allowing + // the paymaster to spend USDC on behalf of the TBA + + // Note: The permit signature would typically be generated by signing: + // - owner: TBA address + // - spender: paymaster address + // - value: permit_amount + // - nonce: current permit nonce from USDC contract + // - deadline: future timestamp + + // For testing, let's try without the signature first to see if we get a different error + data } From 140f94b73b3ceb3912a647030875ef6d1500dcd6 Mon Sep 17 00:00:00 2001 From: Hallmane Date: Thu, 10 Jul 2025 21:51:21 +0200 Subject: [PATCH 31/96] blawpi --- src/wallet.rs | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/src/wallet.rs b/src/wallet.rs index f8f385a..3e15a2d 100644 --- a/src/wallet.rs +++ b/src/wallet.rs @@ -2556,7 +2556,6 @@ pub fn encode_usdc_paymaster_data( let mut data = Vec::new(); data.extend_from_slice(paymaster.as_slice()); -<<<<<<< HEAD // Add paymaster-specific data for Circle's TokenPaymaster v0.8 // Format: encodePacked([uint8, address, uint256, bytes]) // - uint8: mode (0 for permit mode) @@ -2588,17 +2587,5 @@ pub fn encode_usdc_paymaster_data( // For testing, let's try without the signature first to see if we get a different error -======= - // Add paymaster-specific data - // For Circle's paymaster on Base, we only include the token address - // The paymaster will calculate the gas cost internally - - // Token address (USDC) - data.extend_from_slice(token_address.as_slice()); - - // Note: Circle's paymaster may expect additional data, but based on - // the error we're getting, let's try with just the token address - ->>>>>>> aa42c9fcc0514fae774d56a2a6298e2e917ea896 data } From 9f445a5810644feb27d75dd46522ae7dce28de52 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 10 Jul 2025 19:51:58 +0000 Subject: [PATCH 32/96] Format Rust code using rustfmt --- src/wallet.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/wallet.rs b/src/wallet.rs index 3e15a2d..4e887d1 100644 --- a/src/wallet.rs +++ b/src/wallet.rs @@ -2562,30 +2562,30 @@ pub fn encode_usdc_paymaster_data( // - address: USDC token address // - uint256: permit amount // - bytes: permit signature (empty for now, needs EIP-2612 implementation) - + // Mode byte (0 for permit mode) data.push(0u8); - + // Token address (USDC) data.extend_from_slice(token_address.as_slice()); - + // Permit amount - use a reasonable amount for gas payment (10 USDC worth) let permit_amount = U256::from(10_000_000u64); // 10 USDC (6 decimals) data.extend_from_slice(&permit_amount.to_be_bytes::<32>()); - + // TODO: Implement EIP-2612 permit signature // For now, we'll try with an empty signature to see if that changes the error // In production, this would need to be a valid permit signature allowing // the paymaster to spend USDC on behalf of the TBA - + // Note: The permit signature would typically be generated by signing: // - owner: TBA address - // - spender: paymaster address + // - spender: paymaster address // - value: permit_amount // - nonce: current permit nonce from USDC contract // - deadline: future timestamp - + // For testing, let's try without the signature first to see if we get a different error - + data } From a4b85357cbd95307bb4b7e496ccf59b65128db85 Mon Sep 17 00:00:00 2001 From: Hallmane Date: Fri, 11 Jul 2025 14:31:26 +0200 Subject: [PATCH 33/96] wip 2612 permit data --- src/signer.rs | 20 ++++++ src/wallet.rs | 173 +++++++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 178 insertions(+), 15 deletions(-) diff --git a/src/signer.rs b/src/signer.rs index b906797..2b284c0 100644 --- a/src/signer.rs +++ b/src/signer.rs @@ -97,6 +97,9 @@ pub trait Signer { /// Sign a message following Ethereum's personal_sign format fn sign_message(&self, message: &[u8]) -> Result, SignerError>; + + /// Sign a raw hash without any prefix (for EIP-712 and similar) + fn sign_hash(&self, hash: &[u8]) -> Result, SignerError>; } /// Local signer implementation using a private key stored in memory @@ -369,6 +372,23 @@ impl Signer for LocalSigner { Err(e) => Err(SignerError::SigningError(e.to_string())), } } + + fn sign_hash(&self, hash: &[u8]) -> Result, SignerError> { + // Sign a raw hash without any prefix + if hash.len() != 32 { + return Err(SignerError::SigningError( + "Hash must be exactly 32 bytes".to_string() + )); + } + + let hash_bytes = B256::from_slice(hash); + + // Sign the hash directly + match self.inner.sign_hash_sync(&hash_bytes) { + Ok(signature) => Ok(signature.as_bytes().to_vec()), + Err(e) => Err(SignerError::SigningError(e.to_string())), + } + } } /// Encrypt a private key using a password diff --git a/src/wallet.rs b/src/wallet.rs index 3e15a2d..69dbe28 100644 --- a/src/wallet.rs +++ b/src/wallet.rs @@ -2545,13 +2545,113 @@ pub fn get_known_paymaster(chain_id: u64) -> Option { } } -/// Encode paymaster data for USDC payment +/// Structure for EIP-2612 permit data +#[derive(Debug, Clone)] +pub struct PermitData { + pub owner: EthAddress, + pub spender: EthAddress, + pub value: U256, + pub nonce: U256, + pub deadline: U256, +} + +/// Generate EIP-2612 permit signature for USDC +pub fn generate_eip2612_permit_signature( + permit_data: &PermitData, + token_address: EthAddress, + chain_id: u64, + signer: &S, +) -> Result, WalletError> { + use sha3::{Digest, Keccak256}; + + // EIP-712 Domain Separator + // keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)") + let domain_type_hash = Keccak256::digest( + b"EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)" + ); + + // USDC uses "USD Coin" and version "2" + let name_hash = Keccak256::digest(b"USD Coin"); + let version_hash = Keccak256::digest(b"2"); + + // Build domain separator + let mut domain_data = Vec::new(); + domain_data.extend_from_slice(&domain_type_hash); + domain_data.extend_from_slice(&name_hash); + domain_data.extend_from_slice(&version_hash); + domain_data.extend_from_slice(&chain_id.to_be_bytes::<32>()); + domain_data.extend_from_slice(token_address.as_slice()); + let domain_separator = Keccak256::digest(&domain_data); + + // Permit type hash + // keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)") + let permit_type_hash = Keccak256::digest( + b"Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)" + ); + + // Build permit struct hash + let mut permit_data_encoded = Vec::new(); + permit_data_encoded.extend_from_slice(&permit_type_hash); + permit_data_encoded.extend_from_slice(permit_data.owner.as_slice()); + permit_data_encoded.extend_from_slice(permit_data.spender.as_slice()); + permit_data_encoded.extend_from_slice(&permit_data.value.to_be_bytes::<32>()); + permit_data_encoded.extend_from_slice(&permit_data.nonce.to_be_bytes::<32>()); + permit_data_encoded.extend_from_slice(&permit_data.deadline.to_be_bytes::<32>()); + let permit_struct_hash = Keccak256::digest(&permit_data_encoded); + + // Build final message hash for EIP-712 + let mut message = Vec::new(); + message.push(0x19); + message.push(0x01); + message.extend_from_slice(&domain_separator); + message.extend_from_slice(&permit_struct_hash); + let message_hash = Keccak256::digest(&message); + + // Sign the hash (raw signature without prefix) + // We need to sign the raw hash, not use sign_message which adds prefix + // For now, we'll use sign_message and strip the prefix behavior + // TODO: Add raw hash signing to Signer trait + let signature = signer.sign_message(&message_hash)?; + + Ok(signature) +} + +/// Get the current permit nonce for an address from USDC contract +pub fn get_usdc_permit_nonce( + token_address: &str, + owner: EthAddress, + provider: &Provider, +) -> Result { + // USDC has a nonces(address) function + // Function selector: 0x7ecebe00 + let mut call_data = Vec::new(); + call_data.extend_from_slice(&hex::decode("7ecebe00").unwrap()); + call_data.extend_from_slice(&[0u8; 12]); // Pad address to 32 bytes + call_data.extend_from_slice(owner.as_slice()); + + let token = resolve_name(token_address, provider.chain_id)?; + + // Make the call + let result = provider.call(token, call_data, None)?; + + // Parse the result as U256 + if result.len() >= 32 { + Ok(U256::from_be_slice(&result[..32])) + } else { + Err(WalletError::TransactionError("Invalid nonce response".to_string())) + } +} + +/// Encode paymaster data for USDC payment with EIP-2612 permit /// The format is: paymaster address (20 bytes) + paymaster-specific data -pub fn encode_usdc_paymaster_data( +pub fn encode_usdc_paymaster_data_with_permit( paymaster: EthAddress, token_address: EthAddress, + tba_address: EthAddress, max_cost: U256, -) -> Vec { + provider: &Provider, + signer: &S, +) -> Result, WalletError> { // Start with paymaster address (20 bytes) let mut data = Vec::new(); data.extend_from_slice(paymaster.as_slice()); @@ -2561,7 +2661,7 @@ pub fn encode_usdc_paymaster_data( // - uint8: mode (0 for permit mode) // - address: USDC token address // - uint256: permit amount - // - bytes: permit signature (empty for now, needs EIP-2612 implementation) + // - bytes: permit signature // Mode byte (0 for permit mode) data.push(0u8); @@ -2573,19 +2673,62 @@ pub fn encode_usdc_paymaster_data( let permit_amount = U256::from(10_000_000u64); // 10 USDC (6 decimals) data.extend_from_slice(&permit_amount.to_be_bytes::<32>()); - // TODO: Implement EIP-2612 permit signature - // For now, we'll try with an empty signature to see if that changes the error - // In production, this would need to be a valid permit signature allowing - // the paymaster to spend USDC on behalf of the TBA + // Generate EIP-2612 permit signature + // Get current nonce from USDC contract + let nonce = get_usdc_permit_nonce(&token_address.to_string(), tba_address, provider)?; + + // Set deadline to 1 hour from now + let deadline = U256::from(std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .unwrap() + .as_secs() + 3600); + + let permit_data = PermitData { + owner: tba_address, + spender: paymaster, + value: permit_amount, + nonce, + deadline, + }; + + let permit_signature = generate_eip2612_permit_signature( + &permit_data, + token_address, + provider.chain_id, + signer + )?; + + // Append the permit signature + data.extend_from_slice(&permit_signature); + + Ok(data) +} + +/// Encode paymaster data for USDC payment (simplified version without permit for testing) +/// The format is: paymaster address (20 bytes) + paymaster-specific data +pub fn encode_usdc_paymaster_data( + paymaster: EthAddress, + token_address: EthAddress, + max_cost: U256, +) -> Vec { + // For now, return a simplified version that we can use for testing + // The real implementation should use encode_usdc_paymaster_data_with_permit + let mut data = Vec::new(); + data.extend_from_slice(paymaster.as_slice()); + + // Mode byte (0 for permit mode) + data.push(0u8); - // Note: The permit signature would typically be generated by signing: - // - owner: TBA address - // - spender: paymaster address - // - value: permit_amount - // - nonce: current permit nonce from USDC contract - // - deadline: future timestamp + // Token address (USDC) + data.extend_from_slice(token_address.as_slice()); + + // Permit amount + let permit_amount = U256::from(10_000_000u64); // 10 USDC + data.extend_from_slice(&permit_amount.to_be_bytes::<32>()); - // For testing, let's try without the signature first to see if we get a different error + // For testing, add a dummy 65-byte signature + // In production, this should be a real EIP-2612 permit signature + data.extend_from_slice(&[0u8; 65]); data } From 7fabf09f5157cc6a7d1bf53783d8fe04c3468bbb Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 11 Jul 2025 12:34:23 +0000 Subject: [PATCH 34/96] Format Rust code using rustfmt --- src/signer.rs | 10 +++---- src/wallet.rs | 73 ++++++++++++++++++++++++++------------------------- 2 files changed, 42 insertions(+), 41 deletions(-) diff --git a/src/signer.rs b/src/signer.rs index 2b284c0..3af90f4 100644 --- a/src/signer.rs +++ b/src/signer.rs @@ -97,7 +97,7 @@ pub trait Signer { /// Sign a message following Ethereum's personal_sign format fn sign_message(&self, message: &[u8]) -> Result, SignerError>; - + /// Sign a raw hash without any prefix (for EIP-712 and similar) fn sign_hash(&self, hash: &[u8]) -> Result, SignerError>; } @@ -372,17 +372,17 @@ impl Signer for LocalSigner { Err(e) => Err(SignerError::SigningError(e.to_string())), } } - + fn sign_hash(&self, hash: &[u8]) -> Result, SignerError> { // Sign a raw hash without any prefix if hash.len() != 32 { return Err(SignerError::SigningError( - "Hash must be exactly 32 bytes".to_string() + "Hash must be exactly 32 bytes".to_string(), )); } - + let hash_bytes = B256::from_slice(hash); - + // Sign the hash directly match self.inner.sign_hash_sync(&hash_bytes) { Ok(signature) => Ok(signature.as_bytes().to_vec()), diff --git a/src/wallet.rs b/src/wallet.rs index e5f1958..df2f46c 100644 --- a/src/wallet.rs +++ b/src/wallet.rs @@ -2563,17 +2563,17 @@ pub fn generate_eip2612_permit_signature( signer: &S, ) -> Result, WalletError> { use sha3::{Digest, Keccak256}; - + // EIP-712 Domain Separator // keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)") let domain_type_hash = Keccak256::digest( - b"EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)" + b"EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)", ); - + // USDC uses "USD Coin" and version "2" let name_hash = Keccak256::digest(b"USD Coin"); let version_hash = Keccak256::digest(b"2"); - + // Build domain separator let mut domain_data = Vec::new(); domain_data.extend_from_slice(&domain_type_hash); @@ -2582,13 +2582,13 @@ pub fn generate_eip2612_permit_signature( domain_data.extend_from_slice(&chain_id.to_be_bytes::<32>()); domain_data.extend_from_slice(token_address.as_slice()); let domain_separator = Keccak256::digest(&domain_data); - + // Permit type hash // keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)") let permit_type_hash = Keccak256::digest( - b"Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)" + b"Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)", ); - + // Build permit struct hash let mut permit_data_encoded = Vec::new(); permit_data_encoded.extend_from_slice(&permit_type_hash); @@ -2598,7 +2598,7 @@ pub fn generate_eip2612_permit_signature( permit_data_encoded.extend_from_slice(&permit_data.nonce.to_be_bytes::<32>()); permit_data_encoded.extend_from_slice(&permit_data.deadline.to_be_bytes::<32>()); let permit_struct_hash = Keccak256::digest(&permit_data_encoded); - + // Build final message hash for EIP-712 let mut message = Vec::new(); message.push(0x19); @@ -2606,13 +2606,13 @@ pub fn generate_eip2612_permit_signature( message.extend_from_slice(&domain_separator); message.extend_from_slice(&permit_struct_hash); let message_hash = Keccak256::digest(&message); - + // Sign the hash (raw signature without prefix) // We need to sign the raw hash, not use sign_message which adds prefix // For now, we'll use sign_message and strip the prefix behavior // TODO: Add raw hash signing to Signer trait let signature = signer.sign_message(&message_hash)?; - + Ok(signature) } @@ -2628,17 +2628,19 @@ pub fn get_usdc_permit_nonce( call_data.extend_from_slice(&hex::decode("7ecebe00").unwrap()); call_data.extend_from_slice(&[0u8; 12]); // Pad address to 32 bytes call_data.extend_from_slice(owner.as_slice()); - + let token = resolve_name(token_address, provider.chain_id)?; - + // Make the call let result = provider.call(token, call_data, None)?; - + // Parse the result as U256 if result.len() >= 32 { Ok(U256::from_be_slice(&result[..32])) } else { - Err(WalletError::TransactionError("Invalid nonce response".to_string())) + Err(WalletError::TransactionError( + "Invalid nonce response".to_string(), + )) } } @@ -2662,7 +2664,7 @@ pub fn encode_usdc_paymaster_data_with_permit( // - address: USDC token address // - uint256: permit amount // - bytes: permit signature - + // Mode byte (0 for permit mode) data.push(0u8); @@ -2672,17 +2674,20 @@ pub fn encode_usdc_paymaster_data_with_permit( // Permit amount - use a reasonable amount for gas payment (10 USDC worth) let permit_amount = U256::from(10_000_000u64); // 10 USDC (6 decimals) data.extend_from_slice(&permit_amount.to_be_bytes::<32>()); - + // Generate EIP-2612 permit signature // Get current nonce from USDC contract let nonce = get_usdc_permit_nonce(&token_address.to_string(), tba_address, provider)?; - + // Set deadline to 1 hour from now - let deadline = U256::from(std::time::SystemTime::now() - .duration_since(std::time::UNIX_EPOCH) - .unwrap() - .as_secs() + 3600); - + let deadline = U256::from( + std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .unwrap() + .as_secs() + + 3600, + ); + let permit_data = PermitData { owner: tba_address, spender: paymaster, @@ -2690,17 +2695,13 @@ pub fn encode_usdc_paymaster_data_with_permit( nonce, deadline, }; - - let permit_signature = generate_eip2612_permit_signature( - &permit_data, - token_address, - provider.chain_id, - signer - )?; - + + let permit_signature = + generate_eip2612_permit_signature(&permit_data, token_address, provider.chain_id, signer)?; + // Append the permit signature data.extend_from_slice(&permit_signature); - + Ok(data) } @@ -2715,20 +2716,20 @@ pub fn encode_usdc_paymaster_data( // The real implementation should use encode_usdc_paymaster_data_with_permit let mut data = Vec::new(); data.extend_from_slice(paymaster.as_slice()); - + // Mode byte (0 for permit mode) data.push(0u8); - + // Token address (USDC) data.extend_from_slice(token_address.as_slice()); - + // Permit amount let permit_amount = U256::from(10_000_000u64); // 10 USDC data.extend_from_slice(&permit_amount.to_be_bytes::<32>()); - + // For testing, add a dummy 65-byte signature // In production, this should be a real EIP-2612 permit signature data.extend_from_slice(&[0u8; 65]); - + data } From 22e7e14998b743c848c04b4f8a1fbfc37b1c6050 Mon Sep 17 00:00:00 2001 From: Hallmane Date: Fri, 11 Jul 2025 14:44:29 +0200 Subject: [PATCH 35/96] wip --- src/wallet.rs | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/wallet.rs b/src/wallet.rs index e5f1958..2a70bfa 100644 --- a/src/wallet.rs +++ b/src/wallet.rs @@ -2579,7 +2579,7 @@ pub fn generate_eip2612_permit_signature( domain_data.extend_from_slice(&domain_type_hash); domain_data.extend_from_slice(&name_hash); domain_data.extend_from_slice(&version_hash); - domain_data.extend_from_slice(&chain_id.to_be_bytes::<32>()); + domain_data.extend_from_slice(&U256::from(chain_id).to_be_bytes::<32>()); domain_data.extend_from_slice(token_address.as_slice()); let domain_separator = Keccak256::digest(&domain_data); @@ -2631,8 +2631,15 @@ pub fn get_usdc_permit_nonce( let token = resolve_name(token_address, provider.chain_id)?; + // Create a transaction request for the call + let tx = TransactionRequest { + to: Some(TxKind::Call(token)), + input: call_data.into(), + ..Default::default() + }; + // Make the call - let result = provider.call(token, call_data, None)?; + let result = provider.call(tx, None)?; // Parse the result as U256 if result.len() >= 32 { @@ -2648,7 +2655,7 @@ pub fn encode_usdc_paymaster_data_with_permit( paymaster: EthAddress, token_address: EthAddress, tba_address: EthAddress, - max_cost: U256, + _max_cost: U256, provider: &Provider, signer: &S, ) -> Result, WalletError> { @@ -2709,7 +2716,7 @@ pub fn encode_usdc_paymaster_data_with_permit( pub fn encode_usdc_paymaster_data( paymaster: EthAddress, token_address: EthAddress, - max_cost: U256, + _max_cost: U256, ) -> Vec { // For now, return a simplified version that we can use for testing // The real implementation should use encode_usdc_paymaster_data_with_permit From b5833e3416ce7d6ae7000197dc12e6b23daba50d Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 11 Jul 2025 12:46:03 +0000 Subject: [PATCH 36/96] Format Rust code using rustfmt --- src/wallet.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/wallet.rs b/src/wallet.rs index 30c418a..566b877 100644 --- a/src/wallet.rs +++ b/src/wallet.rs @@ -2630,17 +2630,17 @@ pub fn get_usdc_permit_nonce( call_data.extend_from_slice(owner.as_slice()); let token = resolve_name(token_address, provider.chain_id)?; - + // Create a transaction request for the call let tx = TransactionRequest { to: Some(TxKind::Call(token)), input: call_data.into(), ..Default::default() }; - + // Make the call let result = provider.call(tx, None)?; - + // Parse the result as U256 if result.len() >= 32 { Ok(U256::from_be_slice(&result[..32])) From aa7ffcf71ebdab3923cbcf2e05a7afcb0d988e97 Mon Sep 17 00:00:00 2001 From: Hallmane Date: Fri, 11 Jul 2025 15:28:45 +0200 Subject: [PATCH 37/96] wip --- src/wallet.rs | 120 +++++++++++++++++++++++++++++++------------------- 1 file changed, 75 insertions(+), 45 deletions(-) diff --git a/src/wallet.rs b/src/wallet.rs index 566b877..a6f2a57 100644 --- a/src/wallet.rs +++ b/src/wallet.rs @@ -2459,6 +2459,32 @@ impl UserOperationBuilder { packed } + + /// Set paymaster and paymaster data with EIP-2612 permit signature + pub fn paymaster_with_permit( + mut self, + paymaster: EthAddress, + token_address: EthAddress, + max_cost: U256, + tba_address: EthAddress, + signer: &S, + provider: &Provider, + ) -> Result { + // Generate paymaster data with real permit signature + let paymaster_data = encode_usdc_paymaster_data_with_signer( + paymaster, + token_address, + max_cost, + tba_address, + signer, + provider, + )?; + + // Set the combined paymaster and data + self.paymaster_and_data = paymaster_data; + + Ok(self) + } } /// Helper to create calldata for TBA execute through UserOp @@ -2651,15 +2677,14 @@ pub fn get_usdc_permit_nonce( } } -/// Encode paymaster data for USDC payment with EIP-2612 permit -/// The format is: paymaster address (20 bytes) + paymaster-specific data -pub fn encode_usdc_paymaster_data_with_permit( +/// Encode paymaster data for USDC payment with EIP-2612 permit signature +pub fn encode_usdc_paymaster_data_with_signer( paymaster: EthAddress, token_address: EthAddress, + max_cost: U256, tba_address: EthAddress, - _max_cost: U256, - provider: &Provider, signer: &S, + provider: &Provider, ) -> Result, WalletError> { // Start with paymaster address (20 bytes) let mut data = Vec::new(); @@ -2671,72 +2696,77 @@ pub fn encode_usdc_paymaster_data_with_permit( // - address: USDC token address // - uint256: permit amount // - bytes: permit signature - + // Mode byte (0 for permit mode) data.push(0u8); - + // Token address (USDC) data.extend_from_slice(token_address.as_slice()); - - // Permit amount - use a reasonable amount for gas payment (10 USDC worth) - let permit_amount = U256::from(10_000_000u64); // 10 USDC (6 decimals) - data.extend_from_slice(&permit_amount.to_be_bytes::<32>()); - - // Generate EIP-2612 permit signature - // Get current nonce from USDC contract + + // Permit amount - use max_cost which should cover gas + data.extend_from_slice(&max_cost.to_be_bytes::<32>()); + + // Get current nonce for the TBA from USDC contract let nonce = get_usdc_permit_nonce(&token_address.to_string(), tba_address, provider)?; - - // Set deadline to 1 hour from now - let deadline = U256::from( - std::time::SystemTime::now() - .duration_since(std::time::UNIX_EPOCH) - .unwrap() - .as_secs() - + 3600, - ); - + + // Generate permit data + let deadline = U256::from(u64::MAX); // Max deadline let permit_data = PermitData { owner: tba_address, spender: paymaster, - value: permit_amount, + value: max_cost, nonce, deadline, }; - - let permit_signature = - generate_eip2612_permit_signature(&permit_data, token_address, provider.chain_id, signer)?; - - // Append the permit signature + + // Generate the actual permit signature + let chain_id = provider.chain_id; + let permit_signature = generate_eip2612_permit_signature( + &permit_data, + token_address, + chain_id, + signer, + )?; + + // Add the real permit signature data.extend_from_slice(&permit_signature); - + Ok(data) } -/// Encode paymaster data for USDC payment (simplified version without permit for testing) -/// The format is: paymaster address (20 bytes) + paymaster-specific data +/// Encode paymaster data for USDC payment (keeping old function for compatibility) +/// 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, + max_cost: U256, ) -> Vec { - // For now, return a simplified version that we can use for testing - // The real implementation should use encode_usdc_paymaster_data_with_permit + // Start with paymaster address (20 bytes) let mut data = Vec::new(); data.extend_from_slice(paymaster.as_slice()); + // Add paymaster-specific data for Circle's TokenPaymaster v0.8 + // Format: encodePacked([uint8, address, uint256, bytes]) + // - uint8: mode (0 for permit mode) + // - address: USDC token address + // - uint256: permit amount + // - bytes: permit signature (dummy for now) + // Mode byte (0 for permit mode) data.push(0u8); - + // Token address (USDC) data.extend_from_slice(token_address.as_slice()); - - // Permit amount - let permit_amount = U256::from(10_000_000u64); // 10 USDC + + // Permit amount - use a reasonable amount for gas payment + // 10 USDC should be more than enough for any transaction + let permit_amount = U256::from(10_000_000u64); // 10 USDC in 6 decimal units data.extend_from_slice(&permit_amount.to_be_bytes::<32>()); - - // For testing, add a dummy 65-byte signature - // In production, this should be a real EIP-2612 permit signature - data.extend_from_slice(&[0u8; 65]); - + + // Permit signature - DUMMY SIGNATURE + // In production, this needs to be a real EIP-2612 permit signature + let dummy_signature = vec![0u8; 65]; // r (32) + s (32) + v (1) + data.extend_from_slice(&dummy_signature); + data } From a3766efb54954c30ac16826eae07564499663a1a Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 11 Jul 2025 13:29:16 +0000 Subject: [PATCH 38/96] Format Rust code using rustfmt --- src/wallet.rs | 38 +++++++++++++++++--------------------- 1 file changed, 17 insertions(+), 21 deletions(-) diff --git a/src/wallet.rs b/src/wallet.rs index a6f2a57..c40d0ce 100644 --- a/src/wallet.rs +++ b/src/wallet.rs @@ -2479,10 +2479,10 @@ impl UserOperationBuilder { signer, provider, )?; - + // Set the combined paymaster and data self.paymaster_and_data = paymaster_data; - + Ok(self) } } @@ -2696,19 +2696,19 @@ pub fn encode_usdc_paymaster_data_with_signer( // - address: USDC token address // - uint256: permit amount // - bytes: permit signature - + // Mode byte (0 for permit mode) data.push(0u8); - + // Token address (USDC) data.extend_from_slice(token_address.as_slice()); - + // Permit amount - use max_cost which should cover gas data.extend_from_slice(&max_cost.to_be_bytes::<32>()); - + // Get current nonce for the TBA from USDC contract let nonce = get_usdc_permit_nonce(&token_address.to_string(), tba_address, provider)?; - + // Generate permit data let deadline = U256::from(u64::MAX); // Max deadline let permit_data = PermitData { @@ -2718,19 +2718,15 @@ pub fn encode_usdc_paymaster_data_with_signer( nonce, deadline, }; - + // Generate the actual permit signature let chain_id = provider.chain_id; - let permit_signature = generate_eip2612_permit_signature( - &permit_data, - token_address, - chain_id, - signer, - )?; - + let permit_signature = + generate_eip2612_permit_signature(&permit_data, token_address, chain_id, signer)?; + // Add the real permit signature data.extend_from_slice(&permit_signature); - + Ok(data) } @@ -2751,22 +2747,22 @@ pub fn encode_usdc_paymaster_data( // - address: USDC token address // - uint256: permit amount // - bytes: permit signature (dummy for now) - + // Mode byte (0 for permit mode) data.push(0u8); - + // Token address (USDC) data.extend_from_slice(token_address.as_slice()); - + // Permit amount - use a reasonable amount for gas payment // 10 USDC should be more than enough for any transaction let permit_amount = U256::from(10_000_000u64); // 10 USDC in 6 decimal units data.extend_from_slice(&permit_amount.to_be_bytes::<32>()); - + // Permit signature - DUMMY SIGNATURE // In production, this needs to be a real EIP-2612 permit signature let dummy_signature = vec![0u8; 65]; // r (32) + s (32) + v (1) data.extend_from_slice(&dummy_signature); - + data } From 5c2825f7106d5112406d482252f220464e679129 Mon Sep 17 00:00:00 2001 From: Hallmane Date: Fri, 11 Jul 2025 15:53:10 +0200 Subject: [PATCH 39/96] wip --- src/wallet.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/wallet.rs b/src/wallet.rs index c40d0ce..467432a 100644 --- a/src/wallet.rs +++ b/src/wallet.rs @@ -2462,14 +2462,14 @@ impl UserOperationBuilder { /// Set paymaster and paymaster data with EIP-2612 permit signature pub fn paymaster_with_permit( - mut self, + &mut self, paymaster: EthAddress, token_address: EthAddress, max_cost: U256, tba_address: EthAddress, signer: &S, provider: &Provider, - ) -> Result { + ) -> Result<(), WalletError> { // Generate paymaster data with real permit signature let paymaster_data = encode_usdc_paymaster_data_with_signer( paymaster, @@ -2479,11 +2479,11 @@ impl UserOperationBuilder { signer, provider, )?; - + // Set the combined paymaster and data self.paymaster_and_data = paymaster_data; - - Ok(self) + + Ok(()) } } From 0a7fd708cf5746ce3b14871790594f0f9b04e894 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 11 Jul 2025 13:53:38 +0000 Subject: [PATCH 40/96] Format Rust code using rustfmt --- src/wallet.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/wallet.rs b/src/wallet.rs index 467432a..bce8f05 100644 --- a/src/wallet.rs +++ b/src/wallet.rs @@ -2479,10 +2479,10 @@ impl UserOperationBuilder { signer, provider, )?; - + // Set the combined paymaster and data self.paymaster_and_data = paymaster_data; - + Ok(()) } } From 017d33224cb1964fd2782c500d0b6f161cc3e80c Mon Sep 17 00:00:00 2001 From: Hallmane Date: Fri, 11 Jul 2025 17:09:52 +0200 Subject: [PATCH 41/96] wip --- src/wallet.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/wallet.rs b/src/wallet.rs index 467432a..d4e0951 100644 --- a/src/wallet.rs +++ b/src/wallet.rs @@ -2634,10 +2634,8 @@ pub fn generate_eip2612_permit_signature( let message_hash = Keccak256::digest(&message); // Sign the hash (raw signature without prefix) - // We need to sign the raw hash, not use sign_message which adds prefix - // For now, we'll use sign_message and strip the prefix behavior - // TODO: Add raw hash signing to Signer trait - let signature = signer.sign_message(&message_hash)?; + // Use sign_hash for EIP-712, not sign_message which adds prefix + let signature = signer.sign_hash(&message_hash)?; Ok(signature) } @@ -2766,3 +2764,5 @@ pub fn encode_usdc_paymaster_data( data } + + From 68478523e01e19f135930bf45a0c270c8d4b5742 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 11 Jul 2025 15:10:33 +0000 Subject: [PATCH 42/96] Format Rust code using rustfmt --- src/wallet.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/wallet.rs b/src/wallet.rs index 369068d..6afe4cf 100644 --- a/src/wallet.rs +++ b/src/wallet.rs @@ -2764,5 +2764,3 @@ pub fn encode_usdc_paymaster_data( data } - - From 041c7bec59c34ce31d92eb2bb6bae38141f52ae0 Mon Sep 17 00:00:00 2001 From: Hallmane Date: Fri, 11 Jul 2025 17:21:23 +0200 Subject: [PATCH 43/96] wip --- src/wallet.rs | 105 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 105 insertions(+) diff --git a/src/wallet.rs b/src/wallet.rs index 6afe4cf..57facdc 100644 --- a/src/wallet.rs +++ b/src/wallet.rs @@ -2764,3 +2764,108 @@ pub fn encode_usdc_paymaster_data( data } + +/// Creates the ABI-encoded calldata for an ERC20 `permit` call. +/// This is used when the TBA needs to call permit on-chain (since TBAs can't sign off-chain) +/// +/// # Arguments +/// * `owner` - The address that owns the tokens (the TBA in this case) +/// * `spender` - The address to grant allowance to (e.g., the paymaster) +/// * `value` - The amount of tokens to approve +/// * `deadline` - The deadline timestamp for the permit +/// +/// # Returns +/// A `Vec` containing the ABI-encoded calldata for the permit function +pub fn create_erc20_permit_calldata( + owner: EthAddress, + spender: EthAddress, + value: U256, + deadline: U256, +) -> Vec { + // For on-chain permit calls by the TBA, we don't need nonce, v, r, s + // The TBA will call permit() directly as the owner + // This creates calldata for: permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) + // But for TBA on-chain calls, v=0, r=0, s=0 works because the TBA IS the owner + + use alloy_sol_types::SolCall; + + // Define the permit function call + // Note: Using the standard ERC20Permit interface + sol! { + function permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s); + } + + let call = permitCall { + owner, + spender, + value, + deadline, + v: 0, // Dummy values for on-chain call + r: B256::ZERO, + s: B256::ZERO, + }; + + call.abi_encode() +} + +/// 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, + permit_spender: EthAddress, + permit_amount: U256, + permit_deadline: U256, + execute_target: EthAddress, + execute_calldata: Vec, + execute_value: U256, +) -> Vec { + // Create permit calldata + let permit_calldata = create_erc20_permit_calldata( + EthAddress::ZERO, // Will be replaced by TBA address when executed + permit_spender, + permit_amount, + permit_deadline, + ); + + // For TBA execute, we need to create two execute calls: + // 1. Execute permit on token contract + // 2. Execute the actual operation + + // This would need to be wrapped in a multicall or batch execute + // The exact implementation depends on the TBA's interface + + // For now, return just the permit calldata + // In practice, this would be combined with the execute calldata + permit_calldata +} + +/// Encode paymaster data for USDC payment with on-chain permit (for TBAs) +/// This version doesn't include a permit signature since TBAs will call permit on-chain +pub fn encode_usdc_paymaster_data_for_tba( + paymaster: EthAddress, + token_address: EthAddress, + max_cost: U256, +) -> Vec { + // Start with paymaster address (20 bytes) + let mut data = Vec::new(); + data.extend_from_slice(paymaster.as_slice()); + + // Add paymaster-specific data for Circle's TokenPaymaster v0.8 + // For TBAs, we might need a different mode or the paymaster needs to support on-chain permits + + // Mode byte (1 for on-chain approval mode, if supported) + // Note: This depends on Circle's paymaster implementation + // Mode 0 = permit signature mode (for EOAs) + // Mode 1 = assume pre-existing approval (for smart contracts) + data.push(1u8); + + // Token address (USDC) + data.extend_from_slice(token_address.as_slice()); + + // Max cost amount + data.extend_from_slice(&max_cost.to_be_bytes::<32>()); + + // No signature needed for on-chain approval mode + + data +} From 921684b4c1e87cc14107095a6a2eb3f437967edf Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 11 Jul 2025 15:21:55 +0000 Subject: [PATCH 44/96] Format Rust code using rustfmt --- src/wallet.rs | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/wallet.rs b/src/wallet.rs index 57facdc..214d145 100644 --- a/src/wallet.rs +++ b/src/wallet.rs @@ -2786,25 +2786,25 @@ pub fn create_erc20_permit_calldata( // The TBA will call permit() directly as the owner // This creates calldata for: permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) // But for TBA on-chain calls, v=0, r=0, s=0 works because the TBA IS the owner - + use alloy_sol_types::SolCall; - + // Define the permit function call // Note: Using the standard ERC20Permit interface sol! { function permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s); } - + let call = permitCall { owner, spender, value, deadline, - v: 0, // Dummy values for on-chain call + v: 0, // Dummy values for on-chain call r: B256::ZERO, s: B256::ZERO, }; - + call.abi_encode() } @@ -2826,14 +2826,14 @@ pub fn create_multicall_permit_and_execute( permit_amount, permit_deadline, ); - + // For TBA execute, we need to create two execute calls: // 1. Execute permit on token contract // 2. Execute the actual operation - + // This would need to be wrapped in a multicall or batch execute // The exact implementation depends on the TBA's interface - + // For now, return just the permit calldata // In practice, this would be combined with the execute calldata permit_calldata @@ -2852,7 +2852,7 @@ pub fn encode_usdc_paymaster_data_for_tba( // Add paymaster-specific data for Circle's TokenPaymaster v0.8 // For TBAs, we might need a different mode or the paymaster needs to support on-chain permits - + // Mode byte (1 for on-chain approval mode, if supported) // Note: This depends on Circle's paymaster implementation // Mode 0 = permit signature mode (for EOAs) @@ -2866,6 +2866,6 @@ pub fn encode_usdc_paymaster_data_for_tba( data.extend_from_slice(&max_cost.to_be_bytes::<32>()); // No signature needed for on-chain approval mode - + data } From 333c7922b15278ca6ac92c729897d438e98eea22 Mon Sep 17 00:00:00 2001 From: Hallmane Date: Fri, 11 Jul 2025 18:18:32 +0200 Subject: [PATCH 45/96] wip --- src/wallet.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/wallet.rs b/src/wallet.rs index 214d145..9661cbb 100644 --- a/src/wallet.rs +++ b/src/wallet.rs @@ -20,6 +20,7 @@ use alloy::rpc::types::{ use alloy_primitives::TxKind; use alloy_primitives::{Address as EthAddress, Bytes, TxHash, B256, U256}; use alloy_sol_types::{sol, SolCall}; +use hex; use serde::{Deserialize, Serialize}; use std::str::FromStr; use thiserror::Error; @@ -2326,6 +2327,11 @@ impl UserOperationBuilder { // Get the UserOp hash for signing let user_op_hash = self.get_user_op_hash_v08(&packed_op, entry_point, self.chain_id); + // Log the hash before signing + kiprintln!("PL:: UserOperation hash to sign: 0x{}", hex::encode(&user_op_hash)); + kiprintln!("PL:: Entry point: {}", entry_point); + kiprintln!("PL:: Chain ID: {}", self.chain_id); + // Sign the hash let signature = signer.sign_message(&user_op_hash)?; From e6ba98608dafd2bff49c2b26bf0a09467637b7a2 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 11 Jul 2025 16:19:04 +0000 Subject: [PATCH 46/96] Format Rust code using rustfmt --- src/wallet.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/wallet.rs b/src/wallet.rs index 9661cbb..8cefbbe 100644 --- a/src/wallet.rs +++ b/src/wallet.rs @@ -2328,7 +2328,10 @@ impl UserOperationBuilder { let user_op_hash = self.get_user_op_hash_v08(&packed_op, entry_point, self.chain_id); // Log the hash before signing - kiprintln!("PL:: UserOperation hash to sign: 0x{}", hex::encode(&user_op_hash)); + kiprintln!( + "PL:: UserOperation hash to sign: 0x{}", + hex::encode(&user_op_hash) + ); kiprintln!("PL:: Entry point: {}", entry_point); kiprintln!("PL:: Chain ID: {}", self.chain_id); From ca26d85af572a7579840415de834de1f320c4655 Mon Sep 17 00:00:00 2001 From: Hallmane Date: Fri, 11 Jul 2025 18:51:12 +0200 Subject: [PATCH 47/96] wip --- src/wallet.rs | 46 +++++++++++++++++++++------------------------- 1 file changed, 21 insertions(+), 25 deletions(-) diff --git a/src/wallet.rs b/src/wallet.rs index 8cefbbe..89f83eb 100644 --- a/src/wallet.rs +++ b/src/wallet.rs @@ -2744,33 +2744,29 @@ pub fn encode_usdc_paymaster_data( token_address: EthAddress, max_cost: U256, ) -> Vec { - // Start with paymaster address (20 bytes) + // Use the new Circle format with default gas limits + encode_circle_paymaster_data(paymaster, 500_000, 300_000) +} + +/// Encode paymaster data for Circle's USDC paymaster +/// Format: abi.encodePacked(address paymaster, uint128 verificationGasLimit, uint128 callGasLimit) +pub fn encode_circle_paymaster_data( + paymaster: EthAddress, + verification_gas_limit: u128, + call_gas_limit: u128, +) -> Vec { let mut data = Vec::new(); + + // Paymaster address (20 bytes) data.extend_from_slice(paymaster.as_slice()); - - // Add paymaster-specific data for Circle's TokenPaymaster v0.8 - // Format: encodePacked([uint8, address, uint256, bytes]) - // - uint8: mode (0 for permit mode) - // - address: USDC token address - // - uint256: permit amount - // - bytes: permit signature (dummy for now) - - // Mode byte (0 for permit mode) - data.push(0u8); - - // Token address (USDC) - data.extend_from_slice(token_address.as_slice()); - - // Permit amount - use a reasonable amount for gas payment - // 10 USDC should be more than enough for any transaction - let permit_amount = U256::from(10_000_000u64); // 10 USDC in 6 decimal units - data.extend_from_slice(&permit_amount.to_be_bytes::<32>()); - - // Permit signature - DUMMY SIGNATURE - // In production, this needs to be a real EIP-2612 permit signature - let dummy_signature = vec![0u8; 65]; // r (32) + s (32) + v (1) - data.extend_from_slice(&dummy_signature); - + + // Verification gas limit as uint128 (16 bytes) + data.extend_from_slice(&verification_gas_limit.to_be_bytes()); + + // Call gas limit as uint128 (16 bytes) + data.extend_from_slice(&call_gas_limit.to_be_bytes()); + + // Total: 52 bytes (20 + 16 + 16) data } From 580e9f8124c383eb20c9a990aa7136325965b154 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 11 Jul 2025 16:51:41 +0000 Subject: [PATCH 48/96] Format Rust code using rustfmt --- src/wallet.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/wallet.rs b/src/wallet.rs index 89f83eb..a238880 100644 --- a/src/wallet.rs +++ b/src/wallet.rs @@ -2756,16 +2756,16 @@ pub fn encode_circle_paymaster_data( call_gas_limit: u128, ) -> Vec { let mut data = Vec::new(); - + // Paymaster address (20 bytes) data.extend_from_slice(paymaster.as_slice()); - + // Verification gas limit as uint128 (16 bytes) data.extend_from_slice(&verification_gas_limit.to_be_bytes()); - - // Call gas limit as uint128 (16 bytes) + + // Call gas limit as uint128 (16 bytes) data.extend_from_slice(&call_gas_limit.to_be_bytes()); - + // Total: 52 bytes (20 + 16 + 16) data } From dfd3ac72bd04ab1da6cbd14037f7dd9d9fea677a Mon Sep 17 00:00:00 2001 From: Hallmane Date: Fri, 11 Jul 2025 19:04:05 +0200 Subject: [PATCH 49/96] wip --- src/wallet.rs | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/src/wallet.rs b/src/wallet.rs index a238880..ea9e1d2 100644 --- a/src/wallet.rs +++ b/src/wallet.rs @@ -2479,15 +2479,13 @@ impl UserOperationBuilder { signer: &S, provider: &Provider, ) -> Result<(), WalletError> { - // Generate paymaster data with real permit signature - let paymaster_data = encode_usdc_paymaster_data_with_signer( + // Use simple Circle format - no permit signature needed + // The TBA has already approved the paymaster to spend USDC + let paymaster_data = encode_circle_paymaster_data( paymaster, - token_address, - max_cost, - tba_address, - signer, - provider, - )?; + 500_000, // Default verification gas limit + 300_000, // Default call gas limit + ); // Set the combined paymaster and data self.paymaster_and_data = paymaster_data; @@ -2685,6 +2683,8 @@ pub fn get_usdc_permit_nonce( } /// Encode paymaster data for USDC payment with EIP-2612 permit signature +/// DEPRECATED - We don't use permit signatures anymore, only simple format +/* pub fn encode_usdc_paymaster_data_with_signer( paymaster: EthAddress, token_address: EthAddress, @@ -2736,6 +2736,7 @@ pub fn encode_usdc_paymaster_data_with_signer( Ok(data) } +*/ /// Encode paymaster data for USDC payment (keeping old function for compatibility) /// This version uses a dummy signature and will fail with AA33 @@ -2846,6 +2847,8 @@ pub fn create_multicall_permit_and_execute( /// Encode paymaster data for USDC payment with on-chain permit (for TBAs) /// This version doesn't include a permit signature since TBAs will call permit on-chain +/// DEPRECATED - We only use the simple Circle format now +/* pub fn encode_usdc_paymaster_data_for_tba( paymaster: EthAddress, token_address: EthAddress, @@ -2874,3 +2877,4 @@ pub fn encode_usdc_paymaster_data_for_tba( data } +*/ From a5e1e27dc65e74b8155a6bf70f0467167856f39f Mon Sep 17 00:00:00 2001 From: Hallmane Date: Fri, 11 Jul 2025 19:04:45 +0200 Subject: [PATCH 50/96] wip --- src/wallet.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/wallet.rs b/src/wallet.rs index ea9e1d2..c74be0e 100644 --- a/src/wallet.rs +++ b/src/wallet.rs @@ -2845,9 +2845,9 @@ pub fn create_multicall_permit_and_execute( permit_calldata } -/// Encode paymaster data for USDC payment with on-chain permit (for TBAs) -/// This version doesn't include a permit signature since TBAs will call permit on-chain -/// DEPRECATED - We only use the simple Circle format now +// Encode paymaster data for USDC payment with on-chain permit (for TBAs) +// This version doesn't include a permit signature since TBAs will call permit on-chain +// DEPRECATED - We only use the simple Circle format now /* pub fn encode_usdc_paymaster_data_for_tba( paymaster: EthAddress, From 3995139578e58d9dc6a65b2364493da578bd4f91 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 11 Jul 2025 17:05:11 +0000 Subject: [PATCH 51/96] Format Rust code using rustfmt --- src/wallet.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/wallet.rs b/src/wallet.rs index c74be0e..baf416a 100644 --- a/src/wallet.rs +++ b/src/wallet.rs @@ -2482,9 +2482,8 @@ impl UserOperationBuilder { // Use simple Circle format - no permit signature needed // The TBA has already approved the paymaster to spend USDC let paymaster_data = encode_circle_paymaster_data( - paymaster, - 500_000, // Default verification gas limit - 300_000, // Default call gas limit + paymaster, 500_000, // Default verification gas limit + 300_000, // Default call gas limit ); // Set the combined paymaster and data From f6c17b3e28377587bb25e764508bb3e984a1b575 Mon Sep 17 00:00:00 2001 From: Hallmane Date: Fri, 11 Jul 2025 19:10:30 +0200 Subject: [PATCH 52/96] wip --- src/wallet.rs | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/src/wallet.rs b/src/wallet.rs index c74be0e..333fd22 100644 --- a/src/wallet.rs +++ b/src/wallet.rs @@ -2750,7 +2750,7 @@ pub fn encode_usdc_paymaster_data( } /// Encode paymaster data for Circle's USDC paymaster -/// Format: abi.encodePacked(address paymaster, uint128 verificationGasLimit, uint128 callGasLimit) +/// Format: abi.encode(address paymaster, uint256 verificationGasLimit, uint256 callGasLimit) pub fn encode_circle_paymaster_data( paymaster: EthAddress, verification_gas_limit: u128, @@ -2758,16 +2758,21 @@ pub fn encode_circle_paymaster_data( ) -> Vec { let mut data = Vec::new(); - // Paymaster address (20 bytes) - data.extend_from_slice(paymaster.as_slice()); + // ABI encoding pads all values to 32 bytes + + // Paymaster address (32 bytes - padded on the left with zeros) + data.extend_from_slice(&[0u8; 12]); // 12 bytes of padding + data.extend_from_slice(paymaster.as_slice()); // 20 bytes of address - // Verification gas limit as uint128 (16 bytes) - data.extend_from_slice(&verification_gas_limit.to_be_bytes()); + // Verification gas limit as uint256 (32 bytes) + let verification_gas_u256 = U256::from(verification_gas_limit); + data.extend_from_slice(&verification_gas_u256.to_be_bytes::<32>()); - // Call gas limit as uint128 (16 bytes) - data.extend_from_slice(&call_gas_limit.to_be_bytes()); + // Call gas limit as uint256 (32 bytes) + let call_gas_u256 = U256::from(call_gas_limit); + data.extend_from_slice(&call_gas_u256.to_be_bytes::<32>()); - // Total: 52 bytes (20 + 16 + 16) + // Total: 96 bytes (32 + 32 + 32) data } From 66fb6b87938620c790b3c23a12fa2d66f4ffbdd5 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 11 Jul 2025 17:11:53 +0000 Subject: [PATCH 53/96] Format Rust code using rustfmt --- src/wallet.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wallet.rs b/src/wallet.rs index 5c9c99c..2937430 100644 --- a/src/wallet.rs +++ b/src/wallet.rs @@ -2758,7 +2758,7 @@ pub fn encode_circle_paymaster_data( let mut data = Vec::new(); // ABI encoding pads all values to 32 bytes - + // Paymaster address (32 bytes - padded on the left with zeros) data.extend_from_slice(&[0u8; 12]); // 12 bytes of padding data.extend_from_slice(paymaster.as_slice()); // 20 bytes of address From 0ef160ed477b456dc16624f7b44c9fc2c7e58db4 Mon Sep 17 00:00:00 2001 From: Hallmane Date: Fri, 11 Jul 2025 19:17:25 +0200 Subject: [PATCH 54/96] wip --- src/wallet.rs | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/wallet.rs b/src/wallet.rs index 5c9c99c..7ed672d 100644 --- a/src/wallet.rs +++ b/src/wallet.rs @@ -2749,7 +2749,8 @@ pub fn encode_usdc_paymaster_data( } /// Encode paymaster data for Circle's USDC paymaster -/// Format: abi.encode(address paymaster, uint256 verificationGasLimit, uint256 callGasLimit) +/// Format: abi.encode(uint256 verificationGasLimit, uint256 callGasLimit) +/// Note: The paymaster address is NOT included here - it goes in the separate paymaster field pub fn encode_circle_paymaster_data( paymaster: EthAddress, verification_gas_limit: u128, @@ -2757,12 +2758,11 @@ pub fn encode_circle_paymaster_data( ) -> Vec { let mut data = Vec::new(); - // ABI encoding pads all values to 32 bytes + // First, add the paymaster address (20 bytes) - this will be extracted by the bundler + data.extend_from_slice(paymaster.as_slice()); + + // Then add the ABI-encoded gas limits (what the paymaster actually expects) - // Paymaster address (32 bytes - padded on the left with zeros) - data.extend_from_slice(&[0u8; 12]); // 12 bytes of padding - data.extend_from_slice(paymaster.as_slice()); // 20 bytes of address - // Verification gas limit as uint256 (32 bytes) let verification_gas_u256 = U256::from(verification_gas_limit); data.extend_from_slice(&verification_gas_u256.to_be_bytes::<32>()); @@ -2771,7 +2771,10 @@ pub fn encode_circle_paymaster_data( let call_gas_u256 = U256::from(call_gas_limit); data.extend_from_slice(&call_gas_u256.to_be_bytes::<32>()); - // Total: 96 bytes (32 + 32 + 32) + // Total: 84 bytes (20 + 32 + 32) + // The bundler will split this into: + // - paymaster: first 20 bytes + // - paymasterData: remaining 64 bytes (the ABI-encoded gas limits) data } From eaf7222a28f3bc066e30e668846d1a4319e06712 Mon Sep 17 00:00:00 2001 From: Hallmane Date: Fri, 11 Jul 2025 19:19:48 +0200 Subject: [PATCH 55/96] wip --- src/wallet.rs | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/wallet.rs b/src/wallet.rs index 84df38c..7ed672d 100644 --- a/src/wallet.rs +++ b/src/wallet.rs @@ -2758,20 +2758,11 @@ pub fn encode_circle_paymaster_data( ) -> Vec { let mut data = Vec::new(); -<<<<<<< HEAD // First, add the paymaster address (20 bytes) - this will be extracted by the bundler data.extend_from_slice(paymaster.as_slice()); // Then add the ABI-encoded gas limits (what the paymaster actually expects) -======= - // ABI encoding pads all values to 32 bytes - - // Paymaster address (32 bytes - padded on the left with zeros) - data.extend_from_slice(&[0u8; 12]); // 12 bytes of padding - data.extend_from_slice(paymaster.as_slice()); // 20 bytes of address - ->>>>>>> 66fb6b87938620c790b3c23a12fa2d66f4ffbdd5 // Verification gas limit as uint256 (32 bytes) let verification_gas_u256 = U256::from(verification_gas_limit); data.extend_from_slice(&verification_gas_u256.to_be_bytes::<32>()); From 5d138fe49d08bdfc241048fa7724a47eb8646be7 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 11 Jul 2025 17:20:15 +0000 Subject: [PATCH 56/96] Format Rust code using rustfmt --- src/wallet.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/wallet.rs b/src/wallet.rs index 7ed672d..8d9b393 100644 --- a/src/wallet.rs +++ b/src/wallet.rs @@ -2760,9 +2760,9 @@ pub fn encode_circle_paymaster_data( // First, add the paymaster address (20 bytes) - this will be extracted by the bundler data.extend_from_slice(paymaster.as_slice()); - + // Then add the ABI-encoded gas limits (what the paymaster actually expects) - + // Verification gas limit as uint256 (32 bytes) let verification_gas_u256 = U256::from(verification_gas_limit); data.extend_from_slice(&verification_gas_u256.to_be_bytes::<32>()); From b0cd5385a73537ca5f205b279bdf10b74e98c8ff Mon Sep 17 00:00:00 2001 From: Hallmane Date: Fri, 11 Jul 2025 19:30:36 +0200 Subject: [PATCH 57/96] wip --- src/wallet.rs | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/src/wallet.rs b/src/wallet.rs index 7ed672d..562f621 100644 --- a/src/wallet.rs +++ b/src/wallet.rs @@ -2749,8 +2749,8 @@ pub fn encode_usdc_paymaster_data( } /// Encode paymaster data for Circle's USDC paymaster -/// Format: abi.encode(uint256 verificationGasLimit, uint256 callGasLimit) -/// Note: The paymaster address is NOT included here - it goes in the separate paymaster field +/// Format: abi.encode(address paymaster, uint256 verificationGasLimit, uint256 callGasLimit) +/// Returns the complete ABI encoding that the paymaster expects to receive pub fn encode_circle_paymaster_data( paymaster: EthAddress, verification_gas_limit: u128, @@ -2758,23 +2758,25 @@ pub fn encode_circle_paymaster_data( ) -> Vec { let mut data = Vec::new(); - // First, add the paymaster address (20 bytes) - this will be extracted by the bundler - data.extend_from_slice(paymaster.as_slice()); + // ABI encoding includes the paymaster address as the first parameter + // This matches the developer's example: + // encode(["address", "uint256", "uint256"], ['0x0578cFB241215b77442a541325d6A4E6dFE700Ec', 500000, 300000]) - // Then add the ABI-encoded gas limits (what the paymaster actually expects) + // First parameter: paymaster address as uint256 (padded to 32 bytes) + let mut padded_address = vec![0u8; 12]; // 12 zero bytes for padding + padded_address.extend_from_slice(paymaster.as_slice()); // 20 bytes of address + data.extend_from_slice(&padded_address); - // Verification gas limit as uint256 (32 bytes) + // Second parameter: verification gas limit as uint256 (32 bytes) let verification_gas_u256 = U256::from(verification_gas_limit); data.extend_from_slice(&verification_gas_u256.to_be_bytes::<32>()); - // Call gas limit as uint256 (32 bytes) + // Third parameter: call gas limit as uint256 (32 bytes) let call_gas_u256 = U256::from(call_gas_limit); data.extend_from_slice(&call_gas_u256.to_be_bytes::<32>()); - // Total: 84 bytes (20 + 32 + 32) - // The bundler will split this into: - // - paymaster: first 20 bytes - // - paymasterData: remaining 64 bytes (the ABI-encoded gas limits) + // Total: 96 bytes (32 + 32 + 32) + // This is the complete ABI encoding the paymaster expects data } From 81ab04603983a770805499b36d88d19b8731eccf Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 11 Jul 2025 17:32:08 +0000 Subject: [PATCH 58/96] Format Rust code using rustfmt --- src/wallet.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/wallet.rs b/src/wallet.rs index 562f621..ddeb181 100644 --- a/src/wallet.rs +++ b/src/wallet.rs @@ -2761,12 +2761,12 @@ pub fn encode_circle_paymaster_data( // ABI encoding includes the paymaster address as the first parameter // This matches the developer's example: // encode(["address", "uint256", "uint256"], ['0x0578cFB241215b77442a541325d6A4E6dFE700Ec', 500000, 300000]) - + // First parameter: paymaster address as uint256 (padded to 32 bytes) let mut padded_address = vec![0u8; 12]; // 12 zero bytes for padding padded_address.extend_from_slice(paymaster.as_slice()); // 20 bytes of address data.extend_from_slice(&padded_address); - + // Second parameter: verification gas limit as uint256 (32 bytes) let verification_gas_u256 = U256::from(verification_gas_limit); data.extend_from_slice(&verification_gas_u256.to_be_bytes::<32>()); From 96d95f833883219172ced40d7e1175bc62ef866b Mon Sep 17 00:00:00 2001 From: Hallmane Date: Fri, 25 Jul 2025 16:14:49 +0200 Subject: [PATCH 59/96] initial version of hyperwallet_client --- src/hyperwallet_client.rs | 403 ++++++++++++++++++++++++++++++++++++++ src/lib.rs | 2 + 2 files changed, 405 insertions(+) create mode 100644 src/hyperwallet_client.rs diff --git a/src/hyperwallet_client.rs b/src/hyperwallet_client.rs new file mode 100644 index 0000000..ba893bd --- /dev/null +++ b/src/hyperwallet_client.rs @@ -0,0 +1,403 @@ +//! High-level, ergonomic client for interacting with the `hyperwallet:hyperwallet:*` service. +//! +//! This module provides a type-safe and convenient way for Hyperware processes to manage +//! wallets and perform blockchain operations by communicating with the system's central +//! Hyperwallet service. The primary entry point is the `initialize` function, which +//! performs the handshake protocol to establish a session. +//! The main use of sessions is to tie it with wallets' "unlocked" state. Currently, I don't really +//! use it, but it might be an easier handle than the process string. +//! +//! It contains a public `types` submodule that defines the entire protocol, which is +//! also used by the Hyperwallet server process to ensure compatibility. + + +use crate::{Address, Request}; +use serde::{de::DeserializeOwned}; +use thiserror::Error; + +// Re-export the most important types for convenience for developers using this client. +pub use types::{ + HandshakeConfig, Operation, OperationCategory, OperationError, ProcessPermissions, SessionInfo, + SpendingLimits, +}; + +/// The static address of the system's Hyperwallet service. (TODO: change when system package) +const HYPERWALLET_ADDRESS: &str = "hyperwallet:hyperwallet:sys"; + +/// Errors that can occur when interacting with the Hyperwallet client. +#[derive(Debug, Error)] +pub enum HyperwalletClientError { + #[error("Handshake Error: Version incompatibility - client {client} vs server {server}")] + VersionMismatch { client: String, server: String }, + #[error("Handshake Error: Required operation is not supported by the server: {operation:?}")] + OperationNotSupported { operation: Operation }, + #[error("Communication error while sending request to Hyperwallet: {0}")] + Communication(anyhow::Error), + #[error("Hyperwallet service returned a failure response: {0:?}")] + ServerError(OperationError), + #[error("Failed to serialize request: {0}")] + Serialization(serde_json::Error), + #[error("Failed to deserialize response: {0}")] + Deserialization(serde_json::Error), +} + +/// Performs the full handshake and registration protocol with the Hyperwallet service. +/// This is the primary entry point for any process wanting to use Hyperwallet. +/// +/// On success, it returns a `SessionInfo` containing the session ID required for +/// all subsequent calls. +pub fn initialize(config: HandshakeConfig) -> Result { + let our_address = crate::our(); + let client_name = config + .client_name + .unwrap_or_else(|| our_address.process().to_string()); + + // Step 1: Send ClientHello + let hello_step = types::HandshakeStep::ClientHello { + client_version: env!("CARGO_PKG_VERSION").to_string(), + client_name, + }; + let welcome_response: types::OperationResponse = send_handshake_step(hello_step, &our_address)?; + + // Step 2: Parse ServerWelcome and check compatibility + let welcome_data = welcome_response.data.ok_or_else(|| { + HyperwalletClientError::ServerError(types::OperationError::internal_error( + "ServerWelcome response contained no data", + )) + })?; + + let supported_ops: Vec = + serde_json::from_value(welcome_data["supported_operations"].clone()) + .map_err(HyperwalletClientError::Deserialization)?; + + for op in &config.required_operations { + if !supported_ops.contains(op) { + return Err(HyperwalletClientError::OperationNotSupported { + operation: op.clone(), + }); + } + } + + // Step 3: Send Register + let register_step = types::HandshakeStep::Register { + required_operations: config.required_operations.into_iter().collect(), + spending_limits: config.spending_limits, + }; + let complete_response = send_handshake_step(register_step, &our_address)?; + + // Step 4: Parse Complete and return SessionInfo + let complete_data = complete_response.data.ok_or_else(|| { + HyperwalletClientError::ServerError(types::OperationError::internal_error( + "Complete response contained no data", + )) + })?; + + serde_json::from_value(complete_data).map_err(HyperwalletClientError::Deserialization) +} + +/// Generic helper to create and send an `OperationRequest` to Hyperwallet. +/// yeah, not really necessary +fn send_request( + _session_info: &SessionInfo, + operation: Operation, + params: serde_json::Value, + wallet_id: Option, + chain_id: Option, +) -> Result { + let request = types::OperationRequest { + operation, + params, + wallet_id, + chain_id, + // The session_id would be included here in the auth struct if needed + auth: types::ProcessAuth { + process_address: crate::our().to_string(), + signature: None, + }, + request_id: None, + timestamp: std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .unwrap() + .as_secs(), + }; + + let response: types::OperationResponse = execute_request(request)?; + match response.data { + Some(data) => serde_json::from_value(data).map_err(HyperwalletClientError::Deserialization), + None => Err(HyperwalletClientError::ServerError( + types::OperationError::internal_error("Operation succeeded but returned no data"), + )), + } +} + +// Internal helper for the handshake steps which don't have a session yet. +fn send_handshake_step( + step: types::HandshakeStep, + our_address: &Address, +) -> Result { + let request = types::OperationRequest { + operation: Operation::Handshake, + params: serde_json::to_value(step).map_err(HyperwalletClientError::Serialization)?, + auth: types::ProcessAuth { + process_address: our_address.to_string(), + signature: None, + }, + wallet_id: None, + chain_id: None, + request_id: None, + timestamp: 0, + }; + execute_request(request) +} + +// The lowest-level helper that handles sending the request and awaiting a response. +fn execute_request( + request: types::OperationRequest, +) -> Result { + let response = Request::new() + .target(HYPERWALLET_ADDRESS.parse::
().unwrap()) + .body(serde_json::to_vec(&request).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 op_response: types::OperationResponse = serde_json::from_slice(response.body()) + .map_err(HyperwalletClientError::Deserialization)?; + + if !op_response.success { + return Err(HyperwalletClientError::ServerError( + op_response.error.unwrap_or_else(|| { + types::OperationError::internal_error("Operation failed with no error details") + }), + )); + } + + Ok(op_response) +} + +/// Contains all the shared types for the Hyperwallet protocol. +pub mod types { + use serde::{Deserialize, Serialize}; + use std::collections::HashSet; + + /// All possible wallet operations that can be performed through the hyperwallet service. + #[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, + } + + /// Categories for grouping operations. + #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] + pub enum OperationCategory { + System, + ProcessManagement, + WalletManagement, + Ethereum, + TokenBoundAccount, + ERC4337, + Hypermap, + Query, + Advanced, + } + + impl Operation { + pub fn all() -> Vec { + // In a real implementation, this would list all variants + vec![] + } + pub fn category(&self) -> OperationCategory { + match self { + Operation::Handshake | Operation::UnlockWallet => OperationCategory::System, + // ... other categories ... + _ => OperationCategory::Query, + } + } + } + + /// A configuration object for the `initialize` handshake. + #[derive(Debug, Default)] + pub struct HandshakeConfig { + pub(crate) required_operations: HashSet, + pub(crate) spending_limits: Option, + pub(crate) client_name: Option, + } + + impl HandshakeConfig { + pub fn new() -> Self { + Default::default() + } + pub fn with_operations(mut self, operations: &[Operation]) -> Self { + self.required_operations.extend(operations.iter().cloned()); + self + } + pub fn require_category(mut self, category: OperationCategory) -> Self { + self.required_operations.extend( + Operation::all() + .into_iter() + .filter(|op| op.category() == category), + ); + self + } + pub fn with_spending_limits(mut self, limits: SpendingLimits) -> Self { + self.spending_limits = Some(limits); + self + } + pub fn with_name(mut self, name: impl Into) -> Self { + self.client_name = Some(name.into()); + self + } + } + + /// Information about an established session with Hyperwallet. + #[derive(Debug, Serialize, Deserialize)] + pub struct SessionInfo { + pub server_version: String, + pub session_id: String, + pub registered_permissions: ProcessPermissions, + } + + /// The steps involved in the handshake protocol. + #[derive(Debug, Serialize, Deserialize)] + pub enum HandshakeStep { + ClientHello { + client_version: String, + client_name: String, + }, + Register { + required_operations: Vec, + spending_limits: Option, + }, + } + + // All other request/response and permission structs + + #[derive(Debug, Clone, Serialize, Deserialize)] + pub struct OperationRequest { + pub operation: Operation, + pub params: serde_json::Value, + pub wallet_id: Option, + pub chain_id: Option, + pub auth: ProcessAuth, + pub request_id: Option, + pub timestamp: u64, + } + + #[derive(Debug, Clone, Serialize, Deserialize)] + pub struct ProcessAuth { + pub process_address: String, + pub signature: Option>, + } + + #[derive(Debug, Clone, Serialize, Deserialize)] + pub struct OperationResponse { + pub success: bool, + pub data: Option, + pub error: Option, + pub request_id: Option, + pub timestamp: u64, + } + + #[derive(Debug, Clone, Serialize, Deserialize)] + pub struct OperationError { + pub code: ErrorCode, + pub message: String, + pub details: Option, + } + + impl OperationError { + pub fn internal_error(message: &str) -> Self { + Self { + code: ErrorCode::InternalError, + message: message.to_string(), + details: None, + } + } + } + + #[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 process_address: String, + pub allowed_operations: HashSet, + pub spending_limits: Option, + pub updatable_settings: Vec, + pub registered_at: u64, + } +} diff --git a/src/lib.rs b/src/lib.rs index 0b0c223..be83181 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -77,6 +77,8 @@ pub mod wallet; /// A set of types and macros for writing "script" processes. pub mod scripting; +pub mod hyperwallet_client; + mod types; pub use types::{ address::{Address, AddressParseError}, From 6057b48f0dbe52e4308c16d0f26f418d6c1f68c6 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 25 Jul 2025 14:15:41 +0000 Subject: [PATCH 60/96] Format Rust code using rustfmt --- src/hyperwallet_client.rs | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/src/hyperwallet_client.rs b/src/hyperwallet_client.rs index ba893bd..7fba418 100644 --- a/src/hyperwallet_client.rs +++ b/src/hyperwallet_client.rs @@ -3,16 +3,15 @@ //! This module provides a type-safe and convenient way for Hyperware processes to manage //! wallets and perform blockchain operations by communicating with the system's central //! Hyperwallet service. The primary entry point is the `initialize` function, which -//! performs the handshake protocol to establish a session. +//! performs the handshake protocol to establish a session. //! The main use of sessions is to tie it with wallets' "unlocked" state. Currently, I don't really //! use it, but it might be an easier handle than the process string. //! //! It contains a public `types` submodule that defines the entire protocol, which is //! also used by the Hyperwallet server process to ensure compatibility. - use crate::{Address, Request}; -use serde::{de::DeserializeOwned}; +use serde::de::DeserializeOwned; use thiserror::Error; // Re-export the most important types for convenience for developers using this client. @@ -161,8 +160,8 @@ fn execute_request( .map_err(|e| HyperwalletClientError::Communication(e.into()))? .map_err(|e| HyperwalletClientError::Communication(e.into()))?; - let op_response: types::OperationResponse = serde_json::from_slice(response.body()) - .map_err(HyperwalletClientError::Deserialization)?; + let op_response: types::OperationResponse = + serde_json::from_slice(response.body()).map_err(HyperwalletClientError::Deserialization)?; if !op_response.success { return Err(HyperwalletClientError::ServerError( @@ -313,9 +312,9 @@ pub mod types { spending_limits: Option, }, } - + // All other request/response and permission structs - + #[derive(Debug, Clone, Serialize, Deserialize)] pub struct OperationRequest { pub operation: Operation, @@ -348,7 +347,7 @@ pub mod types { pub message: String, pub details: Option, } - + impl OperationError { pub fn internal_error(message: &str) -> Self { Self { @@ -375,7 +374,7 @@ pub mod types { OperationNotSupported, VersionMismatch, } - + #[derive(Debug, Clone, Serialize, Deserialize, Default)] pub struct SpendingLimits { pub per_tx_eth: Option, From c773d9f4931b99d6a6b00d1b24f351df537ab403 Mon Sep 17 00:00:00 2001 From: Hallmane Date: Fri, 25 Jul 2025 18:39:57 +0200 Subject: [PATCH 61/96] lol --- src/hyperwallet_client.rs | 92 ++++++++++++--------------------------- 1 file changed, 27 insertions(+), 65 deletions(-) diff --git a/src/hyperwallet_client.rs b/src/hyperwallet_client.rs index ba893bd..2287ae0 100644 --- a/src/hyperwallet_client.rs +++ b/src/hyperwallet_client.rs @@ -1,27 +1,22 @@ //! High-level, ergonomic client for interacting with the `hyperwallet:hyperwallet:*` service. //! //! This module provides a type-safe and convenient way for Hyperware processes to manage -//! wallets and perform blockchain operations by communicating with the system's central -//! Hyperwallet service. The primary entry point is the `initialize` function, which -//! performs the handshake protocol to establish a session. -//! The main use of sessions is to tie it with wallets' "unlocked" state. Currently, I don't really -//! use it, but it might be an easier handle than the process string. +//! wallets and perform blockchain operations. The primary entry point is the `initialize` +//! function, which performs the handshake protocol. //! //! It contains a public `types` submodule that defines the entire protocol, which is //! also used by the Hyperwallet server process to ensure compatibility. - use crate::{Address, Request}; -use serde::{de::DeserializeOwned}; use thiserror::Error; -// Re-export the most important types for convenience for developers using this client. +// Re-export the most important types for convenience. pub use types::{ HandshakeConfig, Operation, OperationCategory, OperationError, ProcessPermissions, SessionInfo, SpendingLimits, }; -/// The static address of the system's Hyperwallet service. (TODO: change when system package) +/// The static address of the system's Hyperwallet service. const HYPERWALLET_ADDRESS: &str = "hyperwallet:hyperwallet:sys"; /// Errors that can occur when interacting with the Hyperwallet client. @@ -42,22 +37,21 @@ pub enum HyperwalletClientError { } /// Performs the full handshake and registration protocol with the Hyperwallet service. -/// This is the primary entry point for any process wanting to use Hyperwallet. -/// -/// On success, it returns a `SessionInfo` containing the session ID required for -/// all subsequent calls. -pub fn initialize(config: HandshakeConfig) -> Result { - let our_address = crate::our(); +/// The calling process must provide its own address (`our`). +pub fn initialize( + our: &Address, + config: HandshakeConfig, +) -> Result { let client_name = config .client_name - .unwrap_or_else(|| our_address.process().to_string()); + .unwrap_or_else(|| our.process().to_string()); // Step 1: Send ClientHello let hello_step = types::HandshakeStep::ClientHello { client_version: env!("CARGO_PKG_VERSION").to_string(), client_name, }; - let welcome_response: types::OperationResponse = send_handshake_step(hello_step, &our_address)?; + let welcome_response: types::OperationResponse = send_handshake_step(hello_step, our)?; // Step 2: Parse ServerWelcome and check compatibility let welcome_data = welcome_response.data.ok_or_else(|| { @@ -83,7 +77,7 @@ pub fn initialize(config: HandshakeConfig) -> Result Result( - _session_info: &SessionInfo, - operation: Operation, - params: serde_json::Value, - wallet_id: Option, - chain_id: Option, -) -> Result { - let request = types::OperationRequest { - operation, - params, - wallet_id, - chain_id, - // The session_id would be included here in the auth struct if needed - auth: types::ProcessAuth { - process_address: crate::our().to_string(), - signature: None, - }, - request_id: None, - timestamp: std::time::SystemTime::now() - .duration_since(std::time::UNIX_EPOCH) - .unwrap() - .as_secs(), - }; - - let response: types::OperationResponse = execute_request(request)?; - match response.data { - Some(data) => serde_json::from_value(data).map_err(HyperwalletClientError::Deserialization), - None => Err(HyperwalletClientError::ServerError( - types::OperationError::internal_error("Operation succeeded but returned no data"), - )), - } -} - -// Internal helper for the handshake steps which don't have a session yet. +// Internal helper for the handshake steps. fn send_handshake_step( step: types::HandshakeStep, - our_address: &Address, + our: &Address, ) -> Result { let request = types::OperationRequest { operation: Operation::Handshake, params: serde_json::to_value(step).map_err(HyperwalletClientError::Serialization)?, auth: types::ProcessAuth { - process_address: our_address.to_string(), + process_address: our.to_string(), signature: None, }, wallet_id: None, @@ -161,8 +120,8 @@ fn execute_request( .map_err(|e| HyperwalletClientError::Communication(e.into()))? .map_err(|e| HyperwalletClientError::Communication(e.into()))?; - let op_response: types::OperationResponse = serde_json::from_slice(response.body()) - .map_err(HyperwalletClientError::Deserialization)?; + let op_response: types::OperationResponse = + serde_json::from_slice(response.body()).map_err(HyperwalletClientError::Deserialization)?; if !op_response.success { return Err(HyperwalletClientError::ServerError( @@ -247,12 +206,17 @@ pub mod types { impl Operation { pub fn all() -> Vec { - // In a real implementation, this would list all variants + // A full implementation would use a macro or list all variants. + // This is a placeholder. vec![] } + pub fn category(&self) -> OperationCategory { match self { Operation::Handshake | Operation::UnlockWallet => OperationCategory::System, + Operation::RegisterProcess | Operation::UpdateSpendingLimits => { + OperationCategory::ProcessManagement + } // ... other categories ... _ => OperationCategory::Query, } @@ -313,9 +277,7 @@ pub mod types { spending_limits: Option, }, } - - // All other request/response and permission structs - + #[derive(Debug, Clone, Serialize, Deserialize)] pub struct OperationRequest { pub operation: Operation, @@ -348,7 +310,7 @@ pub mod types { pub message: String, pub details: Option, } - + impl OperationError { pub fn internal_error(message: &str) -> Self { Self { @@ -375,7 +337,7 @@ pub mod types { OperationNotSupported, VersionMismatch, } - + #[derive(Debug, Clone, Serialize, Deserialize, Default)] pub struct SpendingLimits { pub per_tx_eth: Option, @@ -400,4 +362,4 @@ pub mod types { pub updatable_settings: Vec, pub registered_at: u64, } -} +} \ No newline at end of file From 9ed69085f009d7761e6047b41e364ff14c20d1e0 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 28 Jul 2025 13:24:56 +0000 Subject: [PATCH 62/96] Format Rust code using rustfmt --- src/hyperwallet_client/api.rs | 22 +++++++-- src/hyperwallet_client/mod.rs | 37 +++++---------- src/hyperwallet_client/types.rs | 84 +++++++++++++++++++-------------- 3 files changed, 78 insertions(+), 65 deletions(-) diff --git a/src/hyperwallet_client/api.rs b/src/hyperwallet_client/api.rs index 9561573..7c9a339 100644 --- a/src/hyperwallet_client/api.rs +++ b/src/hyperwallet_client/api.rs @@ -1,9 +1,9 @@ //! Public API wrappers for Hyperwallet operations. -use super::{execute_request, HyperwalletClientError}; use super::types::{ self, Balance, Operation, SessionInfo, SpendingLimits, TxReceipt, UserOperationHash, Wallet, }; +use super::{execute_request, HyperwalletClientError}; use crate::Address; /// Creates a new wallet for the process. @@ -14,7 +14,14 @@ pub fn create_wallet( password: Option<&str>, ) -> Result { let params = serde_json::json!({ "name": name, "password": password }); - let request = build_request(our, session_info, Operation::CreateWallet, params, None, None); + let request = build_request( + our, + session_info, + Operation::CreateWallet, + params, + None, + None, + ); let response = execute_request(request)?; serde_json::from_value(response.data.unwrap_or_default()) .map_err(HyperwalletClientError::Deserialization) @@ -167,7 +174,14 @@ pub fn import_wallet( "private_key": private_key, "password": password, }); - let request = build_request(our, session_info, Operation::ImportWallet, params, None, None); + let request = build_request( + our, + session_info, + Operation::ImportWallet, + params, + None, + None, + ); let response = execute_request(request)?; serde_json::from_value(response.data.unwrap_or_default()) .map_err(HyperwalletClientError::Deserialization) @@ -457,4 +471,4 @@ fn build_request( .unwrap() .as_secs(), } -} \ No newline at end of file +} diff --git a/src/hyperwallet_client/mod.rs b/src/hyperwallet_client/mod.rs index 96c2762..c8b5947 100644 --- a/src/hyperwallet_client/mod.rs +++ b/src/hyperwallet_client/mod.rs @@ -2,40 +2,25 @@ //! //! This module provides a type-safe and convenient way for Hyperware processes to manage //! wallets and perform blockchain operations. The primary entry point is the `initialize` -//! function, which performs the handshake protocol. +//! function, which performs the handshake protocol. //! //! It contains a public `types` submodule that defines the entire protocol, which is //! also used by the Hyperwallet server process to ensure compatibility. -pub mod types; pub mod api; +pub mod types; -pub use types::{ - Balance, HandshakeConfig, Operation, OperationCategory, OperationError, ProcessPermissions, - SessionInfo, SpendingLimits, TxReceipt, UserOperationHash, Wallet, -}; pub use api::{ - approve_token, - build_and_sign_user_operation_for_payment, - check_tba_ownership, - create_note, - create_wallet, - delete_wallet, - execute_via_tba, - get_balance, - get_token_balance, - get_user_operation_receipt, - get_wallet_info, - import_wallet, - list_wallets, - rename_wallet, - resolve_identity, - send_eth, - send_token, - set_wallet_limits, - submit_user_operation, + approve_token, build_and_sign_user_operation_for_payment, check_tba_ownership, create_note, + create_wallet, delete_wallet, execute_via_tba, get_balance, get_token_balance, + get_user_operation_receipt, get_wallet_info, import_wallet, list_wallets, rename_wallet, + resolve_identity, send_eth, send_token, set_wallet_limits, submit_user_operation, unlock_wallet, }; +pub use types::{ + Balance, HandshakeConfig, Operation, OperationCategory, OperationError, ProcessPermissions, + SessionInfo, SpendingLimits, TxReceipt, UserOperationHash, Wallet, +}; use crate::{Address, Request}; use thiserror::Error; @@ -151,4 +136,4 @@ pub(crate) fn execute_request( } Ok(op_response) -} \ No newline at end of file +} diff --git a/src/hyperwallet_client/types.rs b/src/hyperwallet_client/types.rs index fe5572a..c775ee4 100644 --- a/src/hyperwallet_client/types.rs +++ b/src/hyperwallet_client/types.rs @@ -131,42 +131,58 @@ impl 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 - } + + 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, } } } @@ -332,5 +348,3 @@ pub struct Balance { pub formatted: String, pub raw: String, // U256 as string } - - \ No newline at end of file From bf2ac9a04426183d79bb79eaf276ed9ddf336c14 Mon Sep 17 00:00:00 2001 From: Hallmane Date: Mon, 28 Jul 2025 16:00:12 +0200 Subject: [PATCH 63/96] nope --- src/hyperwallet_client/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hyperwallet_client/mod.rs b/src/hyperwallet_client/mod.rs index c8b5947..511f531 100644 --- a/src/hyperwallet_client/mod.rs +++ b/src/hyperwallet_client/mod.rs @@ -26,7 +26,7 @@ use crate::{Address, Request}; use thiserror::Error; /// The static address of the system's Hyperwallet service. -const HYPERWALLET_ADDRESS: &str = "hyperwallet:hyperwallet:sys"; +const HYPERWALLET_ADDRESS: &str = "hyperwallet:hyperwallet:hallman.hypr"; /// Errors that can occur when interacting with the Hyperwallet client. #[derive(Debug, Error)] From 77de47e10b49d6b3ae5372710a7da3386f46d294 Mon Sep 17 00:00:00 2001 From: Hallmane Date: Mon, 28 Jul 2025 16:08:24 +0200 Subject: [PATCH 64/96] nope2 --- src/hyperwallet_client/api.rs | 40 +++++++++++++++++------------------ src/hyperwallet_client/mod.rs | 13 ++++++++---- 2 files changed, 29 insertions(+), 24 deletions(-) diff --git a/src/hyperwallet_client/api.rs b/src/hyperwallet_client/api.rs index 7c9a339..d261c31 100644 --- a/src/hyperwallet_client/api.rs +++ b/src/hyperwallet_client/api.rs @@ -22,7 +22,7 @@ pub fn create_wallet( None, None, ); - let response = execute_request(request)?; + let response = execute_request(request, our)?; serde_json::from_value(response.data.unwrap_or_default()) .map_err(HyperwalletClientError::Deserialization) } @@ -45,7 +45,7 @@ pub fn send_eth( Some(wallet_id.to_string()), chain_id, ); - let response = execute_request(request)?; + let response = execute_request(request, our)?; serde_json::from_value(response.data.unwrap_or_default()) .map_err(HyperwalletClientError::Deserialization) } @@ -65,7 +65,7 @@ pub fn get_balance( Some(wallet_id.to_string()), chain_id, ); - let response = execute_request(request)?; + let response = execute_request(request, our)?; serde_json::from_value(response.data.unwrap_or_default()) .map_err(HyperwalletClientError::Deserialization) } @@ -85,7 +85,7 @@ pub fn create_note( None, chain_id, ); - let response = execute_request(request)?; + let response = execute_request(request, our)?; Ok(response.data.unwrap_or_default()) } @@ -113,7 +113,7 @@ pub fn execute_via_tba( None, chain_id, ); - let response = execute_request(request)?; + let response = execute_request(request, our)?; Ok(response.data.unwrap_or_default()) } @@ -137,7 +137,7 @@ pub fn check_tba_ownership( None, chain_id, ); - let response = execute_request(request)?; + let response = execute_request(request, our)?; Ok(response.data.unwrap_or_default()) } @@ -157,7 +157,7 @@ pub fn unlock_wallet( Some(wallet_id.to_string()), None, ); - execute_request(request)?; + execute_request(request, our)?; Ok(()) } @@ -182,7 +182,7 @@ pub fn import_wallet( None, None, ); - let response = execute_request(request)?; + let response = execute_request(request, our)?; serde_json::from_value(response.data.unwrap_or_default()) .map_err(HyperwalletClientError::Deserialization) } @@ -200,7 +200,7 @@ pub fn list_wallets( None, None, ); - let response = execute_request(request)?; + let response = execute_request(request, our)?; serde_json::from_value(response.data.unwrap_or_default()) .map_err(HyperwalletClientError::Deserialization) } @@ -219,7 +219,7 @@ pub fn get_wallet_info( Some(wallet_id.to_string()), None, ); - let response = execute_request(request)?; + let response = execute_request(request, our)?; serde_json::from_value(response.data.unwrap_or_default()) .map_err(HyperwalletClientError::Deserialization) } @@ -238,7 +238,7 @@ pub fn delete_wallet( Some(wallet_id.to_string()), None, ); - execute_request(request)?; + execute_request(request, our)?; Ok(()) } @@ -258,7 +258,7 @@ pub fn rename_wallet( Some(wallet_id.to_string()), None, ); - execute_request(request)?; + execute_request(request, our)?; Ok(()) } @@ -278,7 +278,7 @@ pub fn set_wallet_limits( Some(wallet_id.to_string()), None, ); - execute_request(request)?; + execute_request(request, our)?; Ok(()) } @@ -305,7 +305,7 @@ pub fn send_token( Some(wallet_id.to_string()), chain_id, ); - let response = execute_request(request)?; + let response = execute_request(request, our)?; serde_json::from_value(response.data.unwrap_or_default()) .map_err(HyperwalletClientError::Deserialization) } @@ -333,7 +333,7 @@ pub fn approve_token( Some(wallet_id.to_string()), chain_id, ); - let response = execute_request(request)?; + let response = execute_request(request, our)?; serde_json::from_value(response.data.unwrap_or_default()) .map_err(HyperwalletClientError::Deserialization) } @@ -355,7 +355,7 @@ pub fn get_token_balance( Some(wallet_id.to_string()), chain_id, ); - let response = execute_request(request)?; + let response = execute_request(request, our)?; Ok(response.data.unwrap_or_default()) } @@ -380,7 +380,7 @@ pub fn build_and_sign_user_operation_for_payment( Some(wallet_id.to_string()), chain_id, ); - let response = execute_request(request)?; + let response = execute_request(request, our)?; Ok(response.data.unwrap_or_default()) } @@ -403,7 +403,7 @@ pub fn submit_user_operation( None, None, ); - let response = execute_request(request)?; + let response = execute_request(request, our)?; serde_json::from_value(response.data.unwrap_or_default()) .map_err(HyperwalletClientError::Deserialization) } @@ -423,7 +423,7 @@ pub fn get_user_operation_receipt( None, None, ); - let response = execute_request(request)?; + let response = execute_request(request, our)?; Ok(response.data.unwrap_or_default()) } @@ -443,7 +443,7 @@ pub fn resolve_identity( None, chain_id, ); - let response = execute_request(request)?; + let response = execute_request(request, our)?; Ok(response.data.unwrap_or_default()) } diff --git a/src/hyperwallet_client/mod.rs b/src/hyperwallet_client/mod.rs index 511f531..a07a06e 100644 --- a/src/hyperwallet_client/mod.rs +++ b/src/hyperwallet_client/mod.rs @@ -25,8 +25,8 @@ pub use types::{ use crate::{Address, Request}; use thiserror::Error; -/// The static address of the system's Hyperwallet service. -const HYPERWALLET_ADDRESS: &str = "hyperwallet:hyperwallet:hallman.hypr"; +/// The process identifier for the system's Hyperwallet service. +const HYPERWALLET_PROCESS: &str = "hyperwallet:hyperwallet:hallman.hypr"; /// Errors that can occur when interacting with the Hyperwallet client. #[derive(Debug, Error)] @@ -110,15 +110,20 @@ fn send_handshake_step( request_id: None, timestamp: 0, }; - execute_request(request) + execute_request(request, our) } // The lowest-level helper that handles sending all requests. pub(crate) fn execute_request( request: types::OperationRequest, + our: &Address, ) -> Result { + // Construct the full hyperwallet address using our node + let process_id: crate::ProcessId = ("hyperwallet", "hyperwallet", "hallman.hypr").into(); + let hyperwallet_address = Address::new(our.node(), process_id); + let response = Request::new() - .target(HYPERWALLET_ADDRESS.parse::
().unwrap()) + .target(hyperwallet_address) .body(serde_json::to_vec(&request).map_err(HyperwalletClientError::Serialization)?) .send_and_await_response(5) // 5s timeout .map_err(|e| HyperwalletClientError::Communication(e.into()))? From a7c93afae75721037557aa8cc9b7088db68b9ed2 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 28 Jul 2025 14:08:58 +0000 Subject: [PATCH 65/96] Format Rust code using rustfmt --- src/hyperwallet_client/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hyperwallet_client/mod.rs b/src/hyperwallet_client/mod.rs index a07a06e..28fd313 100644 --- a/src/hyperwallet_client/mod.rs +++ b/src/hyperwallet_client/mod.rs @@ -121,7 +121,7 @@ pub(crate) fn execute_request( // Construct the full hyperwallet address using our node let process_id: crate::ProcessId = ("hyperwallet", "hyperwallet", "hallman.hypr").into(); let hyperwallet_address = Address::new(our.node(), process_id); - + let response = Request::new() .target(hyperwallet_address) .body(serde_json::to_vec(&request).map_err(HyperwalletClientError::Serialization)?) From a90a3c78265ba8b93d70fa47c8978381495f965e Mon Sep 17 00:00:00 2001 From: Hallmane Date: Mon, 28 Jul 2025 16:50:08 +0200 Subject: [PATCH 66/96] nope3 --- src/hyperwallet_client/api.rs | 8 ++++++-- src/hyperwallet_client/mod.rs | 26 +++++++++++++++++++++++++- src/hyperwallet_client/types.rs | 11 +++++++++++ 3 files changed, 42 insertions(+), 3 deletions(-) diff --git a/src/hyperwallet_client/api.rs b/src/hyperwallet_client/api.rs index d261c31..7bd8406 100644 --- a/src/hyperwallet_client/api.rs +++ b/src/hyperwallet_client/api.rs @@ -148,13 +148,17 @@ pub fn unlock_wallet( wallet_id: &str, password: &str, ) -> Result<(), HyperwalletClientError> { - let params = serde_json::json!({ "password": password }); + let params = serde_json::json!({ + "session_id": session_info.session_id, + "wallet_id": wallet_id, + "password": password + }); let request = build_request( our, session_info, Operation::UnlockWallet, params, - Some(wallet_id.to_string()), + None, // Don't duplicate wallet_id in request.wallet_id None, ); execute_request(request, our)?; diff --git a/src/hyperwallet_client/mod.rs b/src/hyperwallet_client/mod.rs index a07a06e..8ed40cc 100644 --- a/src/hyperwallet_client/mod.rs +++ b/src/hyperwallet_client/mod.rs @@ -90,7 +90,31 @@ pub fn initialize( )) })?; - serde_json::from_value(complete_data).map_err(HyperwalletClientError::Deserialization) + // Extract session info from the Complete response + let session_id = complete_data["session_id"] + .as_str() + .ok_or_else(|| HyperwalletClientError::ServerError( + types::OperationError::internal_error("Missing session_id in Complete response") + ))? + .to_string(); + + let registered_permissions: ProcessPermissions = + serde_json::from_value(complete_data["registered_permissions"].clone()) + .map_err(HyperwalletClientError::Deserialization)?; + + // Get server version from the earlier welcome response + let server_version = welcome_data["server_version"] + .as_str() + .ok_or_else(|| HyperwalletClientError::ServerError( + types::OperationError::internal_error("Missing server_version in ServerWelcome response") + ))? + .to_string(); + + Ok(SessionInfo { + server_version, + session_id, + registered_permissions, + }) } // Internal helper for the handshake steps. diff --git a/src/hyperwallet_client/types.rs b/src/hyperwallet_client/types.rs index c775ee4..b70f440 100644 --- a/src/hyperwallet_client/types.rs +++ b/src/hyperwallet_client/types.rs @@ -231,15 +231,26 @@ pub struct SessionInfo { /// The steps involved in the handshake protocol. #[derive(Debug, 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)] From dc4abb1b0198cf7302e6d4b84a0bf1ab84caa12e Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 28 Jul 2025 14:51:13 +0000 Subject: [PATCH 67/96] Format Rust code using rustfmt --- src/hyperwallet_client/mod.rs | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/hyperwallet_client/mod.rs b/src/hyperwallet_client/mod.rs index ce6bc84..8a49959 100644 --- a/src/hyperwallet_client/mod.rs +++ b/src/hyperwallet_client/mod.rs @@ -93,21 +93,25 @@ pub fn initialize( // Extract session info from the Complete response let session_id = complete_data["session_id"] .as_str() - .ok_or_else(|| HyperwalletClientError::ServerError( - types::OperationError::internal_error("Missing session_id in Complete response") - ))? + .ok_or_else(|| { + HyperwalletClientError::ServerError(types::OperationError::internal_error( + "Missing session_id in Complete response", + )) + })? .to_string(); - let registered_permissions: ProcessPermissions = + let registered_permissions: ProcessPermissions = serde_json::from_value(complete_data["registered_permissions"].clone()) .map_err(HyperwalletClientError::Deserialization)?; // Get server version from the earlier welcome response let server_version = welcome_data["server_version"] .as_str() - .ok_or_else(|| HyperwalletClientError::ServerError( - types::OperationError::internal_error("Missing server_version in ServerWelcome response") - ))? + .ok_or_else(|| { + HyperwalletClientError::ServerError(types::OperationError::internal_error( + "Missing server_version in ServerWelcome response", + )) + })? .to_string(); Ok(SessionInfo { From 812c999b449eee3d7c249e37124022f8fe1f9c23 Mon Sep 17 00:00:00 2001 From: Hallmane Date: Mon, 28 Jul 2025 17:27:14 +0200 Subject: [PATCH 68/96] yes1 --- src/hyperwallet_client/api.rs | 60 ++++++++++++++++++++++++++--------- 1 file changed, 45 insertions(+), 15 deletions(-) diff --git a/src/hyperwallet_client/api.rs b/src/hyperwallet_client/api.rs index 7bd8406..d2041f1 100644 --- a/src/hyperwallet_client/api.rs +++ b/src/hyperwallet_client/api.rs @@ -1,8 +1,6 @@ //! Public API wrappers for Hyperwallet operations. -use super::types::{ - self, Balance, Operation, SessionInfo, SpendingLimits, TxReceipt, UserOperationHash, Wallet, -}; +use super::types::{self, Balance, Operation, SessionInfo, SpendingLimits, TxReceipt, Wallet}; use super::{execute_request, HyperwalletClientError}; use crate::Address; @@ -369,13 +367,32 @@ pub fn build_and_sign_user_operation_for_payment( session_info: &SessionInfo, wallet_id: &str, target: &str, - amount: &str, + call_data: &str, + value: Option<&str>, + use_paymaster: bool, + metadata: Option>, + password: Option<&str>, chain_id: Option, ) -> Result { - let params = serde_json::json!({ + let mut params = serde_json::json!({ "target": target, - "amount": amount, + "call_data": call_data, + "use_paymaster": use_paymaster, }); + + if let Some(v) = value { + params["value"] = serde_json::Value::String(v.to_string()); + } + + if let Some(pwd) = password { + params["password"] = serde_json::Value::String(pwd.to_string()); + } + + // Pass metadata through to hyperwallet + if let Some(meta) = metadata { + params["metadata"] = serde_json::Value::Object(meta); + } + let request = build_request( our, session_info, @@ -392,12 +409,15 @@ pub fn build_and_sign_user_operation_for_payment( pub fn submit_user_operation( our: &Address, session_info: &SessionInfo, - user_operation: serde_json::Value, + signed_user_operation: serde_json::Value, entry_point: &str, -) -> Result { + bundler_url: Option<&str>, + chain_id: Option, +) -> Result { let params = serde_json::json!({ - "user_operation": user_operation, + "signed_user_operation": signed_user_operation, "entry_point": entry_point, + "bundler_url": bundler_url, }); let request = build_request( our, @@ -405,27 +425,37 @@ pub fn submit_user_operation( Operation::SubmitUserOperation, params, None, - None, + chain_id, ); let response = execute_request(request, our)?; - serde_json::from_value(response.data.unwrap_or_default()) - .map_err(HyperwalletClientError::Deserialization) + + // Extract user_op_hash from response data + let data = response.data.unwrap_or_default(); + data.get("user_op_hash") + .and_then(|h| h.as_str()) + .map(|s| s.to_string()) + .ok_or_else(|| { + HyperwalletClientError::ServerError(types::OperationError::internal_error( + "Missing UserOperation hash in response", + )) + }) } /// Gets the receipt for a UserOperation. pub fn get_user_operation_receipt( our: &Address, session_info: &SessionInfo, - user_op_hash: &UserOperationHash, + user_op_hash: &str, + chain_id: Option, ) -> Result { - let params = serde_json::json!({ "user_operation_hash": user_op_hash }); + let params = serde_json::json!({ "user_op_hash": user_op_hash }); let request = build_request( our, session_info, Operation::GetUserOperationReceipt, params, None, - None, + chain_id, ); let response = execute_request(request, our)?; Ok(response.data.unwrap_or_default()) From 3e5ec416edfbb4cba8f63abe3a2fb35debc1af97 Mon Sep 17 00:00:00 2001 From: Hallmane Date: Tue, 29 Jul 2025 15:00:02 +0200 Subject: [PATCH 69/96] shoveling complexity upwards --- src/hyperwallet_client/api.rs | 204 +++++++++++++++++++++++++++++++++- src/hyperwallet_client/mod.rs | 11 +- 2 files changed, 207 insertions(+), 8 deletions(-) diff --git a/src/hyperwallet_client/api.rs b/src/hyperwallet_client/api.rs index d2041f1..8cf8f52 100644 --- a/src/hyperwallet_client/api.rs +++ b/src/hyperwallet_client/api.rs @@ -379,20 +379,82 @@ pub fn build_and_sign_user_operation_for_payment( "call_data": call_data, "use_paymaster": use_paymaster, }); - + if let Some(v) = value { params["value"] = serde_json::Value::String(v.to_string()); } - + if let Some(pwd) = password { params["password"] = serde_json::Value::String(pwd.to_string()); } - + // Pass metadata through to hyperwallet if let Some(meta) = metadata { params["metadata"] = serde_json::Value::Object(meta); } + + let request = build_request( + our, + session_info, + Operation::BuildAndSignUserOperationForPayment, + params, + Some(wallet_id.to_string()), + chain_id, + ); + let response = execute_request(request, our)?; + Ok(response.data.unwrap_or_default()) +} +/// High-level convenience function for building and signing UserOperations with TBA support. +/// This mirrors your current build_and_sign_user_operation function. +pub fn build_and_sign_user_operation( + our: &Address, + session_info: &SessionInfo, + wallet_id: &str, + target: &str, + call_data: &str, + value: Option<&str>, + use_paymaster: bool, + tba_address: Option<&str>, + password: Option<&str>, + chain_id: Option, +) -> Result { + let mut params = serde_json::json!({ + "target": target, + "call_data": call_data, + "use_paymaster": use_paymaster, + }); + + if let Some(v) = value { + params["value"] = serde_json::Value::String(v.to_string()); + } + + if let Some(pwd) = password { + params["password"] = serde_json::Value::String(pwd.to_string()); + } + + // Create metadata with Circle paymaster configuration if using paymaster + if use_paymaster { + let mut metadata = serde_json::Map::new(); + + // Always add Circle paymaster metadata for gasless transactions + // These constants should be defined somewhere accessible + metadata.insert("paymaster_address".to_string(), + serde_json::json!("0x2Ac3c1d3e24b45c6C310534Bc2Dd84B5ed576335")); // Base Circle paymaster + metadata.insert("is_circle_paymaster".to_string(), serde_json::json!(true)); + metadata.insert("paymaster_verification_gas".to_string(), + serde_json::json!("0x30000")); // 196608 + metadata.insert("paymaster_post_op_gas".to_string(), + serde_json::json!("0x20000")); // 131072 + + // Add TBA address if provided - tells hyperwallet to use TBA as sender + if let Some(tba) = tba_address { + metadata.insert("tba_address".to_string(), serde_json::json!(tba)); + } + + params["metadata"] = serde_json::Value::Object(metadata); + } + let request = build_request( our, session_info, @@ -461,6 +523,142 @@ pub fn get_user_operation_receipt( Ok(response.data.unwrap_or_default()) } +/// High-level convenience function that executes a complete payment flow. +/// This function handles: session management, building UserOp, submitting, and waiting for receipt. +pub fn execute_gasless_payment( + our: &Address, + wallet_id: &str, + target: &str, + call_data: &str, + value: Option<&str>, + tba_address: Option<&str>, + password: Option<&str>, + chain_id: Option, +) -> Result { + // Step 1: Initialize session if needed (you might want to cache this) + let session = super::initialize(our, super::HandshakeConfig::new())?; + + // Step 2: Build and sign UserOperation + let signed_data = build_and_sign_user_operation( + our, + &session, + wallet_id, + target, + call_data, + value, + true, // Always use paymaster for gasless + tba_address, + password, + chain_id, + )?; + + // Step 3: Extract signed UserOperation and entry point + let signed_user_op = signed_data.get("signed_user_operation") + .ok_or_else(|| HyperwalletClientError::ServerError( + super::types::OperationError::internal_error("Missing signed_user_operation in response") + ))? + .clone(); + + let entry_point = signed_data.get("entry_point") + .and_then(|e| e.as_str()) + .ok_or_else(|| HyperwalletClientError::ServerError( + super::types::OperationError::internal_error("Missing entry_point in response") + ))?; + + // Step 4: Submit UserOperation + let user_op_hash = submit_user_operation( + our, + &session, + signed_user_op, + entry_point, + None, // Use default bundler + chain_id, + )?; + + // Step 5: Get receipt (you might want to add polling with timeout) + let receipt = get_user_operation_receipt(our, &session, &user_op_hash, chain_id)?; + + Ok(receipt) +} + +/// Simplified gasless payment function - abstracts away all complexity. +/// This is what the operator should actually use. +pub fn build_and_sign_gasless_payment( + our: &Address, + session_info: &SessionInfo, + signer_wallet_id: &str, + tba_address: &str, + call_data: &str, + chain_id: Option, +) -> Result { + let params = serde_json::json!({ + "target": tba_address, + "call_data": call_data, + "use_paymaster": true, // Always gasless + "metadata": { + "tba_address": tba_address, + "is_circle_paymaster": true, + "paymaster_address": "0x0578cFB241215b77442a541325d6A4E6dFE700Ec", + "paymaster_verification_gas": "0x7a120", + "paymaster_post_op_gas": "0x493e0" + } + }); + + let request = build_request( + our, + session_info, + Operation::BuildAndSignUserOperationForPayment, + params, + Some(signer_wallet_id.to_string()), + chain_id, + ); + let response = execute_request(request, our)?; + Ok(response.data.unwrap_or_default()) +} + +/// Simplified submit that extracts entry point automatically from build response. +pub fn submit_gasless_payment( + our: &Address, + session_info: &SessionInfo, + signed_user_op_response: serde_json::Value, + chain_id: Option, +) -> Result { + // Extract signed UserOperation and entry point from the build response + let signed_user_op = signed_user_op_response.get("signed_user_operation") + .ok_or_else(|| HyperwalletClientError::ServerError( + super::types::OperationError::internal_error("Missing signed_user_operation in response") + ))? + .clone(); + + let entry_point = signed_user_op_response.get("entry_point") + .and_then(|e| e.as_str()) + .ok_or_else(|| HyperwalletClientError::ServerError( + super::types::OperationError::internal_error("Missing entry_point in response") + ))?; + + // Submit using the extracted data + submit_user_operation(our, session_info, signed_user_op, entry_point, None, chain_id) +} + +/// Get receipt with proper transaction hash extraction. +pub fn get_payment_receipt( + our: &Address, + session_info: &SessionInfo, + user_op_hash: &str, + chain_id: Option, +) -> Result<(String, serde_json::Value), HyperwalletClientError> { + let receipt = get_user_operation_receipt(our, session_info, user_op_hash, chain_id)?; + + // Extract transaction hash if available + let tx_hash = receipt.get("receipt") + .and_then(|r| r.get("transactionHash")) + .and_then(|h| h.as_str()) + .unwrap_or(user_op_hash) // Fallback to user op hash + .to_string(); + + Ok((tx_hash, receipt)) +} + /// Resolves an identity name to an address via Hypermap. pub fn resolve_identity( our: &Address, diff --git a/src/hyperwallet_client/mod.rs b/src/hyperwallet_client/mod.rs index 8a49959..119d44d 100644 --- a/src/hyperwallet_client/mod.rs +++ b/src/hyperwallet_client/mod.rs @@ -11,11 +11,12 @@ pub mod api; pub mod types; pub use api::{ - approve_token, build_and_sign_user_operation_for_payment, check_tba_ownership, create_note, - create_wallet, delete_wallet, execute_via_tba, get_balance, get_token_balance, - get_user_operation_receipt, get_wallet_info, import_wallet, list_wallets, rename_wallet, - resolve_identity, send_eth, send_token, set_wallet_limits, submit_user_operation, - unlock_wallet, + approve_token, build_and_sign_gasless_payment, build_and_sign_user_operation, + build_and_sign_user_operation_for_payment, check_tba_ownership, create_note, create_wallet, + delete_wallet, execute_gasless_payment, execute_via_tba, get_balance, get_payment_receipt, + get_token_balance, get_user_operation_receipt, get_wallet_info, import_wallet, list_wallets, + rename_wallet, resolve_identity, send_eth, send_token, set_wallet_limits, submit_gasless_payment, + submit_user_operation, unlock_wallet, }; pub use types::{ Balance, HandshakeConfig, Operation, OperationCategory, OperationError, ProcessPermissions, From d46c887ac44ca3cc7707f4e38d2a94f1d9eb580d Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 29 Jul 2025 13:00:30 +0000 Subject: [PATCH 70/96] Format Rust code using rustfmt --- src/hyperwallet_client/api.rs | 118 +++++++++++++++++++++------------- src/hyperwallet_client/mod.rs | 10 +-- 2 files changed, 77 insertions(+), 51 deletions(-) diff --git a/src/hyperwallet_client/api.rs b/src/hyperwallet_client/api.rs index 8cf8f52..5dc49d1 100644 --- a/src/hyperwallet_client/api.rs +++ b/src/hyperwallet_client/api.rs @@ -379,20 +379,20 @@ pub fn build_and_sign_user_operation_for_payment( "call_data": call_data, "use_paymaster": use_paymaster, }); - + if let Some(v) = value { params["value"] = serde_json::Value::String(v.to_string()); } - + if let Some(pwd) = password { params["password"] = serde_json::Value::String(pwd.to_string()); } - + // Pass metadata through to hyperwallet if let Some(meta) = metadata { params["metadata"] = serde_json::Value::Object(meta); } - + let request = build_request( our, session_info, @@ -424,37 +424,43 @@ pub fn build_and_sign_user_operation( "call_data": call_data, "use_paymaster": use_paymaster, }); - + if let Some(v) = value { params["value"] = serde_json::Value::String(v.to_string()); } - + if let Some(pwd) = password { params["password"] = serde_json::Value::String(pwd.to_string()); } - + // Create metadata with Circle paymaster configuration if using paymaster if use_paymaster { let mut metadata = serde_json::Map::new(); - + // Always add Circle paymaster metadata for gasless transactions // These constants should be defined somewhere accessible - metadata.insert("paymaster_address".to_string(), - serde_json::json!("0x2Ac3c1d3e24b45c6C310534Bc2Dd84B5ed576335")); // Base Circle paymaster + metadata.insert( + "paymaster_address".to_string(), + serde_json::json!("0x2Ac3c1d3e24b45c6C310534Bc2Dd84B5ed576335"), + ); // Base Circle paymaster metadata.insert("is_circle_paymaster".to_string(), serde_json::json!(true)); - metadata.insert("paymaster_verification_gas".to_string(), - serde_json::json!("0x30000")); // 196608 - metadata.insert("paymaster_post_op_gas".to_string(), - serde_json::json!("0x20000")); // 131072 - + metadata.insert( + "paymaster_verification_gas".to_string(), + serde_json::json!("0x30000"), + ); // 196608 + metadata.insert( + "paymaster_post_op_gas".to_string(), + serde_json::json!("0x20000"), + ); // 131072 + // Add TBA address if provided - tells hyperwallet to use TBA as sender if let Some(tba) = tba_address { metadata.insert("tba_address".to_string(), serde_json::json!(tba)); } - + params["metadata"] = serde_json::Value::Object(metadata); } - + let request = build_request( our, session_info, @@ -537,7 +543,7 @@ pub fn execute_gasless_payment( ) -> Result { // Step 1: Initialize session if needed (you might want to cache this) let session = super::initialize(our, super::HandshakeConfig::new())?; - + // Step 2: Build and sign UserOperation let signed_data = build_and_sign_user_operation( our, @@ -551,20 +557,26 @@ pub fn execute_gasless_payment( password, chain_id, )?; - + // Step 3: Extract signed UserOperation and entry point - let signed_user_op = signed_data.get("signed_user_operation") - .ok_or_else(|| HyperwalletClientError::ServerError( - super::types::OperationError::internal_error("Missing signed_user_operation in response") - ))? + let signed_user_op = signed_data + .get("signed_user_operation") + .ok_or_else(|| { + HyperwalletClientError::ServerError(super::types::OperationError::internal_error( + "Missing signed_user_operation in response", + )) + })? .clone(); - - let entry_point = signed_data.get("entry_point") + + let entry_point = signed_data + .get("entry_point") .and_then(|e| e.as_str()) - .ok_or_else(|| HyperwalletClientError::ServerError( - super::types::OperationError::internal_error("Missing entry_point in response") - ))?; - + .ok_or_else(|| { + HyperwalletClientError::ServerError(super::types::OperationError::internal_error( + "Missing entry_point in response", + )) + })?; + // Step 4: Submit UserOperation let user_op_hash = submit_user_operation( our, @@ -574,10 +586,10 @@ pub fn execute_gasless_payment( None, // Use default bundler chain_id, )?; - + // Step 5: Get receipt (you might want to add polling with timeout) let receipt = get_user_operation_receipt(our, &session, &user_op_hash, chain_id)?; - + Ok(receipt) } @@ -603,7 +615,7 @@ pub fn build_and_sign_gasless_payment( "paymaster_post_op_gas": "0x493e0" } }); - + let request = build_request( our, session_info, @@ -624,20 +636,33 @@ pub fn submit_gasless_payment( chain_id: Option, ) -> Result { // Extract signed UserOperation and entry point from the build response - let signed_user_op = signed_user_op_response.get("signed_user_operation") - .ok_or_else(|| HyperwalletClientError::ServerError( - super::types::OperationError::internal_error("Missing signed_user_operation in response") - ))? + let signed_user_op = signed_user_op_response + .get("signed_user_operation") + .ok_or_else(|| { + HyperwalletClientError::ServerError(super::types::OperationError::internal_error( + "Missing signed_user_operation in response", + )) + })? .clone(); - - let entry_point = signed_user_op_response.get("entry_point") + + let entry_point = signed_user_op_response + .get("entry_point") .and_then(|e| e.as_str()) - .ok_or_else(|| HyperwalletClientError::ServerError( - super::types::OperationError::internal_error("Missing entry_point in response") - ))?; - + .ok_or_else(|| { + HyperwalletClientError::ServerError(super::types::OperationError::internal_error( + "Missing entry_point in response", + )) + })?; + // Submit using the extracted data - submit_user_operation(our, session_info, signed_user_op, entry_point, None, chain_id) + submit_user_operation( + our, + session_info, + signed_user_op, + entry_point, + None, + chain_id, + ) } /// Get receipt with proper transaction hash extraction. @@ -648,14 +673,15 @@ pub fn get_payment_receipt( chain_id: Option, ) -> Result<(String, serde_json::Value), HyperwalletClientError> { let receipt = get_user_operation_receipt(our, session_info, user_op_hash, chain_id)?; - + // Extract transaction hash if available - let tx_hash = receipt.get("receipt") + let tx_hash = receipt + .get("receipt") .and_then(|r| r.get("transactionHash")) .and_then(|h| h.as_str()) .unwrap_or(user_op_hash) // Fallback to user op hash .to_string(); - + Ok((tx_hash, receipt)) } diff --git a/src/hyperwallet_client/mod.rs b/src/hyperwallet_client/mod.rs index 119d44d..c377bf8 100644 --- a/src/hyperwallet_client/mod.rs +++ b/src/hyperwallet_client/mod.rs @@ -11,12 +11,12 @@ pub mod api; pub mod types; pub use api::{ - approve_token, build_and_sign_gasless_payment, build_and_sign_user_operation, - build_and_sign_user_operation_for_payment, check_tba_ownership, create_note, create_wallet, + approve_token, build_and_sign_gasless_payment, build_and_sign_user_operation, + build_and_sign_user_operation_for_payment, check_tba_ownership, create_note, create_wallet, delete_wallet, execute_gasless_payment, execute_via_tba, get_balance, get_payment_receipt, - get_token_balance, get_user_operation_receipt, get_wallet_info, import_wallet, list_wallets, - rename_wallet, resolve_identity, send_eth, send_token, set_wallet_limits, submit_gasless_payment, - submit_user_operation, unlock_wallet, + get_token_balance, get_user_operation_receipt, get_wallet_info, import_wallet, list_wallets, + rename_wallet, resolve_identity, send_eth, send_token, set_wallet_limits, + submit_gasless_payment, submit_user_operation, unlock_wallet, }; pub use types::{ Balance, HandshakeConfig, Operation, OperationCategory, OperationError, ProcessPermissions, From 7e43a6538a090b8b47ebbd169a696dd9a612c57b Mon Sep 17 00:00:00 2001 From: Hallmane Date: Tue, 29 Jul 2025 15:22:22 +0200 Subject: [PATCH 71/96] even more, before removing the superfluous metadata from hyperwallet --- src/hyperwallet_client/api.rs | 166 +++++++++++++++++++++++++++++++- src/hyperwallet_client/mod.rs | 7 +- src/hyperwallet_client/types.rs | 22 +++-- 3 files changed, 184 insertions(+), 11 deletions(-) diff --git a/src/hyperwallet_client/api.rs b/src/hyperwallet_client/api.rs index 8cf8f52..82b3568 100644 --- a/src/hyperwallet_client/api.rs +++ b/src/hyperwallet_client/api.rs @@ -2,7 +2,8 @@ use super::types::{self, Balance, Operation, SessionInfo, SpendingLimits, TxReceipt, Wallet}; use super::{execute_request, HyperwalletClientError}; -use crate::Address; +use crate::{Address, wallet}; +use alloy_primitives::{Address as EthAddress, U256}; /// Creates a new wallet for the process. pub fn create_wallet( @@ -679,6 +680,169 @@ pub fn resolve_identity( Ok(response.data.unwrap_or_default()) } +/// Creates TBA execute calldata for an ERC20 transfer payment. +/// This wraps the existing wallet.rs functions to make payments simpler. +pub fn create_tba_payment_calldata( + usdc_contract: &str, + recipient_address: &str, + amount_usdc: f64, +) -> Result { + // Parse addresses + let usdc_addr = usdc_contract.parse::() + .map_err(|_| HyperwalletClientError::ServerError( + super::types::OperationError::invalid_params("Invalid USDC contract address") + ))?; + + let recipient_addr = recipient_address.parse::() + .map_err(|_| HyperwalletClientError::ServerError( + super::types::OperationError::invalid_params("Invalid recipient address") + ))?; + + // Convert USDC amount to units (6 decimals) + let amount_units = (amount_usdc * 1_000_000.0) as u128; + + // Create ERC20 transfer calldata using wallet.rs + let erc20_calldata = wallet::create_erc20_transfer_calldata(recipient_addr, U256::from(amount_units)); + + // Create TBA execute calldata using wallet.rs + let tba_calldata = wallet::create_tba_userop_calldata( + usdc_addr, // target: USDC contract + U256::ZERO, // value: 0 (no ETH transfer) + erc20_calldata, // data: ERC20 transfer calldata + 0, // operation: 0 = CALL + ); + + Ok(format!("0x{}", hex::encode(tba_calldata))) +} + +/// Complete payment function that handles everything internally. +/// The operator just needs to call this one function. +pub fn execute_complete_gasless_payment( + our: &Address, + session_info: &SessionInfo, + signer_wallet_id: &str, + tba_address: &str, + recipient_address: &str, + amount_usdc: f64, + chain_id: u64, +) -> Result { + // Step 1: Get USDC contract for the chain + let usdc_contract = match chain_id { + 8453 => "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", // Base USDC + 1 => "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", // Mainnet USDC + _ => return Err(HyperwalletClientError::ServerError( + super::types::OperationError::invalid_params(&format!("Unsupported chain ID: {}", chain_id)) + )), + }; + + // Step 2: Create payment calldata using wallet.rs functions + let tba_calldata = create_tba_payment_calldata(usdc_contract, recipient_address, amount_usdc)?; + + // Step 3: Build and sign gasless payment + let signed_data = build_and_sign_gasless_payment( + our, + session_info, + signer_wallet_id, + tba_address, + &tba_calldata, + Some(chain_id), + )?; + + // Step 4: Submit payment + let user_op_hash = submit_gasless_payment( + our, + session_info, + signed_data, + Some(chain_id), + )?; + + // Step 5: Get receipt and extract transaction hash + let (tx_hash, _receipt) = get_payment_receipt( + our, + session_info, + &user_op_hash, + Some(chain_id), + ).unwrap_or_else(|_| { + // Fallback to user op hash if receipt fails + (user_op_hash.clone(), serde_json::Value::Null) + }); + + Ok(tx_hash) +} + +/// Validates payment setup and returns the required addresses and amounts. +/// This replaces the manual validation logic in the operator. +pub fn validate_gasless_payment_setup( + tba_address: Option<&String>, + recipient_address: &str, + amount_usdc_str: &str, + chain_id: u64, +) -> Result<(String, String, String, f64), HyperwalletClientError> { + // Get USDC contract address for the chain + let usdc_contract = match chain_id { + 8453 => "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", // Base USDC + 1 => "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", // Mainnet USDC + _ => return Err(HyperwalletClientError::ServerError( + super::types::OperationError::invalid_params(&format!("Unsupported chain ID: {}", chain_id)) + )), + }; + + // Check if operator TBA is configured + let operator_tba = match tba_address { + Some(addr) => addr.clone(), + None => return Err(HyperwalletClientError::ServerError( + super::types::OperationError::invalid_params("Operator TBA not configured") + )), + }; + + // Validate recipient address format + recipient_address.parse::() + .map_err(|_| HyperwalletClientError::ServerError( + super::types::OperationError::invalid_params("Invalid recipient address") + ))?; + + // Parse and validate USDC amount + let amount_usdc = amount_usdc_str.parse::() + .map_err(|_| HyperwalletClientError::ServerError( + super::types::OperationError::invalid_params("Invalid USDC amount") + ))?; + + if amount_usdc <= 0.0 { + return Err(HyperwalletClientError::ServerError( + super::types::OperationError::invalid_params("USDC amount must be positive") + )); + } + + Ok((usdc_contract.to_string(), operator_tba, recipient_address.to_string(), amount_usdc)) +} + +/// Creates TBA execute calldata for a USDC payment. +/// This replaces the manual calldata creation logic in the operator. +pub fn create_usdc_payment_calldata( + usdc_contract: &str, + recipient_address: &str, + amount_usdc: f64, +) -> Result { + create_tba_payment_calldata(usdc_contract, recipient_address, amount_usdc) +} + +/// Extracts transaction hash from a payment receipt, with fallback logic. +/// This replaces the manual receipt parsing in the operator. +pub fn extract_payment_tx_hash( + receipt_result: Result<(String, serde_json::Value), HyperwalletClientError>, + user_op_hash_fallback: &str, +) -> String { + match receipt_result { + Ok((tx_hash, _receipt)) => { + tx_hash + } + Err(_) => { + // Fallback to user op hash if receipt fails + user_op_hash_fallback.to_string() + } + } +} + // Internal helper to reduce boilerplate in the API functions. fn build_request( our: &Address, diff --git a/src/hyperwallet_client/mod.rs b/src/hyperwallet_client/mod.rs index 119d44d..65aaeac 100644 --- a/src/hyperwallet_client/mod.rs +++ b/src/hyperwallet_client/mod.rs @@ -13,10 +13,11 @@ pub mod types; pub use api::{ approve_token, build_and_sign_gasless_payment, build_and_sign_user_operation, build_and_sign_user_operation_for_payment, check_tba_ownership, create_note, create_wallet, - delete_wallet, execute_gasless_payment, execute_via_tba, get_balance, get_payment_receipt, + create_tba_payment_calldata, create_usdc_payment_calldata, delete_wallet, execute_complete_gasless_payment, + execute_gasless_payment, execute_via_tba, extract_payment_tx_hash, get_balance, get_payment_receipt, get_token_balance, get_user_operation_receipt, get_wallet_info, import_wallet, list_wallets, - rename_wallet, resolve_identity, send_eth, send_token, set_wallet_limits, submit_gasless_payment, - submit_user_operation, unlock_wallet, + rename_wallet, resolve_identity, send_eth, send_token, set_wallet_limits, submit_gasless_payment, + submit_user_operation, unlock_wallet, validate_gasless_payment_setup, }; pub use types::{ Balance, HandshakeConfig, Operation, OperationCategory, OperationError, ProcessPermissions, diff --git a/src/hyperwallet_client/types.rs b/src/hyperwallet_client/types.rs index b70f440..9513f9a 100644 --- a/src/hyperwallet_client/types.rs +++ b/src/hyperwallet_client/types.rs @@ -286,15 +286,23 @@ pub struct OperationError { pub details: Option, } -impl OperationError { - pub fn internal_error(message: &str) -> Self { - Self { - code: ErrorCode::InternalError, - message: message.to_string(), - details: None, + impl OperationError { + pub fn internal_error(message: &str) -> Self { + Self { + code: ErrorCode::InternalError, + message: message.to_string(), + details: None, + } + } + + pub fn invalid_params(message: &str) -> Self { + Self { + code: ErrorCode::InvalidParams, + message: message.to_string(), + details: None, + } } } -} #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] pub enum ErrorCode { From e7ed1ae9ecf250a6a0b13d58cab7243263be8e11 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 29 Jul 2025 13:24:21 +0000 Subject: [PATCH 72/96] Format Rust code using rustfmt --- src/hyperwallet_client/api.rs | 145 +++++++++++++++++--------------- src/hyperwallet_client/mod.rs | 13 +-- src/hyperwallet_client/types.rs | 28 +++--- 3 files changed, 99 insertions(+), 87 deletions(-) diff --git a/src/hyperwallet_client/api.rs b/src/hyperwallet_client/api.rs index 6a83423..559e70b 100644 --- a/src/hyperwallet_client/api.rs +++ b/src/hyperwallet_client/api.rs @@ -2,7 +2,7 @@ use super::types::{self, Balance, Operation, SessionInfo, SpendingLimits, TxReceipt, Wallet}; use super::{execute_request, HyperwalletClientError}; -use crate::{Address, wallet}; +use crate::{wallet, Address}; use alloy_primitives::{Address as EthAddress, U256}; /// Creates a new wallet for the process. @@ -714,30 +714,33 @@ pub fn create_tba_payment_calldata( amount_usdc: f64, ) -> Result { // Parse addresses - let usdc_addr = usdc_contract.parse::() - .map_err(|_| HyperwalletClientError::ServerError( - super::types::OperationError::invalid_params("Invalid USDC contract address") - ))?; - - let recipient_addr = recipient_address.parse::() - .map_err(|_| HyperwalletClientError::ServerError( - super::types::OperationError::invalid_params("Invalid recipient address") - ))?; - + let usdc_addr = usdc_contract.parse::().map_err(|_| { + HyperwalletClientError::ServerError(super::types::OperationError::invalid_params( + "Invalid USDC contract address", + )) + })?; + + let recipient_addr = recipient_address.parse::().map_err(|_| { + HyperwalletClientError::ServerError(super::types::OperationError::invalid_params( + "Invalid recipient address", + )) + })?; + // Convert USDC amount to units (6 decimals) let amount_units = (amount_usdc * 1_000_000.0) as u128; - + // Create ERC20 transfer calldata using wallet.rs - let erc20_calldata = wallet::create_erc20_transfer_calldata(recipient_addr, U256::from(amount_units)); - - // Create TBA execute calldata using wallet.rs + let erc20_calldata = + wallet::create_erc20_transfer_calldata(recipient_addr, U256::from(amount_units)); + + // Create TBA execute calldata using wallet.rs let tba_calldata = wallet::create_tba_userop_calldata( - usdc_addr, // target: USDC contract - U256::ZERO, // value: 0 (no ETH transfer) - erc20_calldata, // data: ERC20 transfer calldata - 0, // operation: 0 = CALL + usdc_addr, // target: USDC contract + U256::ZERO, // value: 0 (no ETH transfer) + erc20_calldata, // data: ERC20 transfer calldata + 0, // operation: 0 = CALL ); - + Ok(format!("0x{}", hex::encode(tba_calldata))) } @@ -756,14 +759,19 @@ pub fn execute_complete_gasless_payment( let usdc_contract = match chain_id { 8453 => "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", // Base USDC 1 => "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", // Mainnet USDC - _ => return Err(HyperwalletClientError::ServerError( - super::types::OperationError::invalid_params(&format!("Unsupported chain ID: {}", chain_id)) - )), + _ => { + return Err(HyperwalletClientError::ServerError( + super::types::OperationError::invalid_params(&format!( + "Unsupported chain ID: {}", + chain_id + )), + )) + } }; - + // Step 2: Create payment calldata using wallet.rs functions let tba_calldata = create_tba_payment_calldata(usdc_contract, recipient_address, amount_usdc)?; - + // Step 3: Build and sign gasless payment let signed_data = build_and_sign_gasless_payment( our, @@ -773,26 +781,17 @@ pub fn execute_complete_gasless_payment( &tba_calldata, Some(chain_id), )?; - + // Step 4: Submit payment - let user_op_hash = submit_gasless_payment( - our, - session_info, - signed_data, - Some(chain_id), - )?; - + let user_op_hash = submit_gasless_payment(our, session_info, signed_data, Some(chain_id))?; + // Step 5: Get receipt and extract transaction hash - let (tx_hash, _receipt) = get_payment_receipt( - our, - session_info, - &user_op_hash, - Some(chain_id), - ).unwrap_or_else(|_| { - // Fallback to user op hash if receipt fails - (user_op_hash.clone(), serde_json::Value::Null) - }); - + let (tx_hash, _receipt) = get_payment_receipt(our, session_info, &user_op_hash, Some(chain_id)) + .unwrap_or_else(|_| { + // Fallback to user op hash if receipt fails + (user_op_hash.clone(), serde_json::Value::Null) + }); + Ok(tx_hash) } @@ -808,38 +807,52 @@ pub fn validate_gasless_payment_setup( let usdc_contract = match chain_id { 8453 => "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", // Base USDC 1 => "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", // Mainnet USDC - _ => return Err(HyperwalletClientError::ServerError( - super::types::OperationError::invalid_params(&format!("Unsupported chain ID: {}", chain_id)) - )), + _ => { + return Err(HyperwalletClientError::ServerError( + super::types::OperationError::invalid_params(&format!( + "Unsupported chain ID: {}", + chain_id + )), + )) + } }; - + // Check if operator TBA is configured let operator_tba = match tba_address { Some(addr) => addr.clone(), - None => return Err(HyperwalletClientError::ServerError( - super::types::OperationError::invalid_params("Operator TBA not configured") - )), + None => { + return Err(HyperwalletClientError::ServerError( + super::types::OperationError::invalid_params("Operator TBA not configured"), + )) + } }; - + // Validate recipient address format - recipient_address.parse::() - .map_err(|_| HyperwalletClientError::ServerError( - super::types::OperationError::invalid_params("Invalid recipient address") - ))?; - + recipient_address.parse::().map_err(|_| { + HyperwalletClientError::ServerError(super::types::OperationError::invalid_params( + "Invalid recipient address", + )) + })?; + // Parse and validate USDC amount - let amount_usdc = amount_usdc_str.parse::() - .map_err(|_| HyperwalletClientError::ServerError( - super::types::OperationError::invalid_params("Invalid USDC amount") - ))?; - + let amount_usdc = amount_usdc_str.parse::().map_err(|_| { + HyperwalletClientError::ServerError(super::types::OperationError::invalid_params( + "Invalid USDC amount", + )) + })?; + if amount_usdc <= 0.0 { return Err(HyperwalletClientError::ServerError( - super::types::OperationError::invalid_params("USDC amount must be positive") + super::types::OperationError::invalid_params("USDC amount must be positive"), )); } - - Ok((usdc_contract.to_string(), operator_tba, recipient_address.to_string(), amount_usdc)) + + Ok(( + usdc_contract.to_string(), + operator_tba, + recipient_address.to_string(), + amount_usdc, + )) } /// Creates TBA execute calldata for a USDC payment. @@ -859,9 +872,7 @@ pub fn extract_payment_tx_hash( user_op_hash_fallback: &str, ) -> String { match receipt_result { - Ok((tx_hash, _receipt)) => { - tx_hash - } + Ok((tx_hash, _receipt)) => tx_hash, Err(_) => { // Fallback to user op hash if receipt fails user_op_hash_fallback.to_string() diff --git a/src/hyperwallet_client/mod.rs b/src/hyperwallet_client/mod.rs index 65aaeac..e4560b7 100644 --- a/src/hyperwallet_client/mod.rs +++ b/src/hyperwallet_client/mod.rs @@ -11,12 +11,13 @@ pub mod api; pub mod types; pub use api::{ - approve_token, build_and_sign_gasless_payment, build_and_sign_user_operation, - build_and_sign_user_operation_for_payment, check_tba_ownership, create_note, create_wallet, - create_tba_payment_calldata, create_usdc_payment_calldata, delete_wallet, execute_complete_gasless_payment, - execute_gasless_payment, execute_via_tba, extract_payment_tx_hash, get_balance, get_payment_receipt, - get_token_balance, get_user_operation_receipt, get_wallet_info, import_wallet, list_wallets, - rename_wallet, resolve_identity, send_eth, send_token, set_wallet_limits, submit_gasless_payment, + approve_token, build_and_sign_gasless_payment, build_and_sign_user_operation, + build_and_sign_user_operation_for_payment, check_tba_ownership, create_note, + create_tba_payment_calldata, create_usdc_payment_calldata, create_wallet, delete_wallet, + execute_complete_gasless_payment, execute_gasless_payment, execute_via_tba, + extract_payment_tx_hash, get_balance, get_payment_receipt, get_token_balance, + get_user_operation_receipt, get_wallet_info, import_wallet, list_wallets, rename_wallet, + resolve_identity, send_eth, send_token, set_wallet_limits, submit_gasless_payment, submit_user_operation, unlock_wallet, validate_gasless_payment_setup, }; pub use types::{ diff --git a/src/hyperwallet_client/types.rs b/src/hyperwallet_client/types.rs index 9513f9a..fbe84e8 100644 --- a/src/hyperwallet_client/types.rs +++ b/src/hyperwallet_client/types.rs @@ -286,23 +286,23 @@ pub struct OperationError { pub details: Option, } - impl OperationError { - pub fn internal_error(message: &str) -> Self { - Self { - code: ErrorCode::InternalError, - message: message.to_string(), - details: None, - } +impl OperationError { + pub fn internal_error(message: &str) -> Self { + Self { + code: ErrorCode::InternalError, + message: message.to_string(), + details: None, } - - pub fn invalid_params(message: &str) -> Self { - Self { - code: ErrorCode::InvalidParams, - message: message.to_string(), - details: None, - } + } + + pub fn invalid_params(message: &str) -> Self { + Self { + code: ErrorCode::InvalidParams, + message: message.to_string(), + details: None, } } +} #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] pub enum ErrorCode { From 7bb11645f37f0f5500f07a56b4a82280c67232f3 Mon Sep 17 00:00:00 2001 From: Hallmane Date: Tue, 29 Jul 2025 15:43:35 +0200 Subject: [PATCH 73/96] u128 --- src/hyperwallet_client/api.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/hyperwallet_client/api.rs b/src/hyperwallet_client/api.rs index 6a83423..48b9f00 100644 --- a/src/hyperwallet_client/api.rs +++ b/src/hyperwallet_client/api.rs @@ -711,7 +711,7 @@ pub fn resolve_identity( pub fn create_tba_payment_calldata( usdc_contract: &str, recipient_address: &str, - amount_usdc: f64, + amount_usdc: u128, ) -> Result { // Parse addresses let usdc_addr = usdc_contract.parse::() @@ -725,7 +725,7 @@ pub fn create_tba_payment_calldata( ))?; // Convert USDC amount to units (6 decimals) - let amount_units = (amount_usdc * 1_000_000.0) as u128; + let amount_units = amount_usdc * 1_000_000; // Create ERC20 transfer calldata using wallet.rs let erc20_calldata = wallet::create_erc20_transfer_calldata(recipient_addr, U256::from(amount_units)); @@ -749,7 +749,7 @@ pub fn execute_complete_gasless_payment( signer_wallet_id: &str, tba_address: &str, recipient_address: &str, - amount_usdc: f64, + amount_usdc: u128, chain_id: u64, ) -> Result { // Step 1: Get USDC contract for the chain @@ -847,7 +847,7 @@ pub fn validate_gasless_payment_setup( pub fn create_usdc_payment_calldata( usdc_contract: &str, recipient_address: &str, - amount_usdc: f64, + amount_usdc: u128, ) -> Result { create_tba_payment_calldata(usdc_contract, recipient_address, amount_usdc) } From a4ed98dd47de989007b9996f97640a2acb8fafec Mon Sep 17 00:00:00 2001 From: Hallmane Date: Tue, 29 Jul 2025 15:44:54 +0200 Subject: [PATCH 74/96] fmt --- src/hyperwallet_client/api.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hyperwallet_client/api.rs b/src/hyperwallet_client/api.rs index 1ef6655..37594e2 100644 --- a/src/hyperwallet_client/api.rs +++ b/src/hyperwallet_client/api.rs @@ -728,7 +728,7 @@ pub fn create_tba_payment_calldata( // Convert USDC amount to units (6 decimals) let amount_units = amount_usdc * 1_000_000; - + // Create ERC20 transfer calldata using wallet.rs let erc20_calldata = wallet::create_erc20_transfer_calldata(recipient_addr, U256::from(amount_units)); From 38c473649fd1250602fdfc87480f458f7a310ffc Mon Sep 17 00:00:00 2001 From: Hallmane Date: Tue, 29 Jul 2025 16:36:24 +0200 Subject: [PATCH 75/96] cleaning w typing --- src/hyperwallet_client/api.rs | 216 +++++++++++++++----------------- src/hyperwallet_client/mod.rs | 86 +++++++++++-- src/hyperwallet_client/types.rs | 144 +++++++++++++++++++++ 3 files changed, 318 insertions(+), 128 deletions(-) diff --git a/src/hyperwallet_client/api.rs b/src/hyperwallet_client/api.rs index 37594e2..c24231b 100644 --- a/src/hyperwallet_client/api.rs +++ b/src/hyperwallet_client/api.rs @@ -1,6 +1,12 @@ //! Public API wrappers for Hyperwallet operations. -use super::types::{self, Balance, Operation, SessionInfo, SpendingLimits, TxReceipt, Wallet}; +use super::types::{ + self, ApproveTokenRequest, Balance, BuildAndSignUserOperationForPaymentRequest, + CheckTbaOwnershipRequest, CreateWalletRequest, ExecuteViaTbaRequest, GetTokenBalanceRequest, + GetUserOperationReceiptRequest, ImportWalletRequest, Operation, PaymasterConfig, + RenameWalletRequest, ResolveIdentityRequest, SendEthRequest, SendTokenRequest, SessionInfo, + SpendingLimits, SubmitUserOperationRequest, TxReceipt, UnlockWalletRequest, Wallet, +}; use super::{execute_request, HyperwalletClientError}; use crate::{wallet, Address}; use alloy_primitives::{Address as EthAddress, U256}; @@ -12,12 +18,15 @@ pub fn create_wallet( name: &str, password: Option<&str>, ) -> Result { - let params = serde_json::json!({ "name": name, "password": password }); + let typed_request = CreateWalletRequest { + name: name.to_string(), + password: password.map(|s| s.to_string()), + }; let request = build_request( our, session_info, Operation::CreateWallet, - params, + serde_json::to_value(typed_request).map_err(HyperwalletClientError::Serialization)?, None, None, ); @@ -35,12 +44,15 @@ pub fn send_eth( amount: &str, // e.g., "0.1 ETH" chain_id: Option, ) -> Result { - let params = serde_json::json!({ "to": to, "amount": amount }); + let typed_request = SendEthRequest { + to: to.to_string(), + amount: amount.to_string(), + }; let request = build_request( our, session_info, Operation::SendEth, - params, + serde_json::to_value(typed_request).map_err(HyperwalletClientError::Serialization)?, Some(wallet_id.to_string()), chain_id, ); @@ -147,16 +159,16 @@ pub fn unlock_wallet( wallet_id: &str, password: &str, ) -> Result<(), HyperwalletClientError> { - let params = serde_json::json!({ - "session_id": session_info.session_id, - "wallet_id": wallet_id, - "password": password - }); + let typed_request = UnlockWalletRequest { + session_id: session_info.session_id.clone(), + wallet_id: wallet_id.to_string(), + password: password.to_string(), + }; let request = build_request( our, session_info, Operation::UnlockWallet, - params, + serde_json::to_value(typed_request).map_err(HyperwalletClientError::Serialization)?, None, // Don't duplicate wallet_id in request.wallet_id None, ); @@ -371,34 +383,24 @@ pub fn build_and_sign_user_operation_for_payment( call_data: &str, value: Option<&str>, use_paymaster: bool, - metadata: Option>, + paymaster_config: Option, password: Option<&str>, chain_id: Option, ) -> Result { - let mut params = serde_json::json!({ - "target": target, - "call_data": call_data, - "use_paymaster": use_paymaster, - }); - - if let Some(v) = value { - params["value"] = serde_json::Value::String(v.to_string()); - } - - if let Some(pwd) = password { - params["password"] = serde_json::Value::String(pwd.to_string()); - } - - // Pass metadata through to hyperwallet - if let Some(meta) = metadata { - params["metadata"] = serde_json::Value::Object(meta); - } + let typed_request = BuildAndSignUserOperationForPaymentRequest { + target: target.to_string(), + call_data: call_data.to_string(), + value: value.map(|s| s.to_string()), + use_paymaster, + paymaster_config, + password: password.map(|s| s.to_string()), + }; let request = build_request( our, session_info, Operation::BuildAndSignUserOperationForPayment, - params, + serde_json::to_value(typed_request).map_err(HyperwalletClientError::Serialization)?, Some(wallet_id.to_string()), chain_id, ); @@ -420,53 +422,29 @@ pub fn build_and_sign_user_operation( password: Option<&str>, chain_id: Option, ) -> Result { - let mut params = serde_json::json!({ - "target": target, - "call_data": call_data, - "use_paymaster": use_paymaster, - }); - - if let Some(v) = value { - params["value"] = serde_json::Value::String(v.to_string()); - } - - if let Some(pwd) = password { - params["password"] = serde_json::Value::String(pwd.to_string()); - } - - // Create metadata with Circle paymaster configuration if using paymaster - if use_paymaster { - let mut metadata = serde_json::Map::new(); - - // Always add Circle paymaster metadata for gasless transactions - // These constants should be defined somewhere accessible - metadata.insert( - "paymaster_address".to_string(), - serde_json::json!("0x2Ac3c1d3e24b45c6C310534Bc2Dd84B5ed576335"), - ); // Base Circle paymaster - metadata.insert("is_circle_paymaster".to_string(), serde_json::json!(true)); - metadata.insert( - "paymaster_verification_gas".to_string(), - serde_json::json!("0x30000"), - ); // 196608 - metadata.insert( - "paymaster_post_op_gas".to_string(), - serde_json::json!("0x20000"), - ); // 131072 - - // Add TBA address if provided - tells hyperwallet to use TBA as sender - if let Some(tba) = tba_address { - metadata.insert("tba_address".to_string(), serde_json::json!(tba)); - } + // Create paymaster configuration if using paymaster + let paymaster_config = if use_paymaster { + let mut config = PaymasterConfig::default(); + config.tba_address = tba_address.map(|s| s.to_string()); + Some(config) + } else { + None + }; - params["metadata"] = serde_json::Value::Object(metadata); - } + let typed_request = BuildAndSignUserOperationForPaymentRequest { + target: target.to_string(), + call_data: call_data.to_string(), + value: value.map(|s| s.to_string()), + use_paymaster, + paymaster_config, + password: password.map(|s| s.to_string()), + }; let request = build_request( our, session_info, Operation::BuildAndSignUserOperationForPayment, - params, + serde_json::to_value(typed_request).map_err(HyperwalletClientError::Serialization)?, Some(wallet_id.to_string()), chain_id, ); @@ -483,16 +461,16 @@ pub fn submit_user_operation( bundler_url: Option<&str>, chain_id: Option, ) -> Result { - let params = serde_json::json!({ - "signed_user_operation": signed_user_operation, - "entry_point": entry_point, - "bundler_url": bundler_url, - }); + let typed_request = SubmitUserOperationRequest { + signed_user_operation, + entry_point: entry_point.to_string(), + bundler_url: bundler_url.map(|s| s.to_string()), + }; let request = build_request( our, session_info, Operation::SubmitUserOperation, - params, + serde_json::to_value(typed_request).map_err(HyperwalletClientError::Serialization)?, None, chain_id, ); @@ -517,12 +495,14 @@ pub fn get_user_operation_receipt( user_op_hash: &str, chain_id: Option, ) -> Result { - let params = serde_json::json!({ "user_op_hash": user_op_hash }); + let typed_request = GetUserOperationReceiptRequest { + user_op_hash: user_op_hash.to_string(), + }; let request = build_request( our, session_info, Operation::GetUserOperationReceipt, - params, + serde_json::to_value(typed_request).map_err(HyperwalletClientError::Serialization)?, None, chain_id, ); @@ -594,6 +574,43 @@ pub fn execute_gasless_payment( Ok(receipt) } +/// Creates TBA execute calldata for an ERC20 transfer payment. +/// This wraps the existing wallet.rs functions to make payments simpler. +pub fn create_tba_payment_calldata( + usdc_contract: &str, + recipient_address: &str, + amount_usdc: u128, +) -> Result { + // Parse addresses + let usdc_addr = usdc_contract.parse::().map_err(|_| { + HyperwalletClientError::ServerError(super::types::OperationError::invalid_params( + "Invalid USDC contract address", + )) + })?; + + let recipient_addr = recipient_address.parse::().map_err(|_| { + HyperwalletClientError::ServerError(super::types::OperationError::invalid_params( + "Invalid recipient address", + )) + })?; + + // Convert USDC amount to units (6 decimals) + let amount_units = amount_usdc * 1_000_000; + + // Create ERC20 transfer calldata using wallet.rs + let erc20_calldata = wallet::create_erc20_transfer_calldata(recipient_addr, U256::from(amount_units)); + + // Create TBA execute calldata using wallet.rs + let tba_calldata = wallet::create_tba_userop_calldata( + usdc_addr, // target: USDC contract + U256::ZERO, // value: 0 (no ETH transfer) + erc20_calldata, // data: ERC20 transfer calldata + 0, // operation: 0 = CALL + ); + + Ok(format!("0x{}", hex::encode(tba_calldata))) +} + /// Simplified gasless payment function - abstracts away all complexity. /// This is what the operator should actually use. pub fn build_and_sign_gasless_payment( @@ -608,7 +625,7 @@ pub fn build_and_sign_gasless_payment( "target": tba_address, "call_data": call_data, "use_paymaster": true, // Always gasless - "metadata": { + "paymaster_config": { "tba_address": tba_address, "is_circle_paymaster": true, "paymaster_address": "0x0578cFB241215b77442a541325d6A4E6dFE700Ec", @@ -706,43 +723,6 @@ pub fn resolve_identity( Ok(response.data.unwrap_or_default()) } -/// Creates TBA execute calldata for an ERC20 transfer payment. -/// This wraps the existing wallet.rs functions to make payments simpler. -pub fn create_tba_payment_calldata( - usdc_contract: &str, - recipient_address: &str, - amount_usdc: u128, -) -> Result { - // Parse addresses - let usdc_addr = usdc_contract.parse::().map_err(|_| { - HyperwalletClientError::ServerError(super::types::OperationError::invalid_params( - "Invalid USDC contract address", - )) - })?; - - let recipient_addr = recipient_address.parse::().map_err(|_| { - HyperwalletClientError::ServerError(super::types::OperationError::invalid_params( - "Invalid recipient address", - )) - })?; - - // Convert USDC amount to units (6 decimals) - let amount_units = amount_usdc * 1_000_000; - - // Create ERC20 transfer calldata using wallet.rs - let erc20_calldata = - wallet::create_erc20_transfer_calldata(recipient_addr, U256::from(amount_units)); - - // Create TBA execute calldata using wallet.rs - let tba_calldata = wallet::create_tba_userop_calldata( - usdc_addr, // target: USDC contract - U256::ZERO, // value: 0 (no ETH transfer) - erc20_calldata, // data: ERC20 transfer calldata - 0, // operation: 0 = CALL - ); - - Ok(format!("0x{}", hex::encode(tba_calldata))) -} /// Complete payment function that handles everything internally. /// The operator just needs to call this one function. diff --git a/src/hyperwallet_client/mod.rs b/src/hyperwallet_client/mod.rs index e4560b7..faf6ad9 100644 --- a/src/hyperwallet_client/mod.rs +++ b/src/hyperwallet_client/mod.rs @@ -11,18 +11,84 @@ pub mod api; pub mod types; pub use api::{ - approve_token, build_and_sign_gasless_payment, build_and_sign_user_operation, - build_and_sign_user_operation_for_payment, check_tba_ownership, create_note, - create_tba_payment_calldata, create_usdc_payment_calldata, create_wallet, delete_wallet, - execute_complete_gasless_payment, execute_gasless_payment, execute_via_tba, - extract_payment_tx_hash, get_balance, get_payment_receipt, get_token_balance, - get_user_operation_receipt, get_wallet_info, import_wallet, list_wallets, rename_wallet, - resolve_identity, send_eth, send_token, set_wallet_limits, submit_gasless_payment, - submit_user_operation, unlock_wallet, validate_gasless_payment_setup, + // Wallet Management + create_wallet, + import_wallet, + unlock_wallet, + delete_wallet, + rename_wallet, + set_wallet_limits, + list_wallets, + get_wallet_info, + + // Balance and Token Operations + get_balance, + get_token_balance, + approve_token, + send_eth, + send_token, + + // TBA (Token Bound Account) Operations + check_tba_ownership, + execute_via_tba, + create_tba_payment_calldata, + create_usdc_payment_calldata, + + // User Operation Building and Submission + build_and_sign_user_operation, + build_and_sign_user_operation_for_payment, + submit_user_operation, + get_user_operation_receipt, + + // Gasless Payment Operations + build_and_sign_gasless_payment, + submit_gasless_payment, + get_payment_receipt, + execute_gasless_payment, + execute_complete_gasless_payment, + validate_gasless_payment_setup, + extract_payment_tx_hash, + + // Miscellaneous + create_note, + resolve_identity, }; pub use types::{ - Balance, HandshakeConfig, Operation, OperationCategory, OperationError, ProcessPermissions, - SessionInfo, SpendingLimits, TxReceipt, UserOperationHash, Wallet, + // Request Types + ApproveTokenRequest, + CheckTbaOwnershipRequest, + CreateWalletRequest, + ExecuteViaTbaRequest, + GetTokenBalanceRequest, + GetUserOperationReceiptRequest, + ImportWalletRequest, + RenameWalletRequest, + ResolveIdentityRequest, + SendEthRequest, + SendTokenRequest, + SubmitUserOperationRequest, + UnlockWalletRequest, + + // Response Types + BuildAndSignUserOperationResponse, + SubmitUserOperationResponse, + TxReceipt, + + // Configuration and Data Types + Balance, + BuildAndSignUserOperationForPaymentRequest, + HandshakeConfig, + PaymasterConfig, + ProcessPermissions, + SessionInfo, + SpendingLimits, + UserOperationHash, + Wallet, + + // Operation and Error Types + Operation, + OperationCategory, + OperationError, }; use crate::{Address, Request}; diff --git a/src/hyperwallet_client/types.rs b/src/hyperwallet_client/types.rs index fbe84e8..e322f59 100644 --- a/src/hyperwallet_client/types.rs +++ b/src/hyperwallet_client/types.rs @@ -367,3 +367,147 @@ pub struct Balance { pub formatted: String, pub raw: String, // U256 as string } + +// === Operation-Specific Request/Response Types === + +/// Configuration for Circle paymaster (gasless transactions) +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct PaymasterConfig { + pub tba_address: Option, + 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 { + tba_address: None, + is_circle_paymaster: true, + paymaster_address: "0x0578cFB241215b77442a541325d6A4E6dFE700Ec".to_string(), // Base Circle paymaster + paymaster_verification_gas: "0x7a120".to_string(), // 500000 + paymaster_post_op_gas: "0x493e0".to_string(), // 300000 + } + } +} + +/// Request for building and signing a UserOperation for payment +#[derive(Debug, Serialize, Deserialize)] +pub struct BuildAndSignUserOperationForPaymentRequest { + pub target: String, + pub call_data: String, + pub value: Option, + pub use_paymaster: bool, + pub paymaster_config: Option, + pub password: Option, +} + +/// Response from building and signing a UserOperation +#[derive(Debug, Serialize, Deserialize)] +pub struct BuildAndSignUserOperationResponse { + pub signed_user_operation: serde_json::Value, + pub entry_point: String, + pub chain_id: u64, + pub ready_to_submit: bool, +} + +/// Request for submitting a UserOperation +#[derive(Debug, Serialize, Deserialize)] +pub struct SubmitUserOperationRequest { + pub signed_user_operation: serde_json::Value, + pub entry_point: String, + pub bundler_url: Option, +} + +/// Response from submitting a UserOperation +#[derive(Debug, Serialize, Deserialize)] +pub struct SubmitUserOperationResponse { + pub user_op_hash: String, +} + +/// Request for getting UserOperation receipt +#[derive(Debug, Serialize, Deserialize)] +pub struct GetUserOperationReceiptRequest { + pub user_op_hash: String, +} + +/// Request for creating a wallet +#[derive(Debug, Serialize, Deserialize)] +pub struct CreateWalletRequest { + pub name: String, + pub password: Option, +} + +/// Request for unlocking a wallet +#[derive(Debug, Serialize, Deserialize)] +pub struct UnlockWalletRequest { + pub session_id: String, + pub wallet_id: String, + pub password: String, +} + +/// Request for importing a wallet +#[derive(Debug, Serialize, Deserialize)] +pub struct ImportWalletRequest { + pub name: String, + pub private_key: String, + pub password: Option, +} + +/// Request for renaming a wallet +#[derive(Debug, Serialize, Deserialize)] +pub struct RenameWalletRequest { + pub new_name: String, +} + +/// Request for sending ETH +#[derive(Debug, Serialize, Deserialize)] +pub struct SendEthRequest { + pub to: String, + pub amount: String, +} + +/// Request for sending tokens +#[derive(Debug, Serialize, Deserialize)] +pub struct SendTokenRequest { + pub token_address: String, + pub to: String, + pub amount: String, +} + +/// Request for approving tokens +#[derive(Debug, Serialize, Deserialize)] +pub struct ApproveTokenRequest { + pub token_address: String, + pub spender: String, + pub amount: String, +} + +/// Request for getting token balance +#[derive(Debug, Serialize, Deserialize)] +pub struct GetTokenBalanceRequest { + pub token_address: String, +} + +/// Request for executing via TBA +#[derive(Debug, Serialize, Deserialize)] +pub struct ExecuteViaTbaRequest { + pub tba_address: String, + pub target: String, + pub call_data: String, + pub value: Option, +} + +/// Request for checking TBA ownership +#[derive(Debug, Serialize, Deserialize)] +pub struct CheckTbaOwnershipRequest { + pub tba_address: String, + pub signer_address: String, +} + +/// Request for resolving identity +#[derive(Debug, Serialize, Deserialize)] +pub struct ResolveIdentityRequest { + pub entry_name: String, +} From bbbde5fc1493e1ed0bd005f16decf4196f8f6117 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 29 Jul 2025 14:36:52 +0000 Subject: [PATCH 76/96] Format Rust code using rustfmt --- src/hyperwallet_client/api.rs | 4 +- src/hyperwallet_client/mod.rs | 97 ++++++++++++++++----------------- src/hyperwallet_client/types.rs | 4 +- 3 files changed, 52 insertions(+), 53 deletions(-) diff --git a/src/hyperwallet_client/api.rs b/src/hyperwallet_client/api.rs index c24231b..34faebc 100644 --- a/src/hyperwallet_client/api.rs +++ b/src/hyperwallet_client/api.rs @@ -598,7 +598,8 @@ pub fn create_tba_payment_calldata( let amount_units = amount_usdc * 1_000_000; // Create ERC20 transfer calldata using wallet.rs - let erc20_calldata = wallet::create_erc20_transfer_calldata(recipient_addr, U256::from(amount_units)); + let erc20_calldata = + wallet::create_erc20_transfer_calldata(recipient_addr, U256::from(amount_units)); // Create TBA execute calldata using wallet.rs let tba_calldata = wallet::create_tba_userop_calldata( @@ -723,7 +724,6 @@ pub fn resolve_identity( Ok(response.data.unwrap_or_default()) } - /// Complete payment function that handles everything internally. /// The operator just needs to call this one function. pub fn execute_complete_gasless_payment( diff --git a/src/hyperwallet_client/mod.rs b/src/hyperwallet_client/mod.rs index faf6ad9..1c79c21 100644 --- a/src/hyperwallet_client/mod.rs +++ b/src/hyperwallet_client/mod.rs @@ -11,84 +11,83 @@ pub mod api; pub mod types; pub use api::{ + approve_token, + // Gasless Payment Operations + build_and_sign_gasless_payment, + // User Operation Building and Submission + build_and_sign_user_operation, + build_and_sign_user_operation_for_payment, + // TBA (Token Bound Account) Operations + check_tba_ownership, + // Miscellaneous + create_note, + create_tba_payment_calldata, + create_usdc_payment_calldata, + // Wallet Management create_wallet, - import_wallet, - unlock_wallet, delete_wallet, - rename_wallet, - set_wallet_limits, - list_wallets, - get_wallet_info, - + execute_complete_gasless_payment, + execute_gasless_payment, + execute_via_tba, + extract_payment_tx_hash, + // Balance and Token Operations get_balance, + get_payment_receipt, get_token_balance, - approve_token, + get_user_operation_receipt, + + get_wallet_info, + + import_wallet, + list_wallets, + rename_wallet, + resolve_identity, send_eth, send_token, - - // TBA (Token Bound Account) Operations - check_tba_ownership, - execute_via_tba, - create_tba_payment_calldata, - create_usdc_payment_calldata, - - // User Operation Building and Submission - build_and_sign_user_operation, - build_and_sign_user_operation_for_payment, - submit_user_operation, - get_user_operation_receipt, - - // Gasless Payment Operations - build_and_sign_gasless_payment, + + set_wallet_limits, submit_gasless_payment, - get_payment_receipt, - execute_gasless_payment, - execute_complete_gasless_payment, + submit_user_operation, + unlock_wallet, validate_gasless_payment_setup, - extract_payment_tx_hash, - - // Miscellaneous - create_note, - resolve_identity, }; pub use types::{ // Request Types ApproveTokenRequest, + // Configuration and Data Types + Balance, + BuildAndSignUserOperationForPaymentRequest, + // Response Types + BuildAndSignUserOperationResponse, CheckTbaOwnershipRequest, CreateWalletRequest, ExecuteViaTbaRequest, GetTokenBalanceRequest, GetUserOperationReceiptRequest, + HandshakeConfig, ImportWalletRequest, + // Operation and Error Types + Operation, + OperationCategory, + OperationError, + PaymasterConfig, + ProcessPermissions, RenameWalletRequest, ResolveIdentityRequest, SendEthRequest, SendTokenRequest, + SessionInfo, + SpendingLimits, SubmitUserOperationRequest, - UnlockWalletRequest, - - // Response Types - BuildAndSignUserOperationResponse, SubmitUserOperationResponse, TxReceipt, - - // Configuration and Data Types - Balance, - BuildAndSignUserOperationForPaymentRequest, - HandshakeConfig, - PaymasterConfig, - ProcessPermissions, - SessionInfo, - SpendingLimits, + + UnlockWalletRequest, + UserOperationHash, Wallet, - - // Operation and Error Types - Operation, - OperationCategory, - OperationError, }; use crate::{Address, Request}; diff --git a/src/hyperwallet_client/types.rs b/src/hyperwallet_client/types.rs index e322f59..909338e 100644 --- a/src/hyperwallet_client/types.rs +++ b/src/hyperwallet_client/types.rs @@ -386,8 +386,8 @@ impl Default for PaymasterConfig { tba_address: None, is_circle_paymaster: true, paymaster_address: "0x0578cFB241215b77442a541325d6A4E6dFE700Ec".to_string(), // Base Circle paymaster - paymaster_verification_gas: "0x7a120".to_string(), // 500000 - paymaster_post_op_gas: "0x493e0".to_string(), // 300000 + paymaster_verification_gas: "0x7a120".to_string(), // 500000 + paymaster_post_op_gas: "0x493e0".to_string(), // 300000 } } } From 97e19e625b48face1f6c009588f30ecd8fd0e0b7 Mon Sep 17 00:00:00 2001 From: Hallmane Date: Tue, 29 Jul 2025 20:24:36 +0200 Subject: [PATCH 77/96] thinner api --- src/hyperwallet_client/api.rs | 740 +++++++++----------------------- src/hyperwallet_client/mod.rs | 190 ++++---- src/hyperwallet_client/types.rs | 334 ++++++++------ 3 files changed, 481 insertions(+), 783 deletions(-) diff --git a/src/hyperwallet_client/api.rs b/src/hyperwallet_client/api.rs index 34faebc..850ed1a 100644 --- a/src/hyperwallet_client/api.rs +++ b/src/hyperwallet_client/api.rs @@ -1,482 +1,232 @@ -//! Public API wrappers for Hyperwallet operations. +//! Public API wrappers for Hyperwallet operations using clean typed messaging. +//! +//! All functions take specific HyperwalletRequest objects for perfect type safety. +//! Users construct the exact request type needed and get compile-time verification. use super::types::{ self, ApproveTokenRequest, Balance, BuildAndSignUserOperationForPaymentRequest, - CheckTbaOwnershipRequest, CreateWalletRequest, ExecuteViaTbaRequest, GetTokenBalanceRequest, - GetUserOperationReceiptRequest, ImportWalletRequest, Operation, PaymasterConfig, - RenameWalletRequest, ResolveIdentityRequest, SendEthRequest, SendTokenRequest, SessionInfo, - SpendingLimits, SubmitUserOperationRequest, TxReceipt, UnlockWalletRequest, Wallet, + CheckTbaOwnershipRequest, CreateWalletRequest, ExecuteViaTbaRequest, ExportWalletRequest, + ExportWalletResponse, GetTokenBalanceRequest, GetUserOperationReceiptRequest, + HyperwalletMessage, HyperwalletRequest, HyperwalletResponse, ImportWalletRequest, + ListWalletsResponse, PaymasterConfig, RenameWalletRequest, ResolveIdentityRequest, + SendEthRequest, SendTokenRequest, SpendingLimits, SubmitUserOperationRequest, TxReceipt, + UnlockWalletRequest, Wallet, }; -use super::{execute_request, HyperwalletClientError}; +use super::HyperwalletClientError; use crate::{wallet, Address}; use alloy_primitives::{Address as EthAddress, U256}; /// Creates a new wallet for the process. pub fn create_wallet( our: &Address, - session_info: &SessionInfo, - name: &str, - password: Option<&str>, + request: HyperwalletRequest, ) -> Result { - let typed_request = CreateWalletRequest { - name: name.to_string(), - password: password.map(|s| s.to_string()), - }; - let request = build_request( - our, - session_info, - Operation::CreateWallet, - serde_json::to_value(typed_request).map_err(HyperwalletClientError::Serialization)?, - None, - None, - ); - let response = execute_request(request, our)?; - serde_json::from_value(response.data.unwrap_or_default()) - .map_err(HyperwalletClientError::Deserialization) + let message = HyperwalletMessage::CreateWallet(request); + let response: HyperwalletResponse = super::send_message(message, our)?; + response.data.ok_or_else(|| { + HyperwalletClientError::ServerError(types::OperationError::internal_error( + "Missing wallet data in response", + )) + }) } /// Sends ETH from a managed wallet. pub fn send_eth( our: &Address, - session_info: &SessionInfo, - wallet_id: &str, - to: &str, - amount: &str, // e.g., "0.1 ETH" - chain_id: Option, + request: HyperwalletRequest, ) -> Result { - let typed_request = SendEthRequest { - to: to.to_string(), - amount: amount.to_string(), - }; - let request = build_request( - our, - session_info, - Operation::SendEth, - serde_json::to_value(typed_request).map_err(HyperwalletClientError::Serialization)?, - Some(wallet_id.to_string()), - chain_id, - ); - let response = execute_request(request, our)?; - serde_json::from_value(response.data.unwrap_or_default()) - .map_err(HyperwalletClientError::Deserialization) + let message = HyperwalletMessage::SendEth(request); + let response: HyperwalletResponse = super::send_message(message, our)?; + response.data.ok_or_else(|| { + HyperwalletClientError::ServerError(types::OperationError::internal_error( + "Missing transaction receipt in response", + )) + }) } /// Retrieves the native balance of a managed wallet. pub fn get_balance( our: &Address, - session_info: &SessionInfo, - wallet_id: &str, - chain_id: Option, + request: HyperwalletRequest<()>, ) -> Result { - let request = build_request( - our, - session_info, - Operation::GetBalance, - serde_json::Value::Null, - Some(wallet_id.to_string()), - chain_id, - ); - let response = execute_request(request, our)?; - serde_json::from_value(response.data.unwrap_or_default()) - .map_err(HyperwalletClientError::Deserialization) + let message = HyperwalletMessage::GetBalance(request); + let response: HyperwalletResponse = super::send_message(message, our)?; + response.data.ok_or_else(|| { + HyperwalletClientError::ServerError(types::OperationError::internal_error( + "Missing balance data in response", + )) + }) } /// Creates a note in the hypermap. pub fn create_note( our: &Address, - session_info: &SessionInfo, - note_data: serde_json::Value, - chain_id: Option, + request: HyperwalletRequest, ) -> Result { - let request = build_request( - our, - session_info, - Operation::CreateNote, - note_data, - None, - chain_id, - ); - let response = execute_request(request, our)?; + let message = HyperwalletMessage::CreateNote(request); + let response: HyperwalletResponse = super::send_message(message, our)?; Ok(response.data.unwrap_or_default()) } /// Executes a transaction via Token Bound Account (TBA). pub fn execute_via_tba( our: &Address, - session_info: &SessionInfo, - tba_address: &str, - target: &str, - call_data: &[u8], - value: Option<&str>, - chain_id: Option, + request: HyperwalletRequest, ) -> Result { - let params = serde_json::json!({ - "tba_address": tba_address, - "target": target, - "call_data": format!("0x{}", hex::encode(call_data)), - "value": value, - }); - let request = build_request( - our, - session_info, - Operation::ExecuteViaTba, - params, - None, - chain_id, - ); - let response = execute_request(request, our)?; + let message = HyperwalletMessage::ExecuteViaTba(request); + let response: HyperwalletResponse = super::send_message(message, our)?; Ok(response.data.unwrap_or_default()) } /// Checks TBA ownership. pub fn check_tba_ownership( our: &Address, - session_info: &SessionInfo, - tba_address: &str, - signer_address: &str, - chain_id: Option, + request: HyperwalletRequest, ) -> Result { - let params = serde_json::json!({ - "tba_address": tba_address, - "signer_address": signer_address, - }); - let request = build_request( - our, - session_info, - Operation::CheckTbaOwnership, - params, - None, - chain_id, - ); - let response = execute_request(request, our)?; + let message = HyperwalletMessage::CheckTbaOwnership(request); + let response: HyperwalletResponse = super::send_message(message, our)?; Ok(response.data.unwrap_or_default()) } /// Unlocks a wallet with the provided password. pub fn unlock_wallet( our: &Address, - session_info: &SessionInfo, - wallet_id: &str, - password: &str, + request: HyperwalletRequest, ) -> Result<(), HyperwalletClientError> { - let typed_request = UnlockWalletRequest { - session_id: session_info.session_id.clone(), - wallet_id: wallet_id.to_string(), - password: password.to_string(), - }; - let request = build_request( - our, - session_info, - Operation::UnlockWallet, - serde_json::to_value(typed_request).map_err(HyperwalletClientError::Serialization)?, - None, // Don't duplicate wallet_id in request.wallet_id - None, - ); - execute_request(request, our)?; + let message = HyperwalletMessage::UnlockWallet(request); + let _response: HyperwalletResponse<()> = super::send_message(message, our)?; Ok(()) } /// Imports a wallet from a private key. pub fn import_wallet( our: &Address, - session_info: &SessionInfo, - name: &str, - private_key: &str, - password: Option<&str>, + request: HyperwalletRequest, ) -> Result { - let params = serde_json::json!({ - "name": name, - "private_key": private_key, - "password": password, - }); - let request = build_request( - our, - session_info, - Operation::ImportWallet, - params, - None, - None, - ); - let response = execute_request(request, our)?; - serde_json::from_value(response.data.unwrap_or_default()) - .map_err(HyperwalletClientError::Deserialization) + let message = HyperwalletMessage::ImportWallet(request); + let response: HyperwalletResponse = super::send_message(message, our)?; + response.data.ok_or_else(|| { + HyperwalletClientError::ServerError(types::OperationError::internal_error( + "Missing wallet data in response", + )) + }) } /// Lists all wallets accessible to the process. pub fn list_wallets( our: &Address, - session_info: &SessionInfo, + request: HyperwalletRequest<()>, ) -> Result, HyperwalletClientError> { - let request = build_request( - our, - session_info, - Operation::ListWallets, - serde_json::Value::Null, - None, - None, - ); - let response = execute_request(request, our)?; - serde_json::from_value(response.data.unwrap_or_default()) - .map_err(HyperwalletClientError::Deserialization) + let message = HyperwalletMessage::ListWallets(request); + let response: HyperwalletResponse = super::send_message(message, our)?; + let list_response = response.data.ok_or_else(|| { + HyperwalletClientError::ServerError(types::OperationError::internal_error( + "Missing wallet list in response", + )) + })?; + + Ok(list_response.wallets) } /// Gets detailed information about a specific wallet. pub fn get_wallet_info( our: &Address, - session_info: &SessionInfo, - wallet_id: &str, + request: HyperwalletRequest<()>, ) -> Result { - let request = build_request( - our, - session_info, - Operation::GetWalletInfo, - serde_json::Value::Null, - Some(wallet_id.to_string()), - None, - ); - let response = execute_request(request, our)?; - serde_json::from_value(response.data.unwrap_or_default()) - .map_err(HyperwalletClientError::Deserialization) + let message = HyperwalletMessage::GetWalletInfo(request); + let response: HyperwalletResponse = super::send_message(message, our)?; + response.data.ok_or_else(|| { + HyperwalletClientError::ServerError(types::OperationError::internal_error( + "Missing wallet data in response", + )) + }) } /// Deletes a wallet permanently. pub fn delete_wallet( our: &Address, - session_info: &SessionInfo, - wallet_id: &str, + request: HyperwalletRequest<()>, ) -> Result<(), HyperwalletClientError> { - let request = build_request( - our, - session_info, - Operation::DeleteWallet, - serde_json::Value::Null, - Some(wallet_id.to_string()), - None, - ); - execute_request(request, our)?; + let message = HyperwalletMessage::DeleteWallet(request); + let _response: HyperwalletResponse<()> = super::send_message(message, our)?; Ok(()) } /// Renames a wallet. pub fn rename_wallet( our: &Address, - session_info: &SessionInfo, - wallet_id: &str, - new_name: &str, + request: HyperwalletRequest, ) -> Result<(), HyperwalletClientError> { - let params = serde_json::json!({ "new_name": new_name }); - let request = build_request( - our, - session_info, - Operation::RenameWallet, - params, - Some(wallet_id.to_string()), - None, - ); - execute_request(request, our)?; + let message = HyperwalletMessage::RenameWallet(request); + let _response: HyperwalletResponse<()> = super::send_message(message, our)?; Ok(()) } /// Sets spending limits for a wallet. pub fn set_wallet_limits( our: &Address, - session_info: &SessionInfo, - wallet_id: &str, - limits: SpendingLimits, + request: HyperwalletRequest, ) -> Result<(), HyperwalletClientError> { - let params = serde_json::to_value(limits).map_err(HyperwalletClientError::Serialization)?; - let request = build_request( - our, - session_info, - Operation::SetWalletLimits, - params, - Some(wallet_id.to_string()), - None, - ); - execute_request(request, our)?; + let message = HyperwalletMessage::SetWalletLimits(request); + let _response: HyperwalletResponse<()> = super::send_message(message, our)?; Ok(()) } /// Sends tokens from a managed wallet. pub fn send_token( our: &Address, - session_info: &SessionInfo, - wallet_id: &str, - token_address: &str, - to: &str, - amount: &str, - chain_id: Option, + request: HyperwalletRequest, ) -> Result { - let params = serde_json::json!({ - "token_address": token_address, - "to": to, - "amount": amount, - }); - let request = build_request( - our, - session_info, - Operation::SendToken, - params, - Some(wallet_id.to_string()), - chain_id, - ); - let response = execute_request(request, our)?; - serde_json::from_value(response.data.unwrap_or_default()) - .map_err(HyperwalletClientError::Deserialization) + let message = HyperwalletMessage::SendToken(request); + let response: HyperwalletResponse = super::send_message(message, our)?; + response.data.ok_or_else(|| { + HyperwalletClientError::ServerError(types::OperationError::internal_error( + "Missing transaction receipt in response", + )) + }) } /// Approves a spender to transfer tokens from a managed wallet. pub fn approve_token( our: &Address, - session_info: &SessionInfo, - wallet_id: &str, - token_address: &str, - spender: &str, - amount: &str, - chain_id: Option, + request: HyperwalletRequest, ) -> Result { - let params = serde_json::json!({ - "token_address": token_address, - "spender": spender, - "amount": amount, - }); - let request = build_request( - our, - session_info, - Operation::ApproveToken, - params, - Some(wallet_id.to_string()), - chain_id, - ); - let response = execute_request(request, our)?; - serde_json::from_value(response.data.unwrap_or_default()) - .map_err(HyperwalletClientError::Deserialization) + let message = HyperwalletMessage::ApproveToken(request); + let response: HyperwalletResponse = super::send_message(message, our)?; + response.data.ok_or_else(|| { + HyperwalletClientError::ServerError(types::OperationError::internal_error( + "Missing transaction receipt in response", + )) + }) } /// Gets the token balance for a wallet. pub fn get_token_balance( our: &Address, - session_info: &SessionInfo, - wallet_id: &str, - token_address: &str, - chain_id: Option, -) -> Result { - let params = serde_json::json!({ "token_address": token_address }); - let request = build_request( - our, - session_info, - Operation::GetTokenBalance, - params, - Some(wallet_id.to_string()), - chain_id, - ); - let response = execute_request(request, our)?; - Ok(response.data.unwrap_or_default()) -} - -/// Builds and signs a UserOperation for payment via ERC-4337. -pub fn build_and_sign_user_operation_for_payment( - our: &Address, - session_info: &SessionInfo, - wallet_id: &str, - target: &str, - call_data: &str, - value: Option<&str>, - use_paymaster: bool, - paymaster_config: Option, - password: Option<&str>, - chain_id: Option, + request: HyperwalletRequest, ) -> Result { - let typed_request = BuildAndSignUserOperationForPaymentRequest { - target: target.to_string(), - call_data: call_data.to_string(), - value: value.map(|s| s.to_string()), - use_paymaster, - paymaster_config, - password: password.map(|s| s.to_string()), - }; - - let request = build_request( - our, - session_info, - Operation::BuildAndSignUserOperationForPayment, - serde_json::to_value(typed_request).map_err(HyperwalletClientError::Serialization)?, - Some(wallet_id.to_string()), - chain_id, - ); - let response = execute_request(request, our)?; + let message = HyperwalletMessage::GetTokenBalance(request); + let response: HyperwalletResponse = super::send_message(message, our)?; Ok(response.data.unwrap_or_default()) } /// High-level convenience function for building and signing UserOperations with TBA support. -/// This mirrors your current build_and_sign_user_operation function. pub fn build_and_sign_user_operation( our: &Address, - session_info: &SessionInfo, - wallet_id: &str, - target: &str, - call_data: &str, - value: Option<&str>, - use_paymaster: bool, - tba_address: Option<&str>, - password: Option<&str>, - chain_id: Option, + request: HyperwalletRequest, ) -> Result { - // Create paymaster configuration if using paymaster - let paymaster_config = if use_paymaster { - let mut config = PaymasterConfig::default(); - config.tba_address = tba_address.map(|s| s.to_string()); - Some(config) - } else { - None - }; - - let typed_request = BuildAndSignUserOperationForPaymentRequest { - target: target.to_string(), - call_data: call_data.to_string(), - value: value.map(|s| s.to_string()), - use_paymaster, - paymaster_config, - password: password.map(|s| s.to_string()), - }; - - let request = build_request( - our, - session_info, - Operation::BuildAndSignUserOperationForPayment, - serde_json::to_value(typed_request).map_err(HyperwalletClientError::Serialization)?, - Some(wallet_id.to_string()), - chain_id, - ); - let response = execute_request(request, our)?; + let message = HyperwalletMessage::BuildAndSignUserOperationForPayment(request); + let response: HyperwalletResponse = super::send_message(message, our)?; Ok(response.data.unwrap_or_default()) } /// Submits a UserOperation to the network. pub fn submit_user_operation( our: &Address, - session_info: &SessionInfo, - signed_user_operation: serde_json::Value, - entry_point: &str, - bundler_url: Option<&str>, - chain_id: Option, + request: HyperwalletRequest, ) -> Result { - let typed_request = SubmitUserOperationRequest { - signed_user_operation, - entry_point: entry_point.to_string(), - bundler_url: bundler_url.map(|s| s.to_string()), - }; - let request = build_request( - our, - session_info, - Operation::SubmitUserOperation, - serde_json::to_value(typed_request).map_err(HyperwalletClientError::Serialization)?, - None, - chain_id, - ); - let response = execute_request(request, our)?; - - // Extract user_op_hash from response data + let message = HyperwalletMessage::SubmitUserOperation(request); + let response: HyperwalletResponse = super::send_message(message, our)?; let data = response.data.unwrap_or_default(); data.get("user_op_hash") .and_then(|h| h.as_str()) @@ -491,89 +241,39 @@ pub fn submit_user_operation( /// Gets the receipt for a UserOperation. pub fn get_user_operation_receipt( our: &Address, - session_info: &SessionInfo, - user_op_hash: &str, - chain_id: Option, + request: HyperwalletRequest, ) -> Result { - let typed_request = GetUserOperationReceiptRequest { - user_op_hash: user_op_hash.to_string(), - }; - let request = build_request( - our, - session_info, - Operation::GetUserOperationReceipt, - serde_json::to_value(typed_request).map_err(HyperwalletClientError::Serialization)?, - None, - chain_id, - ); - let response = execute_request(request, our)?; + let message = HyperwalletMessage::GetUserOperationReceipt(request); + let response: HyperwalletResponse = super::send_message(message, our)?; Ok(response.data.unwrap_or_default()) } -/// High-level convenience function that executes a complete payment flow. -/// This function handles: session management, building UserOp, submitting, and waiting for receipt. -pub fn execute_gasless_payment( +/// Exports a wallet's private key. +pub fn export_wallet( our: &Address, - wallet_id: &str, - target: &str, - call_data: &str, - value: Option<&str>, - tba_address: Option<&str>, - password: Option<&str>, - chain_id: Option, -) -> Result { - // Step 1: Initialize session if needed (you might want to cache this) - let session = super::initialize(our, super::HandshakeConfig::new())?; - - // Step 2: Build and sign UserOperation - let signed_data = build_and_sign_user_operation( - our, - &session, - wallet_id, - target, - call_data, - value, - true, // Always use paymaster for gasless - tba_address, - password, - chain_id, - )?; - - // Step 3: Extract signed UserOperation and entry point - let signed_user_op = signed_data - .get("signed_user_operation") - .ok_or_else(|| { - HyperwalletClientError::ServerError(super::types::OperationError::internal_error( - "Missing signed_user_operation in response", - )) - })? - .clone(); - - let entry_point = signed_data - .get("entry_point") - .and_then(|e| e.as_str()) - .ok_or_else(|| { - HyperwalletClientError::ServerError(super::types::OperationError::internal_error( - "Missing entry_point in response", - )) - })?; - - // Step 4: Submit UserOperation - let user_op_hash = submit_user_operation( - our, - &session, - signed_user_op, - entry_point, - None, // Use default bundler - chain_id, - )?; - - // Step 5: Get receipt (you might want to add polling with timeout) - let receipt = get_user_operation_receipt(our, &session, &user_op_hash, chain_id)?; + request: HyperwalletRequest, +) -> Result { + let message = HyperwalletMessage::ExportWallet(request); + let response: HyperwalletResponse = super::send_message(message, our)?; + response.data.ok_or_else(|| { + HyperwalletClientError::ServerError(types::OperationError::internal_error( + "Missing export data in response", + )) + }) +} - Ok(receipt) +/// Resolves an identity name to an address via Hypermap. +pub fn resolve_identity( + our: &Address, + request: HyperwalletRequest, +) -> Result { + let message = HyperwalletMessage::ResolveIdentity(request); + let response: HyperwalletResponse = super::send_message(message, our)?; + Ok(response.data.unwrap_or_default()) } +// === HELPER FUNCTIONS === + /// Creates TBA execute calldata for an ERC20 transfer payment. /// This wraps the existing wallet.rs functions to make payments simpler. pub fn create_tba_payment_calldata( @@ -613,44 +313,42 @@ pub fn create_tba_payment_calldata( } /// Simplified gasless payment function - abstracts away all complexity. -/// This is what the operator should actually use. pub fn build_and_sign_gasless_payment( our: &Address, - session_info: &SessionInfo, + session_info: &super::types::SessionInfo, signer_wallet_id: &str, tba_address: &str, call_data: &str, chain_id: Option, ) -> Result { - let params = serde_json::json!({ - "target": tba_address, - "call_data": call_data, - "use_paymaster": true, // Always gasless - "paymaster_config": { - "tba_address": tba_address, - "is_circle_paymaster": true, - "paymaster_address": "0x0578cFB241215b77442a541325d6A4E6dFE700Ec", - "paymaster_verification_gas": "0x7a120", - "paymaster_post_op_gas": "0x493e0" - } - }); + let request = super::types::BuildAndSignUserOperationForPaymentRequest { + target: tba_address.to_string(), + call_data: call_data.to_string(), + value: Some("0".to_string()), + use_paymaster: true, + paymaster_config: Some(create_paymaster_config_with_tba(Some(tba_address))), + password: None, + }; - let request = build_request( - our, - session_info, - Operation::BuildAndSignUserOperationForPayment, - params, - Some(signer_wallet_id.to_string()), + let hyperwallet_request = super::types::HyperwalletRequest { + business_data: request, + wallet_id: Some(signer_wallet_id.to_string()), chain_id, - ); - let response = execute_request(request, our)?; - Ok(response.data.unwrap_or_default()) + auth: super::types::ProcessAuth { + process_address: our.to_string(), + signature: None, + }, + request_id: None, + timestamp: current_timestamp(), + }; + + build_and_sign_user_operation(our, hyperwallet_request) } /// Simplified submit that extracts entry point automatically from build response. pub fn submit_gasless_payment( our: &Address, - session_info: &SessionInfo, + session_info: &super::types::SessionInfo, signed_user_op_response: serde_json::Value, chain_id: Option, ) -> Result { @@ -673,25 +371,49 @@ pub fn submit_gasless_payment( )) })?; - // Submit using the extracted data - submit_user_operation( - our, - session_info, - signed_user_op, - entry_point, - None, + let request = super::types::SubmitUserOperationRequest { + signed_user_operation: signed_user_op, + entry_point: entry_point.to_string(), + bundler_url: None, + }; + + let hyperwallet_request = super::types::HyperwalletRequest { + business_data: request, + wallet_id: None, chain_id, - ) + auth: super::types::ProcessAuth { + process_address: our.to_string(), + signature: None, + }, + request_id: None, + timestamp: current_timestamp(), + }; + + submit_user_operation(our, hyperwallet_request) } /// Get receipt with proper transaction hash extraction. pub fn get_payment_receipt( our: &Address, - session_info: &SessionInfo, + session_info: &super::types::SessionInfo, user_op_hash: &str, chain_id: Option, ) -> Result<(String, serde_json::Value), HyperwalletClientError> { - let receipt = get_user_operation_receipt(our, session_info, user_op_hash, chain_id)?; + let request = super::types::GetUserOperationReceiptRequest { + user_op_hash: user_op_hash.to_string(), + }; + let hyperwallet_request = super::types::HyperwalletRequest { + business_data: request, + wallet_id: None, + chain_id, + auth: super::types::ProcessAuth { + process_address: our.to_string(), + signature: None, + }, + request_id: None, + timestamp: current_timestamp(), + }; + let receipt = get_user_operation_receipt(our, hyperwallet_request)?; // Extract transaction hash if available let tx_hash = receipt @@ -704,31 +426,10 @@ pub fn get_payment_receipt( Ok((tx_hash, receipt)) } -/// Resolves an identity name to an address via Hypermap. -pub fn resolve_identity( - our: &Address, - session_info: &SessionInfo, - entry_name: &str, - chain_id: Option, -) -> Result { - let params = serde_json::json!({ "entry_name": entry_name }); - let request = build_request( - our, - session_info, - Operation::ResolveIdentity, - params, - None, - chain_id, - ); - let response = execute_request(request, our)?; - Ok(response.data.unwrap_or_default()) -} - /// Complete payment function that handles everything internally. -/// The operator just needs to call this one function. pub fn execute_complete_gasless_payment( our: &Address, - session_info: &SessionInfo, + session_info: &super::types::SessionInfo, signer_wallet_id: &str, tba_address: &str, recipient_address: &str, @@ -738,7 +439,6 @@ pub fn execute_complete_gasless_payment( // Step 1: Get USDC contract for the chain let usdc_contract = match chain_id { 8453 => "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", // Base USDC - 1 => "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", // Mainnet USDC _ => { return Err(HyperwalletClientError::ServerError( super::types::OperationError::invalid_params(&format!( @@ -776,7 +476,6 @@ pub fn execute_complete_gasless_payment( } /// Validates payment setup and returns the required addresses and amounts. -/// This replaces the manual validation logic in the operator. pub fn validate_gasless_payment_setup( tba_address: Option<&String>, recipient_address: &str, @@ -786,7 +485,6 @@ pub fn validate_gasless_payment_setup( // Get USDC contract address for the chain let usdc_contract = match chain_id { 8453 => "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", // Base USDC - 1 => "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", // Mainnet USDC _ => { return Err(HyperwalletClientError::ServerError( super::types::OperationError::invalid_params(&format!( @@ -797,12 +495,12 @@ pub fn validate_gasless_payment_setup( } }; - // Check if operator TBA is configured - let operator_tba = match tba_address { + // Check if TBA is configured + let tba = match tba_address { Some(addr) => addr.clone(), None => { return Err(HyperwalletClientError::ServerError( - super::types::OperationError::invalid_params("Operator TBA not configured"), + super::types::OperationError::invalid_params("TBA not configured"), )) } }; @@ -829,24 +527,13 @@ pub fn validate_gasless_payment_setup( Ok(( usdc_contract.to_string(), - operator_tba, + tba, recipient_address.to_string(), amount_usdc, )) } -/// Creates TBA execute calldata for a USDC payment. -/// This replaces the manual calldata creation logic in the operator. -pub fn create_usdc_payment_calldata( - usdc_contract: &str, - recipient_address: &str, - amount_usdc: u128, -) -> Result { - create_tba_payment_calldata(usdc_contract, recipient_address, amount_usdc) -} - /// Extracts transaction hash from a payment receipt, with fallback logic. -/// This replaces the manual receipt parsing in the operator. pub fn extract_payment_tx_hash( receipt_result: Result<(String, serde_json::Value), HyperwalletClientError>, user_op_hash_fallback: &str, @@ -860,28 +547,21 @@ pub fn extract_payment_tx_hash( } } -// Internal helper to reduce boilerplate in the API functions. -fn build_request( - our: &Address, - _session_info: &SessionInfo, - operation: Operation, - params: serde_json::Value, - wallet_id: Option, - chain_id: Option, -) -> types::OperationRequest { - types::OperationRequest { - operation, - params, - wallet_id, - chain_id, - auth: types::ProcessAuth { - process_address: our.to_string(), - signature: None, // Session ID could be sent here - }, - request_id: None, - timestamp: std::time::SystemTime::now() - .duration_since(std::time::UNIX_EPOCH) - .unwrap() - .as_secs(), - } +/// Helper function to create a PaymasterConfig with TBA address for gasless transactions. +pub fn create_paymaster_config_with_tba(tba_address: Option<&str>) -> PaymasterConfig { + let mut config = PaymasterConfig::default(); + config.tba_address = tba_address.map(|s| s.to_string()); + config +} + +// === INTERNAL HELPERS === + +// Uses the shared send_message function from the parent module + +/// Get current timestamp for message construction. +pub fn current_timestamp() -> u64 { + std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .unwrap() + .as_secs() } diff --git a/src/hyperwallet_client/mod.rs b/src/hyperwallet_client/mod.rs index 1c79c21..f3c3b12 100644 --- a/src/hyperwallet_client/mod.rs +++ b/src/hyperwallet_client/mod.rs @@ -7,72 +7,35 @@ //! It contains a public `types` submodule that defines the entire protocol, which is //! also used by the Hyperwallet server process to ensure compatibility. -pub mod api; -pub mod types; +use crate::{Address, Request}; +use thiserror::Error; -pub use api::{ - approve_token, - // Gasless Payment Operations - build_and_sign_gasless_payment, - // User Operation Building and Submission - build_and_sign_user_operation, - build_and_sign_user_operation_for_payment, - // TBA (Token Bound Account) Operations - check_tba_ownership, - // Miscellaneous - create_note, - create_tba_payment_calldata, - create_usdc_payment_calldata, - - // Wallet Management - create_wallet, - delete_wallet, - execute_complete_gasless_payment, - execute_gasless_payment, - execute_via_tba, - extract_payment_tx_hash, - - // Balance and Token Operations - get_balance, - get_payment_receipt, - get_token_balance, - get_user_operation_receipt, - - get_wallet_info, - - import_wallet, - list_wallets, - rename_wallet, - resolve_identity, - send_eth, - send_token, - - set_wallet_limits, - submit_gasless_payment, - submit_user_operation, - unlock_wallet, - validate_gasless_payment_setup, -}; +// Re-export the most important types for convenience. pub use types::{ - // Request Types ApproveTokenRequest, - // Configuration and Data Types + // Response types Balance, + // Business logic types BuildAndSignUserOperationForPaymentRequest, - // Response Types - BuildAndSignUserOperationResponse, CheckTbaOwnershipRequest, CreateWalletRequest, ExecuteViaTbaRequest, + ExportWalletRequest, + ExportWalletResponse, GetTokenBalanceRequest, GetUserOperationReceiptRequest, HandshakeConfig, + HyperwalletMessage, + HyperwalletRequest, + HyperwalletResponse, ImportWalletRequest, - // Operation and Error Types + ListWalletsResponse, Operation, OperationCategory, OperationError, + // Convenience types PaymasterConfig, + ProcessAuth, ProcessPermissions, RenameWalletRequest, ResolveIdentityRequest, @@ -81,18 +44,11 @@ pub use types::{ SessionInfo, SpendingLimits, SubmitUserOperationRequest, - SubmitUserOperationResponse, TxReceipt, - UnlockWalletRequest, - - UserOperationHash, Wallet, }; -use crate::{Address, Request}; -use thiserror::Error; - /// The process identifier for the system's Hyperwallet service. const HYPERWALLET_PROCESS: &str = "hyperwallet:hyperwallet:hallman.hypr"; @@ -114,6 +70,7 @@ pub enum HyperwalletClientError { } /// Performs the full handshake and registration protocol with the Hyperwallet service. +/// The calling process must provide its own address (`our`). pub fn initialize( our: &Address, config: HandshakeConfig, @@ -122,12 +79,26 @@ pub fn initialize( .client_name .unwrap_or_else(|| our.process().to_string()); + // Step 1: Send ClientHello let hello_step = types::HandshakeStep::ClientHello { client_version: env!("CARGO_PKG_VERSION").to_string(), client_name, }; - let welcome_response: types::OperationResponse = send_handshake_step(hello_step, our)?; + let hello_message = types::HyperwalletMessage::Handshake(types::HyperwalletRequest { + business_data: hello_step, + wallet_id: None, + chain_id: None, + auth: types::ProcessAuth { + process_address: our.to_string(), + signature: None, + }, + request_id: None, + timestamp: current_timestamp(), + }); + let welcome_response: types::HyperwalletResponse = + send_message(hello_message, our)?; + // Step 2: Parse ServerWelcome and check compatibility let welcome_data = welcome_response.data.ok_or_else(|| { HyperwalletClientError::ServerError(types::OperationError::internal_error( "ServerWelcome response contained no data", @@ -146,95 +117,80 @@ pub fn initialize( } } + // Step 3: Send Register let register_step = types::HandshakeStep::Register { required_operations: config.required_operations.into_iter().collect(), spending_limits: config.spending_limits, }; - let complete_response = send_handshake_step(register_step, our)?; + let register_message = types::HyperwalletMessage::Handshake(types::HyperwalletRequest { + business_data: register_step, + wallet_id: None, + chain_id: None, + auth: types::ProcessAuth { + process_address: our.to_string(), + signature: None, + }, + request_id: None, + timestamp: current_timestamp(), + }); + let complete_response: types::HyperwalletResponse = + send_message(register_message, our)?; + // Step 4: Parse Complete and return SessionInfo let complete_data = complete_response.data.ok_or_else(|| { HyperwalletClientError::ServerError(types::OperationError::internal_error( "Complete response contained no data", )) })?; - // Extract session info from the Complete response - let session_id = complete_data["session_id"] - .as_str() - .ok_or_else(|| { - HyperwalletClientError::ServerError(types::OperationError::internal_error( - "Missing session_id in Complete response", - )) - })? - .to_string(); - - let registered_permissions: ProcessPermissions = - serde_json::from_value(complete_data["registered_permissions"].clone()) - .map_err(HyperwalletClientError::Deserialization)?; - - // Get server version from the earlier welcome response - let server_version = welcome_data["server_version"] - .as_str() - .ok_or_else(|| { - HyperwalletClientError::ServerError(types::OperationError::internal_error( - "Missing server_version in ServerWelcome response", - )) - })? - .to_string(); - - Ok(SessionInfo { - server_version, - session_id, - registered_permissions, - }) + serde_json::from_value(complete_data).map_err(HyperwalletClientError::Deserialization) } -// Internal helper for the handshake steps. -fn send_handshake_step( - step: types::HandshakeStep, - our: &Address, -) -> Result { - let request = types::OperationRequest { - operation: Operation::Handshake, - params: serde_json::to_value(step).map_err(HyperwalletClientError::Serialization)?, - auth: types::ProcessAuth { - process_address: our.to_string(), - signature: None, - }, - wallet_id: None, - chain_id: None, - request_id: None, - timestamp: 0, - }; - execute_request(request, our) -} +// === INTERNAL HELPERS === -// The lowest-level helper that handles sending all requests. -pub(crate) fn execute_request( - request: types::OperationRequest, +/// Send a typed message to the hyperwallet service +pub(crate) fn send_message( + message: types::HyperwalletMessage, our: &Address, -) -> Result { +) -> Result, HyperwalletClientError> +where + T: for<'de> serde::Deserialize<'de>, +{ // Construct the full hyperwallet address using our node let process_id: crate::ProcessId = ("hyperwallet", "hyperwallet", "hallman.hypr").into(); - let hyperwallet_address = Address::new(our.node(), process_id); + let hyperwallet_address = crate::Address::new(our.node(), process_id); let response = Request::new() .target(hyperwallet_address) - .body(serde_json::to_vec(&request).map_err(HyperwalletClientError::Serialization)?) + .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 op_response: types::OperationResponse = + let hyperwallet_response: types::HyperwalletResponse = serde_json::from_slice(response.body()).map_err(HyperwalletClientError::Deserialization)?; - if !op_response.success { + if !hyperwallet_response.success { return Err(HyperwalletClientError::ServerError( - op_response.error.unwrap_or_else(|| { + hyperwallet_response.error.unwrap_or_else(|| { types::OperationError::internal_error("Operation failed with no error details") }), )); } - Ok(op_response) + Ok(hyperwallet_response) +} + +/// Get current timestamp +fn current_timestamp() -> u64 { + std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .unwrap() + .as_secs() } + +/// Contains all the shared types for the Hyperwallet protocol. +pub mod types; + +/// High-level API functions +pub mod api; diff --git a/src/hyperwallet_client/types.rs b/src/hyperwallet_client/types.rs index 909338e..3c358e0 100644 --- a/src/hyperwallet_client/types.rs +++ b/src/hyperwallet_client/types.rs @@ -3,7 +3,6 @@ use serde::{Deserialize, Serialize}; use std::collections::HashSet; -// Type Aliases pub type ProcessAddress = String; pub type WalletAddress = String; pub type ChainId = u64; @@ -11,7 +10,6 @@ pub type SessionId = String; pub type UserOperationHash = String; pub type Signature = Vec; -/// All possible wallet operations that can be performed through the hyperwallet service. #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)] pub enum Operation { Handshake, @@ -221,16 +219,14 @@ impl HandshakeConfig { } } -/// Information about an established session with Hyperwallet. -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub struct SessionInfo { pub server_version: String, pub session_id: SessionId, pub registered_permissions: ProcessPermissions, } -/// The steps involved in the handshake protocol. -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize)] #[serde(tag = "step")] pub enum HandshakeStep { ClientHello { @@ -254,9 +250,14 @@ pub enum HandshakeStep { } #[derive(Debug, Clone, Serialize, Deserialize)] -pub struct OperationRequest { - pub operation: Operation, - pub params: serde_json::Value, +pub struct ProcessAuth { + pub process_address: String, + pub signature: Option>, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct HyperwalletRequest { + pub business_data: T, pub wallet_id: Option, pub chain_id: Option, pub auth: ProcessAuth, @@ -264,10 +265,175 @@ pub struct OperationRequest { pub timestamp: u64, } +/// Typed message enum for type-safe communication #[derive(Debug, Clone, Serialize, Deserialize)] -pub struct ProcessAuth { - pub process_address: String, - pub signature: Option>, +#[serde(tag = "operation")] +pub enum HyperwalletMessage { + Handshake(HyperwalletRequest), + CreateWallet(HyperwalletRequest), + ImportWallet(HyperwalletRequest), + UnlockWallet(HyperwalletRequest), + DeleteWallet(HyperwalletRequest<()>), + RenameWallet(HyperwalletRequest), + ExportWallet(HyperwalletRequest), + GetWalletInfo(HyperwalletRequest<()>), + ListWallets(HyperwalletRequest<()>), + SetWalletLimits(HyperwalletRequest), + SendEth(HyperwalletRequest), + SendToken(HyperwalletRequest), + ApproveToken(HyperwalletRequest), + GetBalance(HyperwalletRequest<()>), + GetTokenBalance(HyperwalletRequest), + ExecuteViaTba(HyperwalletRequest), + CheckTbaOwnership(HyperwalletRequest), + BuildAndSignUserOperationForPayment( + HyperwalletRequest, + ), + SubmitUserOperation(HyperwalletRequest), + GetUserOperationReceipt(HyperwalletRequest), + ResolveIdentity(HyperwalletRequest), + CreateNote(HyperwalletRequest), // Flexible for notes +} + +/// Unified response type +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct HyperwalletResponse { + pub success: bool, + pub data: Option, + pub error: Option, + pub request_id: Option, + pub timestamp: u64, +} + +/// Configuration for Circle paymaster (gasless transactions) +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct PaymasterConfig { + pub tba_address: Option, + 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 { + tba_address: None, + is_circle_paymaster: true, + paymaster_address: "0x0578cFB241215b77442a541325d6A4E6dFE700Ec".to_string(), // Base Circle paymaster + paymaster_verification_gas: "0x7a120".to_string(), // 500000 + paymaster_post_op_gas: "0x493e0".to_string(), // 300000 + } + } +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct BuildAndSignUserOperationForPaymentRequest { + pub target: String, + pub call_data: String, + pub value: Option, + pub use_paymaster: bool, + pub paymaster_config: Option, + pub password: Option, +} + +#[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: String, + 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 new_name: String, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct ExportWalletRequest { + pub password: Option, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct SendEthRequest { + pub to: String, + pub amount: String, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct SendTokenRequest { + 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 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, +} + +// === LEGACY COMPATIBILITY === + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct OperationRequest { + pub operation: Operation, + pub params: serde_json::Value, + pub wallet_id: Option, + pub chain_id: Option, + pub auth: ProcessAuth, + pub request_id: Option, + pub timestamp: u64, } #[derive(Debug, Clone, Serialize, Deserialize)] @@ -354,6 +520,19 @@ pub struct Wallet { 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)] @@ -368,42 +547,6 @@ pub struct Balance { pub raw: String, // U256 as string } -// === Operation-Specific Request/Response Types === - -/// Configuration for Circle paymaster (gasless transactions) -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct PaymasterConfig { - pub tba_address: Option, - 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 { - tba_address: None, - is_circle_paymaster: true, - paymaster_address: "0x0578cFB241215b77442a541325d6A4E6dFE700Ec".to_string(), // Base Circle paymaster - paymaster_verification_gas: "0x7a120".to_string(), // 500000 - paymaster_post_op_gas: "0x493e0".to_string(), // 300000 - } - } -} - -/// Request for building and signing a UserOperation for payment -#[derive(Debug, Serialize, Deserialize)] -pub struct BuildAndSignUserOperationForPaymentRequest { - pub target: String, - pub call_data: String, - pub value: Option, - pub use_paymaster: bool, - pub paymaster_config: Option, - pub password: Option, -} - -/// Response from building and signing a UserOperation #[derive(Debug, Serialize, Deserialize)] pub struct BuildAndSignUserOperationResponse { pub signed_user_operation: serde_json::Value, @@ -412,102 +555,21 @@ pub struct BuildAndSignUserOperationResponse { pub ready_to_submit: bool, } -/// Request for submitting a UserOperation -#[derive(Debug, Serialize, Deserialize)] -pub struct SubmitUserOperationRequest { - pub signed_user_operation: serde_json::Value, - pub entry_point: String, - pub bundler_url: Option, -} - -/// Response from submitting a UserOperation #[derive(Debug, Serialize, Deserialize)] pub struct SubmitUserOperationResponse { pub user_op_hash: String, } -/// Request for getting UserOperation receipt -#[derive(Debug, Serialize, Deserialize)] -pub struct GetUserOperationReceiptRequest { - pub user_op_hash: String, -} - -/// Request for creating a wallet #[derive(Debug, Serialize, Deserialize)] -pub struct CreateWalletRequest { - pub name: String, - pub password: Option, -} - -/// Request for unlocking a wallet -#[derive(Debug, Serialize, Deserialize)] -pub struct UnlockWalletRequest { - pub session_id: String, - pub wallet_id: String, - pub password: String, -} - -/// Request for importing a wallet -#[derive(Debug, Serialize, Deserialize)] -pub struct ImportWalletRequest { - pub name: String, +pub struct ExportWalletResponse { + pub address: String, pub private_key: String, - pub password: Option, -} - -/// Request for renaming a wallet -#[derive(Debug, Serialize, Deserialize)] -pub struct RenameWalletRequest { - pub new_name: String, -} - -/// Request for sending ETH -#[derive(Debug, Serialize, Deserialize)] -pub struct SendEthRequest { - pub to: String, - pub amount: String, -} - -/// Request for sending tokens -#[derive(Debug, Serialize, Deserialize)] -pub struct SendTokenRequest { - pub token_address: String, - pub to: String, - pub amount: String, -} - -/// Request for approving tokens -#[derive(Debug, Serialize, Deserialize)] -pub struct ApproveTokenRequest { - pub token_address: String, - pub spender: String, - pub amount: String, -} - -/// Request for getting token balance -#[derive(Debug, Serialize, Deserialize)] -pub struct GetTokenBalanceRequest { - pub token_address: String, -} - -/// Request for executing via TBA -#[derive(Debug, Serialize, Deserialize)] -pub struct ExecuteViaTbaRequest { - pub tba_address: String, - pub target: String, - pub call_data: String, - pub value: Option, -} - -/// Request for checking TBA ownership -#[derive(Debug, Serialize, Deserialize)] -pub struct CheckTbaOwnershipRequest { - pub tba_address: String, - pub signer_address: String, + pub chain_id: u64, } -/// Request for resolving identity #[derive(Debug, Serialize, Deserialize)] -pub struct ResolveIdentityRequest { - pub entry_name: String, +pub struct ListWalletsResponse { + pub process: String, + pub wallets: Vec, + pub total: usize, } From 800724e606a4905eadf4ac135379953c6217a0c4 Mon Sep 17 00:00:00 2001 From: Hallmane Date: Wed, 30 Jul 2025 18:13:22 +0200 Subject: [PATCH 78/96] thinning too early, reverting --- src/hyperwallet_client/api.rs | 725 ++++++++++++++------------------ src/hyperwallet_client/mod.rs | 227 +++++----- src/hyperwallet_client/types.rs | 430 +++++++++++++++++-- 3 files changed, 830 insertions(+), 552 deletions(-) diff --git a/src/hyperwallet_client/api.rs b/src/hyperwallet_client/api.rs index 850ed1a..403195e 100644 --- a/src/hyperwallet_client/api.rs +++ b/src/hyperwallet_client/api.rs @@ -1,28 +1,37 @@ -//! Public API wrappers for Hyperwallet operations using clean typed messaging. +//! Clean, simple API functions for Hyperwallet operations. //! -//! All functions take specific HyperwalletRequest objects for perfect type safety. -//! Users construct the exact request type needed and get compile-time verification. +//! These functions follow the HTTP client pattern: simple parameters in, results out. +//! All the complex message construction is handled internally. use super::types::{ - self, ApproveTokenRequest, Balance, BuildAndSignUserOperationForPaymentRequest, - CheckTbaOwnershipRequest, CreateWalletRequest, ExecuteViaTbaRequest, ExportWalletRequest, - ExportWalletResponse, GetTokenBalanceRequest, GetUserOperationReceiptRequest, - HyperwalletMessage, HyperwalletRequest, HyperwalletResponse, ImportWalletRequest, - ListWalletsResponse, PaymasterConfig, RenameWalletRequest, ResolveIdentityRequest, - SendEthRequest, SendTokenRequest, SpendingLimits, SubmitUserOperationRequest, TxReceipt, - UnlockWalletRequest, Wallet, + self, Balance, BuildAndSignUserOperationForPaymentRequest, BuildAndSignUserOperationResponse, + CreateWalletRequest, ExportWalletResponse, GetTokenBalanceResponse, HyperwalletMessage, + HyperwalletRequest, HyperwalletResponse, ImportWalletRequest, ListWalletsResponse, + PaymasterConfig, SendEthRequest, SendTokenRequest, SessionId, SubmitUserOperationResponse, + TxReceipt, UnlockWalletRequest, UserOperationReceiptResponse, Wallet, }; use super::HyperwalletClientError; -use crate::{wallet, Address}; +use crate::wallet; use alloy_primitives::{Address as EthAddress, U256}; +// === WALLET MANAGEMENT === + /// Creates a new wallet for the process. pub fn create_wallet( - our: &Address, - request: HyperwalletRequest, + session_id: &SessionId, + name: &str, + password: Option<&str>, ) -> Result { + let request = build_request( + session_id, + CreateWalletRequest { + name: name.to_string(), + password: password.map(|s| s.to_string()), + }, + ); + let message = HyperwalletMessage::CreateWallet(request); - let response: HyperwalletResponse = super::send_message(message, our)?; + let response: HyperwalletResponse = super::send_message(message)?; response.data.ok_or_else(|| { HyperwalletClientError::ServerError(types::OperationError::internal_error( "Missing wallet data in response", @@ -30,95 +39,58 @@ pub fn create_wallet( }) } -/// Sends ETH from a managed wallet. -pub fn send_eth( - our: &Address, - request: HyperwalletRequest, -) -> Result { - let message = HyperwalletMessage::SendEth(request); - let response: HyperwalletResponse = super::send_message(message, our)?; - response.data.ok_or_else(|| { - HyperwalletClientError::ServerError(types::OperationError::internal_error( - "Missing transaction receipt in response", - )) - }) -} +/// Imports a wallet from a private key. +pub fn import_wallet( + session_id: &SessionId, + name: &str, + private_key: &str, + password: Option<&str>, +) -> Result { + let request = build_request( + session_id, + ImportWalletRequest { + name: name.to_string(), + private_key: private_key.to_string(), + password: password.map(|s| s.to_string()), + }, + ); -/// Retrieves the native balance of a managed wallet. -pub fn get_balance( - our: &Address, - request: HyperwalletRequest<()>, -) -> Result { - let message = HyperwalletMessage::GetBalance(request); - let response: HyperwalletResponse = super::send_message(message, our)?; + let message = HyperwalletMessage::ImportWallet(request); + let response: HyperwalletResponse = super::send_message(message)?; response.data.ok_or_else(|| { HyperwalletClientError::ServerError(types::OperationError::internal_error( - "Missing balance data in response", + "Missing wallet data in response", )) }) } -/// Creates a note in the hypermap. -pub fn create_note( - our: &Address, - request: HyperwalletRequest, -) -> Result { - let message = HyperwalletMessage::CreateNote(request); - let response: HyperwalletResponse = super::send_message(message, our)?; - Ok(response.data.unwrap_or_default()) -} - -/// Executes a transaction via Token Bound Account (TBA). -pub fn execute_via_tba( - our: &Address, - request: HyperwalletRequest, -) -> Result { - let message = HyperwalletMessage::ExecuteViaTba(request); - let response: HyperwalletResponse = super::send_message(message, our)?; - Ok(response.data.unwrap_or_default()) -} - -/// Checks TBA ownership. -pub fn check_tba_ownership( - our: &Address, - request: HyperwalletRequest, -) -> Result { - let message = HyperwalletMessage::CheckTbaOwnership(request); - let response: HyperwalletResponse = super::send_message(message, our)?; - Ok(response.data.unwrap_or_default()) -} - /// Unlocks a wallet with the provided password. pub fn unlock_wallet( - our: &Address, - request: HyperwalletRequest, + session_id: &SessionId, + target_session_id: &str, + wallet_id: &str, + password: &str, ) -> Result<(), HyperwalletClientError> { + let request = build_request( + session_id, + 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, our)?; + let _response: HyperwalletResponse<()> = super::send_message(message)?; Ok(()) } -/// Imports a wallet from a private key. -pub fn import_wallet( - our: &Address, - request: HyperwalletRequest, -) -> Result { - let message = HyperwalletMessage::ImportWallet(request); - let response: HyperwalletResponse = super::send_message(message, our)?; - response.data.ok_or_else(|| { - HyperwalletClientError::ServerError(types::OperationError::internal_error( - "Missing wallet data in response", - )) - }) -} - /// Lists all wallets accessible to the process. -pub fn list_wallets( - our: &Address, - request: HyperwalletRequest<()>, -) -> Result, HyperwalletClientError> { +pub fn list_wallets(session_id: &SessionId) -> Result, HyperwalletClientError> { + let request = build_request(session_id, ()); + let message = HyperwalletMessage::ListWallets(request); - let response: HyperwalletResponse = super::send_message(message, our)?; + 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", @@ -130,11 +102,18 @@ pub fn list_wallets( /// Gets detailed information about a specific wallet. pub fn get_wallet_info( - our: &Address, - request: HyperwalletRequest<()>, + session_id: &SessionId, + wallet_id: &str, ) -> Result { + let request = build_request( + session_id, + types::GetWalletInfoRequest { + wallet_id: wallet_id.to_string(), + }, + ); + let message = HyperwalletMessage::GetWalletInfo(request); - let response: HyperwalletResponse = super::send_message(message, our)?; + let response: HyperwalletResponse = super::send_message(message)?; response.data.ok_or_else(|| { HyperwalletClientError::ServerError(types::OperationError::internal_error( "Missing wallet data in response", @@ -144,41 +123,91 @@ pub fn get_wallet_info( /// Deletes a wallet permanently. pub fn delete_wallet( - our: &Address, - request: HyperwalletRequest<()>, + session_id: &SessionId, + wallet_id: &str, ) -> Result<(), HyperwalletClientError> { + let request = build_request( + session_id, + types::DeleteWalletRequest { + wallet_id: wallet_id.to_string(), + }, + ); + let message = HyperwalletMessage::DeleteWallet(request); - let _response: HyperwalletResponse<()> = super::send_message(message, our)?; + let _response: HyperwalletResponse<()> = super::send_message(message)?; Ok(()) } -/// Renames a wallet. -pub fn rename_wallet( - our: &Address, - request: HyperwalletRequest, -) -> Result<(), HyperwalletClientError> { - let message = HyperwalletMessage::RenameWallet(request); - let _response: HyperwalletResponse<()> = super::send_message(message, our)?; - Ok(()) +/// Exports a wallet's private key. +pub fn export_wallet( + session_id: &SessionId, + wallet_id: &str, + password: Option<&str>, +) -> Result { + let request = build_request( + session_id, + 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", + )) + }) } -/// Sets spending limits for a wallet. -pub fn set_wallet_limits( - our: &Address, - request: HyperwalletRequest, -) -> Result<(), HyperwalletClientError> { - let message = HyperwalletMessage::SetWalletLimits(request); - let _response: HyperwalletResponse<()> = super::send_message(message, our)?; - Ok(()) +// === TRANSACTIONS === + +/// Sends ETH from a managed wallet. +pub fn send_eth( + session_id: &SessionId, + wallet_id: &str, + to_address: &str, + amount_eth: &str, +) -> Result { + let request = build_request( + session_id, + 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", + )) + }) } /// Sends tokens from a managed wallet. pub fn send_token( - our: &Address, - request: HyperwalletRequest, + session_id: &SessionId, + wallet_id: &str, + token_address: &str, + to_address: &str, + amount: &str, ) -> Result { + let request = build_request( + session_id, + 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, our)?; + let response: HyperwalletResponse = super::send_message(message)?; response.data.ok_or_else(|| { HyperwalletClientError::ServerError(types::OperationError::internal_error( "Missing transaction receipt in response", @@ -186,96 +215,198 @@ pub fn send_token( }) } -/// Approves a spender to transfer tokens from a managed wallet. -pub fn approve_token( - our: &Address, - request: HyperwalletRequest, -) -> Result { - let message = HyperwalletMessage::ApproveToken(request); - let response: HyperwalletResponse = super::send_message(message, our)?; +// === QUERIES === + +/// Retrieves the native balance of a managed wallet. +pub fn get_balance( + session_id: &SessionId, + wallet_id: &str, +) -> Result { + let request = build_request( + session_id, + 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 transaction receipt in response", + "Missing balance data in response", )) }) } /// Gets the token balance for a wallet. pub fn get_token_balance( - our: &Address, - request: HyperwalletRequest, -) -> Result { + session_id: &SessionId, + wallet_id: &str, + token_address: &str, +) -> Result { + let request = build_request( + session_id, + 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, our)?; - Ok(response.data.unwrap_or_default()) + let response: HyperwalletResponse = super::send_message(message)?; + response.data.ok_or_else(|| { + HyperwalletClientError::ServerError(types::OperationError::internal_error( + "Missing token balance data in response", + )) + }) } -/// High-level convenience function for building and signing UserOperations with TBA support. -pub fn build_and_sign_user_operation( - our: &Address, - request: HyperwalletRequest, -) -> Result { +// === ACCOUNT ABSTRACTION === + +/// Build and sign a UserOperation for gasless payments. +pub fn build_and_sign_user_operation_for_payment( + session_id: &SessionId, + wallet_id: &str, + target: &str, + call_data: &str, + value: Option<&str>, + use_paymaster: bool, + paymaster_config: Option, + password: Option<&str>, +) -> Result { + let request = build_request( + session_id, + BuildAndSignUserOperationForPaymentRequest { + wallet_id: wallet_id.to_string(), + target: target.to_string(), + call_data: call_data.to_string(), + value: value.map(|s| s.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, our)?; - Ok(response.data.unwrap_or_default()) + let response: HyperwalletResponse = + super::send_message(message)?; + response.data.ok_or_else(|| { + HyperwalletClientError::ServerError(types::OperationError::internal_error( + "Missing UserOperation build response data", + )) + }) } -/// Submits a UserOperation to the network. +/// Submit a UserOperation to the network. pub fn submit_user_operation( - our: &Address, - request: HyperwalletRequest, + session_id: &SessionId, + signed_user_operation: serde_json::Value, + entry_point: &str, + bundler_url: Option<&str>, ) -> Result { + let request = build_request( + session_id, + types::SubmitUserOperationRequest { + signed_user_operation, + 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, our)?; - let data = response.data.unwrap_or_default(); - data.get("user_op_hash") - .and_then(|h| h.as_str()) - .map(|s| s.to_string()) - .ok_or_else(|| { - HyperwalletClientError::ServerError(types::OperationError::internal_error( - "Missing UserOperation hash in response", - )) - }) + 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) } -/// Gets the receipt for a UserOperation. +/// Get the receipt for a UserOperation. pub fn get_user_operation_receipt( - our: &Address, - request: HyperwalletRequest, -) -> Result { - let message = HyperwalletMessage::GetUserOperationReceipt(request); - let response: HyperwalletResponse = super::send_message(message, our)?; - Ok(response.data.unwrap_or_default()) -} + session_id: &SessionId, + user_op_hash: &str, +) -> Result { + let request = build_request( + session_id, + types::GetUserOperationReceiptRequest { + user_op_hash: user_op_hash.to_string(), + }, + ); -/// Exports a wallet's private key. -pub fn export_wallet( - our: &Address, - request: HyperwalletRequest, -) -> Result { - let message = HyperwalletMessage::ExportWallet(request); - let response: HyperwalletResponse = super::send_message(message, our)?; + let message = HyperwalletMessage::GetUserOperationReceipt(request); + let response: HyperwalletResponse = super::send_message(message)?; response.data.ok_or_else(|| { HyperwalletClientError::ServerError(types::OperationError::internal_error( - "Missing export data in response", + "Missing UserOperation receipt data in response", )) }) } -/// Resolves an identity name to an address via Hypermap. -pub fn resolve_identity( - our: &Address, - request: HyperwalletRequest, -) -> Result { - let message = HyperwalletMessage::ResolveIdentity(request); - let response: HyperwalletResponse = super::send_message(message, our)?; - Ok(response.data.unwrap_or_default()) +// === CONVENIENCE FUNCTIONS === + +/// Complete gasless payment flow in one function. +pub fn execute_gasless_payment( + session_id: &SessionId, + signer_wallet_id: &str, + tba_address: &str, + recipient_address: &str, + amount_usdc: u128, +) -> Result { + // Step 1: Get USDC contract for the chain + let usdc_contract = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"; // Base USDC + + // Step 2: Create payment calldata + let tba_calldata = create_tba_payment_calldata(usdc_contract, recipient_address, amount_usdc)?; + + // Step 3: Build and sign gasless payment + let build_response = build_and_sign_user_operation_for_payment( + session_id, + signer_wallet_id, + tba_address, + &tba_calldata, + Some("0"), + true, // use_paymaster + Some(create_paymaster_config_with_tba(Some(tba_address))), + None, // password + )?; + + // Step 4: Submit payment - now using typed data access + let user_op_hash = submit_user_operation( + session_id, + build_response.signed_user_operation, + &build_response.entry_point, + None, // bundler_url + )?; + + // Step 5: Get receipt and extract transaction hash + let receipt_response = + get_user_operation_receipt(session_id, &user_op_hash).unwrap_or_else(|_| { + UserOperationReceiptResponse { + receipt: None, + user_op_hash: user_op_hash.clone(), + status: "pending".to_string(), + } + }); + + // Extract transaction hash from typed receipt + 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(); + + Ok(tx_hash) } // === HELPER FUNCTIONS === /// Creates TBA execute calldata for an ERC20 transfer payment. -/// This wraps the existing wallet.rs functions to make payments simpler. pub fn create_tba_payment_calldata( usdc_contract: &str, recipient_address: &str, @@ -283,13 +414,13 @@ pub fn create_tba_payment_calldata( ) -> Result { // Parse addresses let usdc_addr = usdc_contract.parse::().map_err(|_| { - HyperwalletClientError::ServerError(super::types::OperationError::invalid_params( + HyperwalletClientError::ServerError(types::OperationError::invalid_params( "Invalid USDC contract address", )) })?; let recipient_addr = recipient_address.parse::().map_err(|_| { - HyperwalletClientError::ServerError(super::types::OperationError::invalid_params( + HyperwalletClientError::ServerError(types::OperationError::invalid_params( "Invalid recipient address", )) })?; @@ -312,241 +443,6 @@ pub fn create_tba_payment_calldata( Ok(format!("0x{}", hex::encode(tba_calldata))) } -/// Simplified gasless payment function - abstracts away all complexity. -pub fn build_and_sign_gasless_payment( - our: &Address, - session_info: &super::types::SessionInfo, - signer_wallet_id: &str, - tba_address: &str, - call_data: &str, - chain_id: Option, -) -> Result { - let request = super::types::BuildAndSignUserOperationForPaymentRequest { - target: tba_address.to_string(), - call_data: call_data.to_string(), - value: Some("0".to_string()), - use_paymaster: true, - paymaster_config: Some(create_paymaster_config_with_tba(Some(tba_address))), - password: None, - }; - - let hyperwallet_request = super::types::HyperwalletRequest { - business_data: request, - wallet_id: Some(signer_wallet_id.to_string()), - chain_id, - auth: super::types::ProcessAuth { - process_address: our.to_string(), - signature: None, - }, - request_id: None, - timestamp: current_timestamp(), - }; - - build_and_sign_user_operation(our, hyperwallet_request) -} - -/// Simplified submit that extracts entry point automatically from build response. -pub fn submit_gasless_payment( - our: &Address, - session_info: &super::types::SessionInfo, - signed_user_op_response: serde_json::Value, - chain_id: Option, -) -> Result { - // Extract signed UserOperation and entry point from the build response - let signed_user_op = signed_user_op_response - .get("signed_user_operation") - .ok_or_else(|| { - HyperwalletClientError::ServerError(super::types::OperationError::internal_error( - "Missing signed_user_operation in response", - )) - })? - .clone(); - - let entry_point = signed_user_op_response - .get("entry_point") - .and_then(|e| e.as_str()) - .ok_or_else(|| { - HyperwalletClientError::ServerError(super::types::OperationError::internal_error( - "Missing entry_point in response", - )) - })?; - - let request = super::types::SubmitUserOperationRequest { - signed_user_operation: signed_user_op, - entry_point: entry_point.to_string(), - bundler_url: None, - }; - - let hyperwallet_request = super::types::HyperwalletRequest { - business_data: request, - wallet_id: None, - chain_id, - auth: super::types::ProcessAuth { - process_address: our.to_string(), - signature: None, - }, - request_id: None, - timestamp: current_timestamp(), - }; - - submit_user_operation(our, hyperwallet_request) -} - -/// Get receipt with proper transaction hash extraction. -pub fn get_payment_receipt( - our: &Address, - session_info: &super::types::SessionInfo, - user_op_hash: &str, - chain_id: Option, -) -> Result<(String, serde_json::Value), HyperwalletClientError> { - let request = super::types::GetUserOperationReceiptRequest { - user_op_hash: user_op_hash.to_string(), - }; - let hyperwallet_request = super::types::HyperwalletRequest { - business_data: request, - wallet_id: None, - chain_id, - auth: super::types::ProcessAuth { - process_address: our.to_string(), - signature: None, - }, - request_id: None, - timestamp: current_timestamp(), - }; - let receipt = get_user_operation_receipt(our, hyperwallet_request)?; - - // Extract transaction hash if available - let tx_hash = receipt - .get("receipt") - .and_then(|r| r.get("transactionHash")) - .and_then(|h| h.as_str()) - .unwrap_or(user_op_hash) // Fallback to user op hash - .to_string(); - - Ok((tx_hash, receipt)) -} - -/// Complete payment function that handles everything internally. -pub fn execute_complete_gasless_payment( - our: &Address, - session_info: &super::types::SessionInfo, - signer_wallet_id: &str, - tba_address: &str, - recipient_address: &str, - amount_usdc: u128, - chain_id: u64, -) -> Result { - // Step 1: Get USDC contract for the chain - let usdc_contract = match chain_id { - 8453 => "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", // Base USDC - _ => { - return Err(HyperwalletClientError::ServerError( - super::types::OperationError::invalid_params(&format!( - "Unsupported chain ID: {}", - chain_id - )), - )) - } - }; - - // Step 2: Create payment calldata using wallet.rs functions - let tba_calldata = create_tba_payment_calldata(usdc_contract, recipient_address, amount_usdc)?; - - // Step 3: Build and sign gasless payment - let signed_data = build_and_sign_gasless_payment( - our, - session_info, - signer_wallet_id, - tba_address, - &tba_calldata, - Some(chain_id), - )?; - - // Step 4: Submit payment - let user_op_hash = submit_gasless_payment(our, session_info, signed_data, Some(chain_id))?; - - // Step 5: Get receipt and extract transaction hash - let (tx_hash, _receipt) = get_payment_receipt(our, session_info, &user_op_hash, Some(chain_id)) - .unwrap_or_else(|_| { - // Fallback to user op hash if receipt fails - (user_op_hash.clone(), serde_json::Value::Null) - }); - - Ok(tx_hash) -} - -/// Validates payment setup and returns the required addresses and amounts. -pub fn validate_gasless_payment_setup( - tba_address: Option<&String>, - recipient_address: &str, - amount_usdc_str: &str, - chain_id: u64, -) -> Result<(String, String, String, f64), HyperwalletClientError> { - // Get USDC contract address for the chain - let usdc_contract = match chain_id { - 8453 => "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", // Base USDC - _ => { - return Err(HyperwalletClientError::ServerError( - super::types::OperationError::invalid_params(&format!( - "Unsupported chain ID: {}", - chain_id - )), - )) - } - }; - - // Check if TBA is configured - let tba = match tba_address { - Some(addr) => addr.clone(), - None => { - return Err(HyperwalletClientError::ServerError( - super::types::OperationError::invalid_params("TBA not configured"), - )) - } - }; - - // Validate recipient address format - recipient_address.parse::().map_err(|_| { - HyperwalletClientError::ServerError(super::types::OperationError::invalid_params( - "Invalid recipient address", - )) - })?; - - // Parse and validate USDC amount - let amount_usdc = amount_usdc_str.parse::().map_err(|_| { - HyperwalletClientError::ServerError(super::types::OperationError::invalid_params( - "Invalid USDC amount", - )) - })?; - - if amount_usdc <= 0.0 { - return Err(HyperwalletClientError::ServerError( - super::types::OperationError::invalid_params("USDC amount must be positive"), - )); - } - - Ok(( - usdc_contract.to_string(), - tba, - recipient_address.to_string(), - amount_usdc, - )) -} - -/// Extracts transaction hash from a payment receipt, with fallback logic. -pub fn extract_payment_tx_hash( - receipt_result: Result<(String, serde_json::Value), HyperwalletClientError>, - user_op_hash_fallback: &str, -) -> String { - match receipt_result { - Ok((tx_hash, _receipt)) => tx_hash, - Err(_) => { - // Fallback to user op hash if receipt fails - user_op_hash_fallback.to_string() - } - } -} - /// Helper function to create a PaymasterConfig with TBA address for gasless transactions. pub fn create_paymaster_config_with_tba(tba_address: Option<&str>) -> PaymasterConfig { let mut config = PaymasterConfig::default(); @@ -556,12 +452,15 @@ pub fn create_paymaster_config_with_tba(tba_address: Option<&str>) -> PaymasterC // === INTERNAL HELPERS === -// Uses the shared send_message function from the parent module +/// Internal helper to build HyperwalletRequest with session context. +fn build_request(session_id: &SessionId, operation_data: T) -> HyperwalletRequest { + HyperwalletRequest { + operation: operation_data, + session_id: session_id.clone(), + } +} /// Get current timestamp for message construction. -pub fn current_timestamp() -> u64 { - std::time::SystemTime::now() - .duration_since(std::time::UNIX_EPOCH) - .unwrap() - .as_secs() +fn current_timestamp() -> u64 { + super::current_timestamp() } diff --git a/src/hyperwallet_client/mod.rs b/src/hyperwallet_client/mod.rs index f3c3b12..89740d9 100644 --- a/src/hyperwallet_client/mod.rs +++ b/src/hyperwallet_client/mod.rs @@ -4,53 +4,73 @@ //! wallets and perform blockchain operations. The primary entry point is the `initialize` //! function, which performs the handshake protocol. //! -//! It contains a public `types` submodule that defines the entire protocol, which is -//! also used by the Hyperwallet server process to ensure compatibility. - -use crate::{Address, Request}; +//! ## Example Usage +//! +//! ```rust +//! use hyperware_process_lib::hyperwallet_client::{self, HandshakeConfig, OperationCategory}; +//! +//! // Initialize hyperwallet connection using category-based permissions +//! let config = HandshakeConfig::new() +//! .require_category(OperationCategory::WalletManagement) +//! .require_category(OperationCategory::ERC4337) +//! .with_initial_chain(8453); // Set default chain to Base mainnet +//! +//! let session = hyperwallet_client::initialize(config)?; +//! +//! // Create a wallet (clean direct access) +//! let wallet = hyperwallet_client::create_wallet( +//! &session.session_id, +//! "MyWallet", +//! Some("password123") +//! )?; +//! +//! // Send ETH (no more ::api:: indirection!) +//! let receipt = hyperwallet_client::send_eth( +//! &session.session_id, +//! &wallet.address, +//! "0x0000000000000000000000000000000000000000", +//! "1.5" +//! )?; +//! +//! // Execute gasless payments directly +//! let tx_hash = hyperwallet_client::execute_gasless_payment( +//! &session.session_id, +//! "signer_wallet", +//! "0x742d35...", // TBA address +//! "0x456789...", // recipient +//! 1_000_000 // 1 USDC +//! )?; +//! ``` + +use crate::Request; use thiserror::Error; -// Re-export the most important types for convenience. +// Export the types module for advanced usage +pub mod types; + +// Export the clean API functions +pub mod api; + +// Re-export the most commonly used types for convenience pub use types::{ - ApproveTokenRequest, - // Response types - Balance, - // Business logic types - BuildAndSignUserOperationForPaymentRequest, - CheckTbaOwnershipRequest, - CreateWalletRequest, - ExecuteViaTbaRequest, - ExportWalletRequest, - ExportWalletResponse, - GetTokenBalanceRequest, - GetUserOperationReceiptRequest, - HandshakeConfig, - HyperwalletMessage, - HyperwalletRequest, - HyperwalletResponse, - ImportWalletRequest, - ListWalletsResponse, - Operation, - OperationCategory, - OperationError, - // Convenience types - PaymasterConfig, - ProcessAuth, - ProcessPermissions, - RenameWalletRequest, - ResolveIdentityRequest, - SendEthRequest, - SendTokenRequest, - SessionInfo, - SpendingLimits, - SubmitUserOperationRequest, - TxReceipt, - UnlockWalletRequest, - Wallet, + Balance, BuildAndSignUserOperationForPaymentRequest, BuildAndSignUserOperationResponse, + CreateWalletRequest, ErrorCode, ExportWalletRequest, ExportWalletResponse, + GetTokenBalanceRequest, GetTokenBalanceResponse, HandshakeConfig, HandshakeRequest, + HandshakeStep, HyperwalletMessage, HyperwalletRequest, HyperwalletResponse, + ImportWalletRequest, ListWalletsResponse, Operation, OperationCategory, OperationError, + PaymasterConfig, ProcessPermissions, SendEthRequest, SendTokenRequest, SessionId, SessionInfo, + SpendingLimits, SubmitUserOperationRequest, SubmitUserOperationResponse, TxReceipt, + UnlockWalletRequest, UpdatableSetting, UserOperationReceiptResponse, Wallet, + WalletSpendingLimits, }; -/// The process identifier for the system's Hyperwallet service. -const HYPERWALLET_PROCESS: &str = "hyperwallet:hyperwallet:hallman.hypr"; +// Re-export all API functions for direct access +pub use api::{ + build_and_sign_user_operation_for_payment, create_paymaster_config_with_tba, + create_tba_payment_calldata, create_wallet, delete_wallet, execute_gasless_payment, + export_wallet, get_balance, get_token_balance, get_user_operation_receipt, get_wallet_info, + import_wallet, list_wallets, send_eth, send_token, submit_user_operation, unlock_wallet, +}; /// Errors that can occur when interacting with the Hyperwallet client. #[derive(Debug, Error)] @@ -70,11 +90,8 @@ pub enum HyperwalletClientError { } /// Performs the full handshake and registration protocol with the Hyperwallet service. -/// The calling process must provide its own address (`our`). -pub fn initialize( - our: &Address, - config: HandshakeConfig, -) -> Result { +pub fn initialize(config: HandshakeConfig) -> Result { + let our = crate::our(); let client_name = config .client_name .unwrap_or_else(|| our.process().to_string()); @@ -84,66 +101,85 @@ pub fn initialize( client_version: env!("CARGO_PKG_VERSION").to_string(), client_name, }; - let hello_message = types::HyperwalletMessage::Handshake(types::HyperwalletRequest { - business_data: hello_step, - wallet_id: None, - chain_id: None, - auth: types::ProcessAuth { - process_address: our.to_string(), - signature: None, - }, - request_id: None, - timestamp: current_timestamp(), + let hello_message = types::HyperwalletMessage::Handshake(types::HandshakeRequest { + operation: hello_step, }); - let welcome_response: types::HyperwalletResponse = - send_message(hello_message, our)?; - // Step 2: Parse ServerWelcome and check compatibility - let welcome_data = welcome_response.data.ok_or_else(|| { + // Step 2: Receive ServerWelcome with type safety + let welcome_response: types::HyperwalletResponse = + send_message(hello_message)?; + + let welcome_step = welcome_response.data.ok_or_else(|| { HyperwalletClientError::ServerError(types::OperationError::internal_error( - "ServerWelcome response contained no data", + "Missing handshake step in ServerWelcome response", )) })?; - let supported_ops: Vec = - serde_json::from_value(welcome_data["supported_operations"].clone()) - .map_err(HyperwalletClientError::Deserialization)?; + let supported_operations = match welcome_step { + types::HandshakeStep::ServerWelcome { + supported_operations, + .. + } => supported_operations, + _ => { + return Err(HyperwalletClientError::ServerError( + types::OperationError::internal_error( + "Expected ServerWelcome handshake step, got different step", + ), + )) + } + }; - for op in &config.required_operations { - if !supported_ops.contains(op) { - return Err(HyperwalletClientError::OperationNotSupported { - operation: op.clone(), - }); + // Step 3: Validate required operations + for required_op in &config.required_operations { + if !supported_operations.contains(required_op) { + return Err(HyperwalletClientError::ServerError(types::OperationError { + code: types::ErrorCode::PermissionDenied, + message: format!( + "Required operation {:?} not supported by server", + required_op + ), + details: None, + })); } } - // Step 3: Send Register + // Step 4: Send Register let register_step = types::HandshakeStep::Register { required_operations: config.required_operations.into_iter().collect(), spending_limits: config.spending_limits, }; - let register_message = types::HyperwalletMessage::Handshake(types::HyperwalletRequest { - business_data: register_step, - wallet_id: None, - chain_id: None, - auth: types::ProcessAuth { - process_address: our.to_string(), - signature: None, - }, - request_id: None, - timestamp: current_timestamp(), + + let register_message = types::HyperwalletMessage::Handshake(types::HandshakeRequest { + operation: register_step, }); - let complete_response: types::HyperwalletResponse = - send_message(register_message, our)?; - // Step 4: Parse Complete and return SessionInfo - let complete_data = complete_response.data.ok_or_else(|| { + // Step 5: Receive Complete with type safety + let complete_response: types::HyperwalletResponse = + send_message(register_message)?; + + let complete_step = complete_response.data.ok_or_else(|| { HyperwalletClientError::ServerError(types::OperationError::internal_error( "Complete response contained no data", )) })?; - serde_json::from_value(complete_data).map_err(HyperwalletClientError::Deserialization) + // Extract SessionInfo using pattern matching + match complete_step { + types::HandshakeStep::Complete { + registered_permissions, + session_id, + } => Ok(SessionInfo { + server_version: env!("CARGO_PKG_VERSION").to_string(), + session_id, + registered_permissions, + initial_chain_id: config.initial_chain_id, + }), + _ => Err(HyperwalletClientError::ServerError( + types::OperationError::internal_error( + "Expected Complete handshake step, received different step", + ), + )), + } } // === INTERNAL HELPERS === @@ -151,17 +187,12 @@ pub fn initialize( /// Send a typed message to the hyperwallet service pub(crate) fn send_message( message: types::HyperwalletMessage, - our: &Address, ) -> Result, HyperwalletClientError> where T: for<'de> serde::Deserialize<'de>, { - // Construct the full hyperwallet address using our node - let process_id: crate::ProcessId = ("hyperwallet", "hyperwallet", "hallman.hypr").into(); - let hyperwallet_address = crate::Address::new(our.node(), process_id); - - let response = Request::new() - .target(hyperwallet_address) + // Use local address pattern like HTTP client - hyperwallet is always local + let response = Request::to(("our", "hyperwallet", "hyperwallet", "hallman.hypr")) .body(serde_json::to_vec(&message).map_err(HyperwalletClientError::Serialization)?) .send_and_await_response(5) // 5s timeout .map_err(|e| HyperwalletClientError::Communication(e.into()))? @@ -181,16 +212,10 @@ where Ok(hyperwallet_response) } -/// Get current timestamp -fn current_timestamp() -> u64 { +/// Get current timestamp for message construction. +pub fn current_timestamp() -> u64 { std::time::SystemTime::now() .duration_since(std::time::UNIX_EPOCH) .unwrap() .as_secs() } - -/// Contains all the shared types for the Hyperwallet protocol. -pub mod types; - -/// High-level API functions -pub mod api; diff --git a/src/hyperwallet_client/types.rs b/src/hyperwallet_client/types.rs index 3c358e0..f8063d3 100644 --- a/src/hyperwallet_client/types.rs +++ b/src/hyperwallet_client/types.rs @@ -185,22 +185,35 @@ impl Operation { } } -/// A configuration object for the `initialize` handshake. -#[derive(Debug, Default)] +#[derive(Debug)] pub struct HandshakeConfig { pub(crate) required_operations: HashSet, pub(crate) spending_limits: Option, pub(crate) client_name: Option, + pub(crate) initial_chain_id: ChainId, +} + +impl Default for HandshakeConfig { + fn default() -> Self { + Self { + required_operations: HashSet::new(), + spending_limits: None, + client_name: None, + initial_chain_id: 8453, // Default to Base mainnet + } + } } impl HandshakeConfig { pub fn new() -> Self { Default::default() } + pub fn with_operations(mut self, operations: &[Operation]) -> Self { self.required_operations.extend(operations.iter().cloned()); self } + pub fn require_category(mut self, category: OperationCategory) -> Self { self.required_operations.extend( Operation::all() @@ -209,14 +222,21 @@ impl HandshakeConfig { ); self } + pub fn with_spending_limits(mut self, limits: SpendingLimits) -> Self { self.spending_limits = Some(limits); self } + pub fn with_name(mut self, name: impl Into) -> Self { self.client_name = Some(name.into()); self } + + pub fn with_initial_chain(mut self, chain_id: ChainId) -> Self { + self.initial_chain_id = chain_id; + self + } } #[derive(Debug, Clone, Serialize, Deserialize)] @@ -224,6 +244,7 @@ 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)] @@ -250,49 +271,140 @@ pub enum HandshakeStep { } #[derive(Debug, Clone, Serialize, Deserialize)] -pub struct ProcessAuth { - pub process_address: String, - pub signature: Option>, +pub struct HyperwalletRequest { + pub operation: T, + pub session_id: SessionId, } #[derive(Debug, Clone, Serialize, Deserialize)] -pub struct HyperwalletRequest { - pub business_data: T, - pub wallet_id: Option, - pub chain_id: Option, - pub auth: ProcessAuth, - pub request_id: Option, - pub timestamp: u64, +pub struct HandshakeRequest { + pub operation: T, } /// Typed message enum for type-safe communication #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(tag = "operation")] pub enum HyperwalletMessage { - Handshake(HyperwalletRequest), + // Session Management (Unauthenticated) + Handshake(HandshakeRequest), + + // Session Management (Authenticated) + UnlockWallet(HyperwalletRequest), + + // Wallet Lifecycle Management CreateWallet(HyperwalletRequest), ImportWallet(HyperwalletRequest), - UnlockWallet(HyperwalletRequest), - DeleteWallet(HyperwalletRequest<()>), + DeleteWallet(HyperwalletRequest), RenameWallet(HyperwalletRequest), ExportWallet(HyperwalletRequest), - GetWalletInfo(HyperwalletRequest<()>), - ListWallets(HyperwalletRequest<()>), - SetWalletLimits(HyperwalletRequest), + ListWallets(HyperwalletRequest<()>), // No parameters needed + GetWalletInfo(HyperwalletRequest), + + // Ethereum Operations SendEth(HyperwalletRequest), SendToken(HyperwalletRequest), ApproveToken(HyperwalletRequest), - GetBalance(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), // Flexible for notes + 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, + + // 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, + + // Process Management (Legacy) + HyperwalletMessage::UpdateSpendingLimits(_) => Operation::UpdateSpendingLimits, + } + } } /// Unified response type @@ -302,7 +414,38 @@ pub struct HyperwalletResponse { pub data: Option, pub error: Option, pub request_id: Option, - pub timestamp: u64, +} + +impl HyperwalletResponse { + pub fn success(data: T) -> Self { + Self { + success: true, + data: Some(data), + error: None, + request_id: None, + } + } + + pub fn error(error: OperationError) -> Self { + Self { + success: false, + data: None, + error: Some(error), + 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, + } + } } /// Configuration for Circle paymaster (gasless transactions) @@ -329,6 +472,7 @@ impl Default for PaymasterConfig { #[derive(Debug, Clone, Serialize, Deserialize)] pub struct BuildAndSignUserOperationForPaymentRequest { + pub wallet_id: String, pub target: String, pub call_data: String, pub value: Option, @@ -364,17 +508,20 @@ pub struct RenameWalletRequest { #[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, @@ -389,6 +536,7 @@ pub struct ApproveTokenRequest { #[derive(Debug, Clone, Serialize, Deserialize)] pub struct GetTokenBalanceRequest { + pub wallet_id: String, pub token_address: String, } @@ -423,28 +571,156 @@ pub struct ResolveIdentityRequest { pub entry_name: String, } -// === LEGACY COMPATIBILITY === +// === NEW PROPERLY TYPED REQUEST STRUCTS === #[derive(Debug, Clone, Serialize, Deserialize)] -pub struct OperationRequest { - pub operation: Operation, - pub params: serde_json::Value, - pub wallet_id: Option, - pub chain_id: Option, - pub auth: ProcessAuth, - pub request_id: Option, - pub timestamp: u64, +pub struct CallContractRequest { + pub to: String, + pub data: String, // hex-encoded calldata + pub value: Option, // wei amount as string } #[derive(Debug, Clone, Serialize, Deserialize)] -pub struct OperationResponse { - pub success: bool, - pub data: Option, - pub error: Option, - pub request_id: Option, - pub timestamp: u64, +pub struct SignTransactionRequest { + pub to: String, + pub value: String, // wei amount as string + pub data: Option, // hex-encoded data + pub gas_limit: Option, + pub gas_price: Option, + pub nonce: Option, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct SignMessageRequest { + pub message: String, // message to sign + pub message_type: MessageType, // EIP-191, EIP-712, etc. +} + +#[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, // hex-encoded calldata + pub value: Option, // wei amount as string +} + +#[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, // permission types +} + +#[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, // UserOperation struct + 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, // UserOperation struct + pub entry_point: String, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct ConfigurePaymasterRequest { + pub paymaster_address: String, + pub paymaster_data: Option, // hex-encoded data + 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, // timestamp +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct VerifyDelegationRequest { + pub delegate_address: String, + pub signature: String, // hex-encoded signature + pub message: String, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct MintEntryRequest { + pub entry_name: String, + pub metadata: serde_json::Value, // entry metadata +} + +#[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, // flexible note content + 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, @@ -551,7 +827,6 @@ pub struct Balance { pub struct BuildAndSignUserOperationResponse { pub signed_user_operation: serde_json::Value, pub entry_point: String, - pub chain_id: u64, pub ready_to_submit: bool, } @@ -564,7 +839,6 @@ pub struct SubmitUserOperationResponse { pub struct ExportWalletResponse { pub address: String, pub private_key: String, - pub chain_id: u64, } #[derive(Debug, Serialize, Deserialize)] @@ -573,3 +847,83 @@ pub struct ListWalletsResponse { pub wallets: Vec, pub total: usize, } + +// === NEW RESPONSE STRUCTS FOR TYPE SAFETY === + +#[derive(Debug, Serialize, Deserialize)] +pub struct GetTokenBalanceResponse { + pub balance: String, + pub formatted: Option, + pub decimals: Option, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct UserOperationReceiptResponse { + pub receipt: Option, // Transaction receipt if mined + pub user_op_hash: String, + pub status: String, // "pending", "included", "failed" +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct CallContractResponse { + pub result: String, // hex-encoded result + pub gas_used: Option, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct SignTransactionResponse { + pub signed_transaction: String, // hex-encoded signed transaction + pub transaction_hash: String, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct SignMessageResponse { + pub signature: String, // hex-encoded signature + pub message_hash: Option, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct EstimateGasResponse { + pub gas_estimate: String, // gas amount as string + pub gas_price: Option, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct GetGasPriceResponse { + pub gas_price: String, // in wei as 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, // "success", "failed", "pending" +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct CheckTbaOwnershipResponse { + pub is_owner: bool, + pub tba_address: String, + pub owner_address: String, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct ResolveIdentityResponse { + pub address: Option, + pub entry_name: String, + pub found: bool, +} From 49b709f0ca7cae04343c1fa2d78f4d091c4df908 Mon Sep 17 00:00:00 2001 From: Hallmane Date: Wed, 30 Jul 2025 20:57:21 +0200 Subject: [PATCH 79/96] added some extra errors --- src/hyperwallet_client/types.rs | 91 +++++++++++++++++++++++++++++++++ 1 file changed, 91 insertions(+) diff --git a/src/hyperwallet_client/types.rs b/src/hyperwallet_client/types.rs index f8063d3..bb3f69b 100644 --- a/src/hyperwallet_client/types.rs +++ b/src/hyperwallet_client/types.rs @@ -744,6 +744,81 @@ impl OperationError { details: None, } } + + pub fn wallet_not_found(wallet_id: &str) -> Self { + Self { + code: ErrorCode::WalletNotFound, + message: format!("Wallet '{}' not found or not accessible", wallet_id), + details: None, + } + } + + pub fn chain_not_allowed(chain_id: u64) -> Self { + Self { + code: ErrorCode::ChainNotAllowed, + message: format!("Chain ID {} is not allowed for this process", chain_id), + details: None, + } + } + + pub fn blockchain_error(message: &str) -> Self { + Self { + code: ErrorCode::BlockchainError, + message: format!("Blockchain error: {}", message), + details: None, + } + } + + pub fn insufficient_funds(details: &str) -> Self { + Self { + code: ErrorCode::InsufficientFunds, + message: format!("Insufficient funds: {}", details), + details: None, + } + } + + pub fn spending_limit_exceeded(details: &str) -> Self { + Self { + code: ErrorCode::SpendingLimitExceeded, + message: format!("Spending limit exceeded: {}", details), + details: None, + } + } + + pub fn authentication_failed(reason: &str) -> Self { + Self { + code: ErrorCode::AuthenticationFailed, + message: format!("Authentication failed: {}", reason), + details: None, + } + } + + pub fn wallet_locked(wallet_id: &str) -> Self { + Self { + code: ErrorCode::WalletLocked, + message: format!( + "Wallet '{}' is locked. Unlock it first to perform operations", + wallet_id + ), + details: None, + } + } + + pub fn operation_not_supported(operation: &str) -> Self { + Self { + code: ErrorCode::OperationNotSupported, + message: format!("Operation '{}' is not supported or not enabled", operation), + details: None, + } + } + + pub fn permission_denied(message: &str) -> Self { + Self { + code: ErrorCode::PermissionDenied, + message: format!("Permission denied: {}", message), + details: None, + } + } } #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] @@ -788,6 +863,22 @@ pub struct ProcessPermissions { pub registered_at: u64, } +impl ProcessPermissions { + /// Create new ProcessPermissions for a process during handshake registration + pub fn new(process_address: String, required_operations: Vec) -> Self { + Self { + process_address, + allowed_operations: required_operations.into_iter().collect(), + spending_limits: None, + updatable_settings: vec![], + registered_at: std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .unwrap() + .as_secs(), + } + } +} + // API Result Structs #[derive(Debug, Serialize, Deserialize)] From e2da49505dc73225d20834e6b37b171a9ebb053a Mon Sep 17 00:00:00 2001 From: Hallmane Date: Wed, 30 Jul 2025 23:07:05 +0200 Subject: [PATCH 80/96] not needed --- src/hyperwallet_client/api.rs | 18 ++++++------------ src/hyperwallet_client/types.rs | 11 +++++++++-- 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/src/hyperwallet_client/api.rs b/src/hyperwallet_client/api.rs index 403195e..adda6b9 100644 --- a/src/hyperwallet_client/api.rs +++ b/src/hyperwallet_client/api.rs @@ -266,10 +266,10 @@ pub fn get_token_balance( /// Build and sign a UserOperation for gasless payments. pub fn build_and_sign_user_operation_for_payment( session_id: &SessionId, - wallet_id: &str, + eoa_wallet_id: &str, + tba_address: &str, target: &str, call_data: &str, - value: Option<&str>, use_paymaster: bool, paymaster_config: Option, password: Option<&str>, @@ -277,10 +277,10 @@ pub fn build_and_sign_user_operation_for_payment( let request = build_request( session_id, BuildAndSignUserOperationForPaymentRequest { - wallet_id: wallet_id.to_string(), + eoa_wallet_id: eoa_wallet_id.to_string(), + tba_address: tba_address.to_string(), target: target.to_string(), call_data: call_data.to_string(), - value: value.map(|s| s.to_string()), use_paymaster, paymaster_config, password: password.map(|s| s.to_string()), @@ -356,25 +356,21 @@ pub fn execute_gasless_payment( recipient_address: &str, amount_usdc: u128, ) -> Result { - // Step 1: Get USDC contract for the chain let usdc_contract = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"; // Base USDC - // Step 2: Create payment calldata let tba_calldata = create_tba_payment_calldata(usdc_contract, recipient_address, amount_usdc)?; - // Step 3: Build and sign gasless payment let build_response = build_and_sign_user_operation_for_payment( session_id, signer_wallet_id, tba_address, + tba_address, &tba_calldata, - Some("0"), - true, // use_paymaster + true, Some(create_paymaster_config_with_tba(Some(tba_address))), None, // password )?; - // Step 4: Submit payment - now using typed data access let user_op_hash = submit_user_operation( session_id, build_response.signed_user_operation, @@ -382,7 +378,6 @@ pub fn execute_gasless_payment( None, // bundler_url )?; - // Step 5: Get receipt and extract transaction hash let receipt_response = get_user_operation_receipt(session_id, &user_op_hash).unwrap_or_else(|_| { UserOperationReceiptResponse { @@ -392,7 +387,6 @@ pub fn execute_gasless_payment( } }); - // Extract transaction hash from typed receipt let tx_hash = receipt_response .receipt .as_ref() diff --git a/src/hyperwallet_client/types.rs b/src/hyperwallet_client/types.rs index bb3f69b..d527fa6 100644 --- a/src/hyperwallet_client/types.rs +++ b/src/hyperwallet_client/types.rs @@ -472,12 +472,19 @@ impl Default for PaymasterConfig { #[derive(Debug, Clone, Serialize, Deserialize)] pub struct BuildAndSignUserOperationForPaymentRequest { - pub wallet_id: String, + /// Identifier for the EOA that will sign the UserOperation + pub eoa_wallet_id: String, + /// The TBA address that acts as the sender + pub tba_address: String, + /// Target address for the call pub target: String, + /// Call data in hex string format pub call_data: String, - pub value: Option, + /// Whether to use a paymaster pub use_paymaster: bool, + /// Optional paymaster configuration pub paymaster_config: Option, + /// Optional password for the wallet pub password: Option, } From 8d0b289a23386099715bb54daa73d9c3047c2656 Mon Sep 17 00:00:00 2001 From: Hallmane Date: Wed, 30 Jul 2025 23:10:46 +0200 Subject: [PATCH 81/96] oops --- src/hyperwallet_client/api.rs | 9 +------- src/hyperwallet_client/mod.rs | 8 +++---- src/hyperwallet_client/types.rs | 38 ++++++++++++++++----------------- 3 files changed, 23 insertions(+), 32 deletions(-) diff --git a/src/hyperwallet_client/api.rs b/src/hyperwallet_client/api.rs index adda6b9..5531bd5 100644 --- a/src/hyperwallet_client/api.rs +++ b/src/hyperwallet_client/api.rs @@ -367,7 +367,7 @@ pub fn execute_gasless_payment( tba_address, &tba_calldata, true, - Some(create_paymaster_config_with_tba(Some(tba_address))), + Default::default(), None, // password )?; @@ -437,13 +437,6 @@ pub fn create_tba_payment_calldata( Ok(format!("0x{}", hex::encode(tba_calldata))) } -/// Helper function to create a PaymasterConfig with TBA address for gasless transactions. -pub fn create_paymaster_config_with_tba(tba_address: Option<&str>) -> PaymasterConfig { - let mut config = PaymasterConfig::default(); - config.tba_address = tba_address.map(|s| s.to_string()); - config -} - // === INTERNAL HELPERS === /// Internal helper to build HyperwalletRequest with session context. diff --git a/src/hyperwallet_client/mod.rs b/src/hyperwallet_client/mod.rs index 89740d9..371eaca 100644 --- a/src/hyperwallet_client/mod.rs +++ b/src/hyperwallet_client/mod.rs @@ -66,10 +66,10 @@ pub use types::{ // Re-export all API functions for direct access pub use api::{ - build_and_sign_user_operation_for_payment, create_paymaster_config_with_tba, - create_tba_payment_calldata, create_wallet, delete_wallet, execute_gasless_payment, - export_wallet, get_balance, get_token_balance, get_user_operation_receipt, get_wallet_info, - import_wallet, list_wallets, send_eth, send_token, submit_user_operation, unlock_wallet, + build_and_sign_user_operation_for_payment, create_tba_payment_calldata, create_wallet, + delete_wallet, execute_gasless_payment, export_wallet, get_balance, get_token_balance, + get_user_operation_receipt, get_wallet_info, import_wallet, list_wallets, send_eth, send_token, + submit_user_operation, unlock_wallet, }; /// Errors that can occur when interacting with the Hyperwallet client. diff --git a/src/hyperwallet_client/types.rs b/src/hyperwallet_client/types.rs index d527fa6..946083f 100644 --- a/src/hyperwallet_client/types.rs +++ b/src/hyperwallet_client/types.rs @@ -448,10 +448,27 @@ impl HyperwalletResponse { } } +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct BuildAndSignUserOperationForPaymentRequest { + /// Identifier for the EOA that will sign the UserOperation + pub eoa_wallet_id: String, + /// The TBA address that acts as the sender + pub tba_address: String, + /// Target address for the call + pub target: String, + /// Call data in hex string format + pub call_data: String, + /// Whether to use a paymaster + pub use_paymaster: bool, + /// Optional paymaster configuration + pub paymaster_config: Option, + /// Optional password for the wallet + pub password: Option, +} + /// Configuration for Circle paymaster (gasless transactions) #[derive(Debug, Clone, Serialize, Deserialize)] pub struct PaymasterConfig { - pub tba_address: Option, pub is_circle_paymaster: bool, pub paymaster_address: String, pub paymaster_verification_gas: String, @@ -461,7 +478,6 @@ pub struct PaymasterConfig { impl Default for PaymasterConfig { fn default() -> Self { Self { - tba_address: None, is_circle_paymaster: true, paymaster_address: "0x0578cFB241215b77442a541325d6A4E6dFE700Ec".to_string(), // Base Circle paymaster paymaster_verification_gas: "0x7a120".to_string(), // 500000 @@ -470,24 +486,6 @@ impl Default for PaymasterConfig { } } -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct BuildAndSignUserOperationForPaymentRequest { - /// Identifier for the EOA that will sign the UserOperation - pub eoa_wallet_id: String, - /// The TBA address that acts as the sender - pub tba_address: String, - /// Target address for the call - pub target: String, - /// Call data in hex string format - pub call_data: String, - /// Whether to use a paymaster - pub use_paymaster: bool, - /// Optional paymaster configuration - pub paymaster_config: Option, - /// Optional password for the wallet - pub password: Option, -} - #[derive(Debug, Clone, Serialize, Deserialize)] pub struct CreateWalletRequest { pub name: String, From d402194708a87dac526f7c968c92a2a5bbcebe75 Mon Sep 17 00:00:00 2001 From: Hallmane Date: Thu, 31 Jul 2025 00:45:06 +0200 Subject: [PATCH 82/96] Address --- src/hyperwallet_client/types.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/hyperwallet_client/types.rs b/src/hyperwallet_client/types.rs index 946083f..e8f9a48 100644 --- a/src/hyperwallet_client/types.rs +++ b/src/hyperwallet_client/types.rs @@ -3,7 +3,7 @@ use serde::{Deserialize, Serialize}; use std::collections::HashSet; -pub type ProcessAddress = String; +pub type ProcessAddress = crate::Address; pub type WalletAddress = String; pub type ChainId = u64; pub type SessionId = String; @@ -861,7 +861,7 @@ pub enum UpdatableSetting { #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ProcessPermissions { - pub process_address: ProcessAddress, + pub address: ProcessAddress, pub allowed_operations: HashSet, pub spending_limits: Option, pub updatable_settings: Vec, @@ -870,9 +870,9 @@ pub struct ProcessPermissions { impl ProcessPermissions { /// Create new ProcessPermissions for a process during handshake registration - pub fn new(process_address: String, required_operations: Vec) -> Self { + pub fn new(address: crate::Address, required_operations: Vec) -> Self { Self { - process_address, + address, allowed_operations: required_operations.into_iter().collect(), spending_limits: None, updatable_settings: vec![], From f934133a6fc4e8284b42a6b48f46058f84638c27 Mon Sep 17 00:00:00 2001 From: Hallmane Date: Thu, 31 Jul 2025 02:10:38 +0200 Subject: [PATCH 83/96] response enum --- src/hyperwallet_client/mod.rs | 28 ++++-- src/hyperwallet_client/types.rs | 152 ++++++++++++++++++++++++++++---- 2 files changed, 157 insertions(+), 23 deletions(-) diff --git a/src/hyperwallet_client/mod.rs b/src/hyperwallet_client/mod.rs index 371eaca..4c780c0 100644 --- a/src/hyperwallet_client/mod.rs +++ b/src/hyperwallet_client/mod.rs @@ -4,6 +4,15 @@ //! wallets and perform blockchain operations. The primary entry point is the `initialize` //! function, which performs the handshake protocol. //! +//! ## Type-Safe Architecture +//! +//! The library uses `HyperwalletResponseData` enum instead of `serde_json::Value` for responses, +//! providing full type safety throughout the system: +//! +//! - **Individual Functions**: Return specific types like `HyperwalletResponse` +//! - **Message Dispatcher**: Returns `HyperwalletResponse` with enum variants +//! - **Benefits**: Compile-time type checking, better IDE support, no information loss +//! //! ## Example Usage //! //! ```rust @@ -54,14 +63,17 @@ pub mod api; // Re-export the most commonly used types for convenience pub use types::{ Balance, BuildAndSignUserOperationForPaymentRequest, BuildAndSignUserOperationResponse, - CreateWalletRequest, ErrorCode, ExportWalletRequest, ExportWalletResponse, - GetTokenBalanceRequest, GetTokenBalanceResponse, HandshakeConfig, HandshakeRequest, - HandshakeStep, HyperwalletMessage, HyperwalletRequest, HyperwalletResponse, - ImportWalletRequest, ListWalletsResponse, Operation, OperationCategory, OperationError, - PaymasterConfig, ProcessPermissions, SendEthRequest, SendTokenRequest, SessionId, SessionInfo, - SpendingLimits, SubmitUserOperationRequest, SubmitUserOperationResponse, TxReceipt, - UnlockWalletRequest, UpdatableSetting, UserOperationReceiptResponse, Wallet, - WalletSpendingLimits, + ChainId, CheckTbaOwnershipResponse, CreateNoteResponse, CreateWalletRequest, + CreateWalletResponse, DeleteWalletRequest, DeleteWalletResponse, ErrorCode, + ExecuteViaTbaResponse, ExportWalletRequest, ExportWalletResponse, GetBalanceRequest, + GetBalanceResponse, GetTokenBalanceRequest, GetTokenBalanceResponse, GetWalletInfoRequest, + GetWalletInfoResponse, HandshakeConfig, HandshakeRequest, HandshakeResponseData, HandshakeStep, + HyperwalletMessage, HyperwalletRequest, HyperwalletResponse, HyperwalletResponseData, + ImportWalletRequest, ImportWalletResponse, ListWalletsResponse, Operation, OperationCategory, + OperationError, PaymasterConfig, ProcessAddress, ProcessPermissions, SendEthRequest, + SendEthResponse, SendTokenRequest, SendTokenResponse, SessionId, SessionInfo, SpendingLimits, + SubmitUserOperationResponse, TxReceipt, UnlockWalletResponse, UpdatableSetting, + UserOperationHash, UserOperationReceiptResponse, WalletAddress, }; // Re-export all API functions for direct access diff --git a/src/hyperwallet_client/types.rs b/src/hyperwallet_client/types.rs index e8f9a48..799fb73 100644 --- a/src/hyperwallet_client/types.rs +++ b/src/hyperwallet_client/types.rs @@ -886,7 +886,136 @@ impl ProcessPermissions { // API Result Structs -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct HandshakeResponseData { + pub session_id: SessionId, + pub session_info: SessionInfo, +} + +#[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(HandshakeResponseData), + 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, @@ -913,31 +1042,31 @@ pub struct TxReceipt { pub details: serde_json::Value, } -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub struct Balance { pub formatted: String, pub raw: String, // U256 as string } -#[derive(Debug, Serialize, Deserialize)] +#[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, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub struct SubmitUserOperationResponse { pub user_op_hash: String, } -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub struct ExportWalletResponse { pub address: String, pub private_key: String, } -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub struct ListWalletsResponse { pub process: String, pub wallets: Vec, @@ -946,14 +1075,14 @@ pub struct ListWalletsResponse { // === NEW RESPONSE STRUCTS FOR TYPE SAFETY === -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub struct GetTokenBalanceResponse { pub balance: String, pub formatted: Option, pub decimals: Option, } -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub struct UserOperationReceiptResponse { pub receipt: Option, // Transaction receipt if mined pub user_op_hash: String, @@ -1010,13 +1139,6 @@ pub struct TransactionHistoryItem { pub status: String, // "success", "failed", "pending" } -#[derive(Debug, Serialize, Deserialize)] -pub struct CheckTbaOwnershipResponse { - pub is_owner: bool, - pub tba_address: String, - pub owner_address: String, -} - #[derive(Debug, Serialize, Deserialize)] pub struct ResolveIdentityResponse { pub address: Option, From f22a6438d391632ea0eb642141d39df9a0cffd49 Mon Sep 17 00:00:00 2001 From: Hallmane Date: Thu, 31 Jul 2025 02:39:43 +0200 Subject: [PATCH 84/96] wrong handshake step --- src/hyperwallet_client/mod.rs | 10 +++++----- src/hyperwallet_client/types.rs | 8 +------- 2 files changed, 6 insertions(+), 12 deletions(-) diff --git a/src/hyperwallet_client/mod.rs b/src/hyperwallet_client/mod.rs index 4c780c0..993a9d0 100644 --- a/src/hyperwallet_client/mod.rs +++ b/src/hyperwallet_client/mod.rs @@ -67,11 +67,11 @@ pub use types::{ CreateWalletResponse, DeleteWalletRequest, DeleteWalletResponse, ErrorCode, ExecuteViaTbaResponse, ExportWalletRequest, ExportWalletResponse, GetBalanceRequest, GetBalanceResponse, GetTokenBalanceRequest, GetTokenBalanceResponse, GetWalletInfoRequest, - GetWalletInfoResponse, HandshakeConfig, HandshakeRequest, HandshakeResponseData, HandshakeStep, - HyperwalletMessage, HyperwalletRequest, HyperwalletResponse, HyperwalletResponseData, - ImportWalletRequest, ImportWalletResponse, ListWalletsResponse, Operation, OperationCategory, - OperationError, PaymasterConfig, ProcessAddress, ProcessPermissions, SendEthRequest, - SendEthResponse, SendTokenRequest, SendTokenResponse, SessionId, SessionInfo, SpendingLimits, + GetWalletInfoResponse, HandshakeConfig, HandshakeRequest, HandshakeStep, HyperwalletMessage, + HyperwalletRequest, HyperwalletResponse, HyperwalletResponseData, ImportWalletRequest, + ImportWalletResponse, ListWalletsResponse, Operation, OperationCategory, OperationError, + PaymasterConfig, ProcessAddress, ProcessPermissions, SendEthRequest, SendEthResponse, + SendTokenRequest, SendTokenResponse, SessionId, SessionInfo, SpendingLimits, SubmitUserOperationResponse, TxReceipt, UnlockWalletResponse, UpdatableSetting, UserOperationHash, UserOperationReceiptResponse, WalletAddress, }; diff --git a/src/hyperwallet_client/types.rs b/src/hyperwallet_client/types.rs index 799fb73..52fc816 100644 --- a/src/hyperwallet_client/types.rs +++ b/src/hyperwallet_client/types.rs @@ -886,12 +886,6 @@ impl ProcessPermissions { // API Result Structs -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct HandshakeResponseData { - pub session_id: SessionId, - pub session_info: SessionInfo, -} - #[derive(Debug, Clone, Serialize, Deserialize)] pub struct UnlockWalletResponse { pub success: bool, @@ -983,7 +977,7 @@ pub struct CheckTbaOwnershipResponse { #[serde(tag = "operation_type")] pub enum HyperwalletResponseData { // Session Management - Handshake(HandshakeResponseData), + Handshake(HandshakeStep), UnlockWallet(UnlockWalletResponse), // Wallet Lifecycle From f89fb35cbe3d6f8a11eea35e8315e4ab92dfc420 Mon Sep 17 00:00:00 2001 From: Hallmane Date: Thu, 31 Jul 2025 10:50:19 +0200 Subject: [PATCH 85/96] pre-pr --- src/hyperwallet_client/api.rs | 74 ++++++++-------------- src/hyperwallet_client/mod.rs | 105 ++++++-------------------------- src/hyperwallet_client/types.rs | 65 +++++++++----------- 3 files changed, 74 insertions(+), 170 deletions(-) diff --git a/src/hyperwallet_client/api.rs b/src/hyperwallet_client/api.rs index 5531bd5..11a2602 100644 --- a/src/hyperwallet_client/api.rs +++ b/src/hyperwallet_client/api.rs @@ -1,31 +1,24 @@ -//! Clean, simple API functions for Hyperwallet operations. -//! -//! These functions follow the HTTP client pattern: simple parameters in, results out. -//! All the complex message construction is handled internally. - use super::types::{ self, Balance, BuildAndSignUserOperationForPaymentRequest, BuildAndSignUserOperationResponse, CreateWalletRequest, ExportWalletResponse, GetTokenBalanceResponse, HyperwalletMessage, HyperwalletRequest, HyperwalletResponse, ImportWalletRequest, ListWalletsResponse, - PaymasterConfig, SendEthRequest, SendTokenRequest, SessionId, SubmitUserOperationResponse, - TxReceipt, UnlockWalletRequest, UserOperationReceiptResponse, Wallet, + PaymasterConfig, RenameWalletRequest, SendEthRequest, SendTokenRequest, SessionId, + SubmitUserOperationResponse, TxReceipt, UnlockWalletRequest, UserOperationReceiptResponse, + Wallet, }; use super::HyperwalletClientError; use crate::wallet; use alloy_primitives::{Address as EthAddress, U256}; -// === WALLET MANAGEMENT === - -/// Creates a new wallet for the process. pub fn create_wallet( session_id: &SessionId, - name: &str, + name: Option<&str>, password: Option<&str>, ) -> Result { let request = build_request( session_id, CreateWalletRequest { - name: name.to_string(), + name: name.map(|s| s.to_string()).unwrap_or_default(), password: password.map(|s| s.to_string()), }, ); @@ -39,7 +32,6 @@ pub fn create_wallet( }) } -/// Imports a wallet from a private key. pub fn import_wallet( session_id: &SessionId, name: &str, @@ -64,7 +56,6 @@ pub fn import_wallet( }) } -/// Unlocks a wallet with the provided password. pub fn unlock_wallet( session_id: &SessionId, target_session_id: &str, @@ -85,8 +76,7 @@ pub fn unlock_wallet( Ok(()) } -/// Lists all wallets accessible to the process. -pub fn list_wallets(session_id: &SessionId) -> Result, HyperwalletClientError> { +pub fn list_wallets(session_id: &SessionId) -> Result { let request = build_request(session_id, ()); let message = HyperwalletMessage::ListWallets(request); @@ -97,10 +87,9 @@ pub fn list_wallets(session_id: &SessionId) -> Result, HyperwalletCl )) })?; - Ok(list_response.wallets) + Ok(list_response) } -/// Gets detailed information about a specific wallet. pub fn get_wallet_info( session_id: &SessionId, wallet_id: &str, @@ -121,7 +110,6 @@ pub fn get_wallet_info( }) } -/// Deletes a wallet permanently. pub fn delete_wallet( session_id: &SessionId, wallet_id: &str, @@ -138,7 +126,6 @@ pub fn delete_wallet( Ok(()) } -/// Exports a wallet's private key. pub fn export_wallet( session_id: &SessionId, wallet_id: &str, @@ -161,9 +148,25 @@ pub fn export_wallet( }) } +pub fn rename_wallet( + session_id: &SessionId, + wallet_id: &str, + new_name: &str, +) -> Result<(), HyperwalletClientError> { + let request = build_request( + session_id, + 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(()) +} + // === TRANSACTIONS === -/// Sends ETH from a managed wallet. pub fn send_eth( session_id: &SessionId, wallet_id: &str, @@ -188,7 +191,6 @@ pub fn send_eth( }) } -/// Sends tokens from a managed wallet. pub fn send_token( session_id: &SessionId, wallet_id: &str, @@ -217,7 +219,6 @@ pub fn send_token( // === QUERIES === -/// Retrieves the native balance of a managed wallet. pub fn get_balance( session_id: &SessionId, wallet_id: &str, @@ -238,7 +239,6 @@ pub fn get_balance( }) } -/// Gets the token balance for a wallet. pub fn get_token_balance( session_id: &SessionId, wallet_id: &str, @@ -263,7 +263,6 @@ pub fn get_token_balance( // === ACCOUNT ABSTRACTION === -/// Build and sign a UserOperation for gasless payments. pub fn build_and_sign_user_operation_for_payment( session_id: &SessionId, eoa_wallet_id: &str, @@ -297,7 +296,6 @@ pub fn build_and_sign_user_operation_for_payment( }) } -/// Submit a UserOperation to the network. pub fn submit_user_operation( session_id: &SessionId, signed_user_operation: serde_json::Value, @@ -325,7 +323,6 @@ pub fn submit_user_operation( Ok(data.user_op_hash) } -/// Get the receipt for a UserOperation. pub fn get_user_operation_receipt( session_id: &SessionId, user_op_hash: &str, @@ -348,7 +345,6 @@ pub fn get_user_operation_receipt( // === CONVENIENCE FUNCTIONS === -/// Complete gasless payment flow in one function. pub fn execute_gasless_payment( session_id: &SessionId, signer_wallet_id: &str, @@ -400,7 +396,6 @@ pub fn execute_gasless_payment( // === HELPER FUNCTIONS === -/// Creates TBA execute calldata for an ERC20 transfer payment. pub fn create_tba_payment_calldata( usdc_contract: &str, recipient_address: &str, @@ -419,35 +414,20 @@ pub fn create_tba_payment_calldata( )) })?; - // Convert USDC amount to units (6 decimals) - let amount_units = amount_usdc * 1_000_000; - - // Create ERC20 transfer calldata using wallet.rs let erc20_calldata = - wallet::create_erc20_transfer_calldata(recipient_addr, U256::from(amount_units)); + wallet::create_erc20_transfer_calldata(recipient_addr, U256::from(amount_usdc)); // Create TBA execute calldata using wallet.rs - let tba_calldata = wallet::create_tba_userop_calldata( - usdc_addr, // target: USDC contract - U256::ZERO, // value: 0 (no ETH transfer) - erc20_calldata, // data: ERC20 transfer calldata - 0, // operation: 0 = CALL - ); + let tba_calldata = wallet::create_tba_userop_calldata(usdc_addr, U256::ZERO, erc20_calldata, 0); Ok(format!("0x{}", hex::encode(tba_calldata))) } // === INTERNAL HELPERS === -/// Internal helper to build HyperwalletRequest with session context. fn build_request(session_id: &SessionId, operation_data: T) -> HyperwalletRequest { HyperwalletRequest { - operation: operation_data, + data: operation_data, session_id: session_id.clone(), } } - -/// Get current timestamp for message construction. -fn current_timestamp() -> u64 { - super::current_timestamp() -} diff --git a/src/hyperwallet_client/mod.rs b/src/hyperwallet_client/mod.rs index 993a9d0..8547aec 100644 --- a/src/hyperwallet_client/mod.rs +++ b/src/hyperwallet_client/mod.rs @@ -1,66 +1,8 @@ -//! High-level, ergonomic client for interacting with the `hyperwallet:hyperwallet:*` service. -//! -//! This module provides a type-safe and convenient way for Hyperware processes to manage -//! wallets and perform blockchain operations. The primary entry point is the `initialize` -//! function, which performs the handshake protocol. -//! -//! ## Type-Safe Architecture -//! -//! The library uses `HyperwalletResponseData` enum instead of `serde_json::Value` for responses, -//! providing full type safety throughout the system: -//! -//! - **Individual Functions**: Return specific types like `HyperwalletResponse` -//! - **Message Dispatcher**: Returns `HyperwalletResponse` with enum variants -//! - **Benefits**: Compile-time type checking, better IDE support, no information loss -//! -//! ## Example Usage -//! -//! ```rust -//! use hyperware_process_lib::hyperwallet_client::{self, HandshakeConfig, OperationCategory}; -//! -//! // Initialize hyperwallet connection using category-based permissions -//! let config = HandshakeConfig::new() -//! .require_category(OperationCategory::WalletManagement) -//! .require_category(OperationCategory::ERC4337) -//! .with_initial_chain(8453); // Set default chain to Base mainnet -//! -//! let session = hyperwallet_client::initialize(config)?; -//! -//! // Create a wallet (clean direct access) -//! let wallet = hyperwallet_client::create_wallet( -//! &session.session_id, -//! "MyWallet", -//! Some("password123") -//! )?; -//! -//! // Send ETH (no more ::api:: indirection!) -//! let receipt = hyperwallet_client::send_eth( -//! &session.session_id, -//! &wallet.address, -//! "0x0000000000000000000000000000000000000000", -//! "1.5" -//! )?; -//! -//! // Execute gasless payments directly -//! let tx_hash = hyperwallet_client::execute_gasless_payment( -//! &session.session_id, -//! "signer_wallet", -//! "0x742d35...", // TBA address -//! "0x456789...", // recipient -//! 1_000_000 // 1 USDC -//! )?; -//! ``` - use crate::Request; use thiserror::Error; -// Export the types module for advanced usage -pub mod types; - -// Export the clean API functions pub mod api; - -// Re-export the most commonly used types for convenience +pub mod types; pub use types::{ Balance, BuildAndSignUserOperationForPaymentRequest, BuildAndSignUserOperationResponse, ChainId, CheckTbaOwnershipResponse, CreateNoteResponse, CreateWalletRequest, @@ -70,21 +12,19 @@ pub use types::{ GetWalletInfoResponse, HandshakeConfig, HandshakeRequest, HandshakeStep, HyperwalletMessage, HyperwalletRequest, HyperwalletResponse, HyperwalletResponseData, ImportWalletRequest, ImportWalletResponse, ListWalletsResponse, Operation, OperationCategory, OperationError, - PaymasterConfig, ProcessAddress, ProcessPermissions, SendEthRequest, SendEthResponse, - SendTokenRequest, SendTokenResponse, SessionId, SessionInfo, SpendingLimits, + PaymasterConfig, ProcessAddress, ProcessPermissions, RenameWalletRequest, SendEthRequest, + SendEthResponse, SendTokenRequest, SendTokenResponse, SessionId, SessionInfo, SpendingLimits, SubmitUserOperationResponse, TxReceipt, UnlockWalletResponse, UpdatableSetting, UserOperationHash, UserOperationReceiptResponse, WalletAddress, }; -// Re-export all API functions for direct access pub use api::{ build_and_sign_user_operation_for_payment, create_tba_payment_calldata, create_wallet, delete_wallet, execute_gasless_payment, export_wallet, get_balance, get_token_balance, - get_user_operation_receipt, get_wallet_info, import_wallet, list_wallets, send_eth, send_token, - submit_user_operation, unlock_wallet, + get_user_operation_receipt, get_wallet_info, import_wallet, list_wallets, rename_wallet, + send_eth, send_token, submit_user_operation, unlock_wallet, }; -/// Errors that can occur when interacting with the Hyperwallet client. #[derive(Debug, Error)] pub enum HyperwalletClientError { #[error("Handshake Error: Version incompatibility - client {client} vs server {server}")] @@ -103,21 +43,15 @@ pub enum HyperwalletClientError { /// Performs the full handshake and registration protocol with the Hyperwallet service. pub fn initialize(config: HandshakeConfig) -> Result { - let our = crate::our(); - let client_name = config - .client_name - .unwrap_or_else(|| our.process().to_string()); + let client_name = config.client_name.expect("Client name is required"); - // Step 1: Send ClientHello let hello_step = types::HandshakeStep::ClientHello { - client_version: env!("CARGO_PKG_VERSION").to_string(), + client_version: "0.1.0".to_string(), client_name, }; - let hello_message = types::HyperwalletMessage::Handshake(types::HandshakeRequest { - operation: hello_step, - }); + let hello_message = + types::HyperwalletMessage::Handshake(types::HandshakeRequest { step: hello_step }); - // Step 2: Receive ServerWelcome with type safety let welcome_response: types::HyperwalletResponse = send_message(hello_message)?; @@ -141,7 +75,6 @@ pub fn initialize(config: HandshakeConfig) -> Result Result = send_message(register_message)?; @@ -180,12 +111,14 @@ pub fn initialize(config: HandshakeConfig) -> Result Ok(SessionInfo { - server_version: env!("CARGO_PKG_VERSION").to_string(), - session_id, - registered_permissions, - initial_chain_id: config.initial_chain_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, + }) + } _ => Err(HyperwalletClientError::ServerError( types::OperationError::internal_error( "Expected Complete handshake step, received different step", @@ -196,7 +129,6 @@ pub fn initialize(config: HandshakeConfig) -> Result( message: types::HyperwalletMessage, ) -> Result, HyperwalletClientError> @@ -224,7 +156,6 @@ where Ok(hyperwallet_response) } -/// Get current timestamp for message construction. pub fn current_timestamp() -> u64 { std::time::SystemTime::now() .duration_since(std::time::UNIX_EPOCH) diff --git a/src/hyperwallet_client/types.rs b/src/hyperwallet_client/types.rs index 52fc816..f18925c 100644 --- a/src/hyperwallet_client/types.rs +++ b/src/hyperwallet_client/types.rs @@ -60,7 +60,6 @@ pub enum Operation { CancelOperation, } -/// Categories for grouping operations. #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] pub enum OperationCategory { System, @@ -272,13 +271,13 @@ pub enum HandshakeStep { #[derive(Debug, Clone, Serialize, Deserialize)] pub struct HyperwalletRequest { - pub operation: T, + pub data: T, pub session_id: SessionId, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct HandshakeRequest { - pub operation: T, + pub step: T, } /// Typed message enum for type-safe communication @@ -450,19 +449,12 @@ impl HyperwalletResponse { #[derive(Debug, Clone, Serialize, Deserialize)] pub struct BuildAndSignUserOperationForPaymentRequest { - /// Identifier for the EOA that will sign the UserOperation pub eoa_wallet_id: String, - /// The TBA address that acts as the sender pub tba_address: String, - /// Target address for the call pub target: String, - /// Call data in hex string format pub call_data: String, - /// Whether to use a paymaster pub use_paymaster: bool, - /// Optional paymaster configuration pub paymaster_config: Option, - /// Optional password for the wallet pub password: Option, } @@ -494,7 +486,7 @@ pub struct CreateWalletRequest { #[derive(Debug, Clone, Serialize, Deserialize)] pub struct UnlockWalletRequest { - pub session_id: String, + pub session_id: SessionId, pub wallet_id: String, pub password: String, } @@ -508,6 +500,7 @@ pub struct ImportWalletRequest { #[derive(Debug, Clone, Serialize, Deserialize)] pub struct RenameWalletRequest { + pub wallet_id: String, pub new_name: String, } @@ -581,15 +574,15 @@ pub struct ResolveIdentityRequest { #[derive(Debug, Clone, Serialize, Deserialize)] pub struct CallContractRequest { pub to: String, - pub data: String, // hex-encoded calldata - pub value: Option, // wei amount as string + pub data: String, + pub value: Option, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct SignTransactionRequest { pub to: String, - pub value: String, // wei amount as string - pub data: Option, // hex-encoded data + pub value: String, + pub data: Option, pub gas_limit: Option, pub gas_price: Option, pub nonce: Option, @@ -597,8 +590,8 @@ pub struct SignTransactionRequest { #[derive(Debug, Clone, Serialize, Deserialize)] pub struct SignMessageRequest { - pub message: String, // message to sign - pub message_type: MessageType, // EIP-191, EIP-712, etc. + pub message: String, + pub message_type: MessageType, } #[derive(Debug, Clone, Serialize, Deserialize)] @@ -622,8 +615,8 @@ pub struct GetTransactionHistoryRequest { #[derive(Debug, Clone, Serialize, Deserialize)] pub struct EstimateGasRequest { pub to: String, - pub data: Option, // hex-encoded calldata - pub value: Option, // wei amount as string + pub data: Option, + pub value: Option, } #[derive(Debug, Clone, Serialize, Deserialize)] @@ -635,7 +628,7 @@ pub struct GetTransactionReceiptRequest { pub struct SetupTbaDelegationRequest { pub tba_address: String, pub delegate_address: String, - pub permissions: Vec, // permission types + pub permissions: Vec, } #[derive(Debug, Clone, Serialize, Deserialize)] @@ -647,7 +640,7 @@ pub struct BuildUserOperationRequest { #[derive(Debug, Clone, Serialize, Deserialize)] pub struct SignUserOperationRequest { - pub unsigned_user_operation: serde_json::Value, // UserOperation struct + pub unsigned_user_operation: serde_json::Value, pub entry_point: String, } @@ -661,14 +654,14 @@ pub struct BuildAndSignUserOperationRequest { #[derive(Debug, Clone, Serialize, Deserialize)] pub struct EstimateUserOperationGasRequest { - pub user_operation: serde_json::Value, // UserOperation struct + 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, // hex-encoded data + pub paymaster_data: Option, pub verification_gas_limit: String, pub post_op_gas_limit: String, } @@ -682,20 +675,20 @@ pub struct ReadNoteRequest { pub struct SetupDelegationRequest { pub delegate_address: String, pub permissions: Vec, - pub expiry: Option, // timestamp + pub expiry: Option, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct VerifyDelegationRequest { pub delegate_address: String, - pub signature: String, // hex-encoded signature + pub signature: String, pub message: String, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct MintEntryRequest { pub entry_name: String, - pub metadata: serde_json::Value, // entry metadata + pub metadata: serde_json::Value, } #[derive(Debug, Clone, Serialize, Deserialize)] @@ -705,7 +698,7 @@ pub struct UpdateSpendingLimitsRequest { #[derive(Debug, Clone, Serialize, Deserialize)] pub struct CreateNoteRequest { - pub note_data: serde_json::Value, // flexible note content + pub note_data: serde_json::Value, pub metadata: Option, } @@ -1039,7 +1032,7 @@ pub struct TxReceipt { #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Balance { pub formatted: String, - pub raw: String, // U256 as string + pub raw: String, } #[derive(Debug, Clone, Serialize, Deserialize)] @@ -1078,38 +1071,38 @@ pub struct GetTokenBalanceResponse { #[derive(Debug, Clone, Serialize, Deserialize)] pub struct UserOperationReceiptResponse { - pub receipt: Option, // Transaction receipt if mined + pub receipt: Option, pub user_op_hash: String, - pub status: String, // "pending", "included", "failed" + pub status: String, } #[derive(Debug, Serialize, Deserialize)] pub struct CallContractResponse { - pub result: String, // hex-encoded result + pub result: String, pub gas_used: Option, } #[derive(Debug, Serialize, Deserialize)] pub struct SignTransactionResponse { - pub signed_transaction: String, // hex-encoded signed transaction + pub signed_transaction: String, pub transaction_hash: String, } #[derive(Debug, Serialize, Deserialize)] pub struct SignMessageResponse { - pub signature: String, // hex-encoded signature + pub signature: String, pub message_hash: Option, } #[derive(Debug, Serialize, Deserialize)] pub struct EstimateGasResponse { - pub gas_estimate: String, // gas amount as string + pub gas_estimate: String, pub gas_price: Option, } #[derive(Debug, Serialize, Deserialize)] pub struct GetGasPriceResponse { - pub gas_price: String, // in wei as string + pub gas_price: String, pub fast_gas_price: Option, pub standard_gas_price: Option, pub safe_gas_price: Option, @@ -1130,7 +1123,7 @@ pub struct TransactionHistoryItem { pub value: String, pub gas_used: Option, pub timestamp: u64, - pub status: String, // "success", "failed", "pending" + pub status: String, } #[derive(Debug, Serialize, Deserialize)] From 91b4bf4d51013471f5530bdaf3413a52602d06c2 Mon Sep 17 00:00:00 2001 From: Hallman <41445354+Hallmane@users.noreply.github.com> Date: Thu, 31 Jul 2025 18:58:02 +0200 Subject: [PATCH 86/96] Delete Cargo.lock --- Cargo.lock | 3925 ---------------------------------------------------- 1 file changed, 3925 deletions(-) delete mode 100644 Cargo.lock diff --git a/Cargo.lock b/Cargo.lock deleted file mode 100644 index 2360858..0000000 --- a/Cargo.lock +++ /dev/null @@ -1,3925 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 4 - -[[package]] -name = "addr2line" -version = "0.21.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" -dependencies = [ - "gimli", -] - -[[package]] -name = "adler" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" - -[[package]] -name = "ahash" -version = "0.8.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" -dependencies = [ - "cfg-if", - "getrandom", - "once_cell", - "version_check", - "zerocopy", -] - -[[package]] -name = "aho-corasick" -version = "1.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" -dependencies = [ - "memchr", -] - -[[package]] -name = "allocator-api2" -version = "0.2.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" - -[[package]] -name = "alloy" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59febb24956a41c29bb5f450978fbe825bd6456b3f80586c8bd558dc882e7b6a" -dependencies = [ - "alloy-consensus", - "alloy-core", - "alloy-eips", - "alloy-genesis", - "alloy-json-rpc", - "alloy-network", - "alloy-provider", - "alloy-rpc-client", - "alloy-rpc-types", - "alloy-serde", - "alloy-signer", - "alloy-signer-local", - "alloy-transport", - "alloy-transport-http", -] - -[[package]] -name = "alloy-chains" -version = "0.1.49" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830045a4421ee38d3ab570d36d4d2b5152c066e72797139224da8de5d5981fd0" -dependencies = [ - "alloy-primitives", - "num_enum", - "strum", -] - -[[package]] -name = "alloy-consensus" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e88e1edea70787c33e11197d3f32ae380f3db19e6e061e539a5bcf8184a6b326" -dependencies = [ - "alloy-eips", - "alloy-primitives", - "alloy-rlp", - "alloy-serde", - "alloy-trie", - "auto_impl", - "c-kzg", - "derive_more 1.0.0", - "serde", -] - -[[package]] -name = "alloy-consensus-any" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57b1bb53f40c0273cd1975573cd457b39213e68584e36d1401d25fd0398a1d65" -dependencies = [ - "alloy-consensus", - "alloy-eips", - "alloy-primitives", - "alloy-rlp", - "alloy-serde", - "serde", -] - -[[package]] -name = "alloy-core" -version = "0.8.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c618bd382f0bc2ac26a7e4bfae01c9b015ca8f21b37ca40059ae35a7e62b3dc6" -dependencies = [ - "alloy-dyn-abi", - "alloy-json-abi", - "alloy-primitives", - "alloy-rlp", - "alloy-sol-types", -] - -[[package]] -name = "alloy-dyn-abi" -version = "0.8.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41056bde53ae10ffbbf11618efbe1e0290859e5eab0fe9ef82ebdb62f12a866f" -dependencies = [ - "alloy-json-abi", - "alloy-primitives", - "alloy-sol-type-parser", - "alloy-sol-types", - "const-hex", - "itoa", - "serde", - "serde_json", - "winnow 0.6.20", -] - -[[package]] -name = "alloy-eip2930" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0069cf0642457f87a01a014f6dc29d5d893cd4fd8fddf0c3cdfad1bb3ebafc41" -dependencies = [ - "alloy-primitives", - "alloy-rlp", - "serde", -] - -[[package]] -name = "alloy-eip7702" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c986539255fb839d1533c128e190e557e52ff652c9ef62939e233a81dd93f7e" -dependencies = [ - "alloy-primitives", - "alloy-rlp", - "derive_more 1.0.0", - "serde", -] - -[[package]] -name = "alloy-eips" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f9fadfe089e9ccc0650473f2d4ef0a28bc015bbca5631d9f0f09e49b557fdb3" -dependencies = [ - "alloy-eip2930", - "alloy-eip7702", - "alloy-primitives", - "alloy-rlp", - "alloy-serde", - "c-kzg", - "derive_more 1.0.0", - "once_cell", - "serde", - "sha2", -] - -[[package]] -name = "alloy-genesis" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b2a4cf7b70f3495788e74ce1c765260ffe38820a2a774ff4aacb62e31ea73f9" -dependencies = [ - "alloy-primitives", - "alloy-serde", - "alloy-trie", - "serde", -] - -[[package]] -name = "alloy-json-abi" -version = "0.8.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe6beff64ad0aa6ad1019a3db26fef565aefeb011736150ab73ed3366c3cfd1b" -dependencies = [ - "alloy-primitives", - "alloy-sol-type-parser", - "serde", - "serde_json", -] - -[[package]] -name = "alloy-json-rpc" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e29040b9d5fe2fb70415531882685b64f8efd08dfbd6cc907120650504821105" -dependencies = [ - "alloy-primitives", - "alloy-sol-types", - "serde", - "serde_json", - "thiserror 2.0.9", - "tracing", -] - -[[package]] -name = "alloy-network" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "510cc00b318db0dfccfdd2d032411cfae64fc144aef9679409e014145d3dacc4" -dependencies = [ - "alloy-consensus", - "alloy-consensus-any", - "alloy-eips", - "alloy-json-rpc", - "alloy-network-primitives", - "alloy-primitives", - "alloy-rpc-types-any", - "alloy-rpc-types-eth", - "alloy-serde", - "alloy-signer", - "alloy-sol-types", - "async-trait", - "auto_impl", - "futures-utils-wasm", - "serde", - "serde_json", - "thiserror 2.0.9", -] - -[[package]] -name = "alloy-network-primitives" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9081c099e798b8a2bba2145eb82a9a146f01fc7a35e9ab6e7b43305051f97550" -dependencies = [ - "alloy-consensus", - "alloy-eips", - "alloy-primitives", - "alloy-serde", - "serde", -] - -[[package]] -name = "alloy-primitives" -version = "0.8.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c77490fe91a0ce933a1f219029521f20fc28c2c0ca95d53fa4da9c00b8d9d4e" -dependencies = [ - "alloy-rlp", - "bytes", - "cfg-if", - "const-hex", - "derive_more 2.0.1", - "foldhash", - "hashbrown 0.15.2", - "indexmap", - "itoa", - "k256", - "keccak-asm", - "paste", - "proptest", - "rand", - "ruint", - "rustc-hash", - "serde", - "sha3", - "tiny-keccak", -] - -[[package]] -name = "alloy-provider" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc2dfaddd9a30aa870a78a4e1316e3e115ec1e12e552cbc881310456b85c1f24" -dependencies = [ - "alloy-chains", - "alloy-consensus", - "alloy-eips", - "alloy-json-rpc", - "alloy-network", - "alloy-network-primitives", - "alloy-primitives", - "alloy-rpc-client", - "alloy-rpc-types-eth", - "alloy-transport", - "alloy-transport-http", - "async-stream", - "async-trait", - "auto_impl", - "dashmap", - "futures", - "futures-utils-wasm", - "lru", - "parking_lot", - "pin-project", - "reqwest", - "schnellru", - "serde", - "serde_json", - "thiserror 2.0.9", - "tokio", - "tracing", - "url", - "wasmtimer", -] - -[[package]] -name = "alloy-rlp" -version = "0.3.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f542548a609dca89fcd72b3b9f355928cf844d4363c5eed9c5273a3dd225e097" -dependencies = [ - "alloy-rlp-derive", - "arrayvec", - "bytes", -] - -[[package]] -name = "alloy-rlp-derive" -version = "0.3.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a833d97bf8a5f0f878daf2c8451fff7de7f9de38baa5a45d936ec718d81255a" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.100", -] - -[[package]] -name = "alloy-rpc-client" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "531137b283547d5b9a5cafc96b006c64ef76810c681d606f28be9781955293b6" -dependencies = [ - "alloy-json-rpc", - "alloy-primitives", - "alloy-transport", - "alloy-transport-http", - "futures", - "pin-project", - "reqwest", - "serde", - "serde_json", - "tokio", - "tokio-stream", - "tower", - "tracing", - "url", - "wasmtimer", -] - -[[package]] -name = "alloy-rpc-types" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3410a472ce26c457e9780f708ee6bd540b30f88f1f31fdab7a11d00bd6aa1aee" -dependencies = [ - "alloy-primitives", - "alloy-rpc-types-eth", - "alloy-serde", - "serde", -] - -[[package]] -name = "alloy-rpc-types-any" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed98e1af55a7d856bfa385f30f63d8d56be2513593655c904a8f4a7ec963aa3e" -dependencies = [ - "alloy-consensus-any", - "alloy-rpc-types-eth", - "alloy-serde", -] - -[[package]] -name = "alloy-rpc-types-eth" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8737d7a6e37ca7bba9c23e9495c6534caec6760eb24abc9d5ffbaaba147818e1" -dependencies = [ - "alloy-consensus", - "alloy-consensus-any", - "alloy-eips", - "alloy-network-primitives", - "alloy-primitives", - "alloy-rlp", - "alloy-serde", - "alloy-sol-types", - "derive_more 1.0.0", - "itertools 0.13.0", - "serde", - "serde_json", -] - -[[package]] -name = "alloy-serde" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5851bf8d5ad33014bd0c45153c603303e730acc8a209450a7ae6b4a12c2789e2" -dependencies = [ - "alloy-primitives", - "serde", - "serde_json", -] - -[[package]] -name = "alloy-signer" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e10ca565da6500cca015ba35ee424d59798f2e1b85bc0dd8f81dafd401f029a" -dependencies = [ - "alloy-primitives", - "async-trait", - "auto_impl", - "elliptic-curve", - "k256", - "thiserror 2.0.9", -] - -[[package]] -name = "alloy-signer-local" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47fababf5a745133490cde927d48e50267f97d3d1209b9fc9f1d1d666964d172" -dependencies = [ - "alloy-consensus", - "alloy-network", - "alloy-primitives", - "alloy-signer", - "async-trait", - "k256", - "rand", - "thiserror 2.0.9", -] - -[[package]] -name = "alloy-sol-macro" -version = "0.8.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e10ae8e9a91d328ae954c22542415303919aabe976fe7a92eb06db1b68fd59f2" -dependencies = [ - "alloy-sol-macro-expander", - "alloy-sol-macro-input", - "proc-macro-error2", - "proc-macro2", - "quote", - "syn 2.0.100", -] - -[[package]] -name = "alloy-sol-macro-expander" -version = "0.8.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83ad5da86c127751bc607c174d6c9fe9b85ef0889a9ca0c641735d77d4f98f26" -dependencies = [ - "alloy-sol-macro-input", - "const-hex", - "heck", - "indexmap", - "proc-macro-error2", - "proc-macro2", - "quote", - "syn 2.0.100", - "syn-solidity", - "tiny-keccak", -] - -[[package]] -name = "alloy-sol-macro-input" -version = "0.8.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba3d30f0d3f9ba3b7686f3ff1de9ee312647aac705604417a2f40c604f409a9e" -dependencies = [ - "const-hex", - "dunce", - "heck", - "macro-string", - "proc-macro2", - "quote", - "syn 2.0.100", - "syn-solidity", -] - -[[package]] -name = "alloy-sol-type-parser" -version = "0.8.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d162f8524adfdfb0e4bd0505c734c985f3e2474eb022af32eef0d52a4f3935c" -dependencies = [ - "serde", - "winnow 0.7.4", -] - -[[package]] -name = "alloy-sol-types" -version = "0.8.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d43d5e60466a440230c07761aa67671d4719d46f43be8ea6e7ed334d8db4a9ab" -dependencies = [ - "alloy-json-abi", - "alloy-primitives", - "alloy-sol-macro", - "const-hex", - "serde", -] - -[[package]] -name = "alloy-transport" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "538a04a37221469cac0ce231b737fd174de2fdfcdd843bdd068cb39ed3e066ad" -dependencies = [ - "alloy-json-rpc", - "base64", - "futures-util", - "futures-utils-wasm", - "serde", - "serde_json", - "thiserror 2.0.9", - "tokio", - "tower", - "tracing", - "url", - "wasmtimer", -] - -[[package]] -name = "alloy-transport-http" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ed40eb1e1265b2911512f6aa1dcece9702d078f5a646730c45e39e2be00ac1c" -dependencies = [ - "alloy-json-rpc", - "alloy-transport", - "reqwest", - "serde_json", - "tower", - "tracing", - "url", -] - -[[package]] -name = "alloy-trie" -version = "0.7.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e428104b2445a4f929030891b3dbf8c94433a8349ba6480946bf6af7975c2f6" -dependencies = [ - "alloy-primitives", - "alloy-rlp", - "arrayvec", - "derive_more 1.0.0", - "nybbles", - "serde", - "smallvec", - "tracing", -] - -[[package]] -name = "anyhow" -version = "1.0.95" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04" - -[[package]] -name = "ark-ff" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b3235cc41ee7a12aaaf2c575a2ad7b46713a8a50bda2fc3b003a04845c05dd6" -dependencies = [ - "ark-ff-asm 0.3.0", - "ark-ff-macros 0.3.0", - "ark-serialize 0.3.0", - "ark-std 0.3.0", - "derivative", - "num-bigint", - "num-traits", - "paste", - "rustc_version 0.3.3", - "zeroize", -] - -[[package]] -name = "ark-ff" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec847af850f44ad29048935519032c33da8aa03340876d351dfab5660d2966ba" -dependencies = [ - "ark-ff-asm 0.4.2", - "ark-ff-macros 0.4.2", - "ark-serialize 0.4.2", - "ark-std 0.4.0", - "derivative", - "digest 0.10.7", - "itertools 0.10.5", - "num-bigint", - "num-traits", - "paste", - "rustc_version 0.4.1", - "zeroize", -] - -[[package]] -name = "ark-ff-asm" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db02d390bf6643fb404d3d22d31aee1c4bc4459600aef9113833d17e786c6e44" -dependencies = [ - "quote", - "syn 1.0.109", -] - -[[package]] -name = "ark-ff-asm" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ed4aa4fe255d0bc6d79373f7e31d2ea147bcf486cba1be5ba7ea85abdb92348" -dependencies = [ - "quote", - "syn 1.0.109", -] - -[[package]] -name = "ark-ff-macros" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db2fd794a08ccb318058009eefdf15bcaaaaf6f8161eb3345f907222bac38b20" -dependencies = [ - "num-bigint", - "num-traits", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "ark-ff-macros" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7abe79b0e4288889c4574159ab790824d0033b9fdcb2a112a3182fac2e514565" -dependencies = [ - "num-bigint", - "num-traits", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "ark-serialize" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d6c2b318ee6e10f8c2853e73a83adc0ccb88995aa978d8a3408d492ab2ee671" -dependencies = [ - "ark-std 0.3.0", - "digest 0.9.0", -] - -[[package]] -name = "ark-serialize" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adb7b85a02b83d2f22f89bd5cac66c9c89474240cb6207cb1efc16d098e822a5" -dependencies = [ - "ark-std 0.4.0", - "digest 0.10.7", - "num-bigint", -] - -[[package]] -name = "ark-std" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1df2c09229cbc5a028b1d70e00fdb2acee28b1055dfb5ca73eea49c5a25c4e7c" -dependencies = [ - "num-traits", - "rand", -] - -[[package]] -name = "ark-std" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94893f1e0c6eeab764ade8dc4c0db24caf4fe7cbbaafc0eba0a9030f447b5185" -dependencies = [ - "num-traits", - "rand", -] - -[[package]] -name = "arrayvec" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" -dependencies = [ - "serde", -] - -[[package]] -name = "async-stream" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b5a71a6f37880a80d1d7f19efd781e4b5de42c88f0722cc13bcb6cc2cfe8476" -dependencies = [ - "async-stream-impl", - "futures-core", - "pin-project-lite", -] - -[[package]] -name = "async-stream-impl" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.100", -] - -[[package]] -name = "async-trait" -version = "0.1.83" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.100", -] - -[[package]] -name = "auto_impl" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c87f3f15e7794432337fc718554eaa4dc8f04c9677a950ffe366f20a162ae42" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.100", -] - -[[package]] -name = "autocfg" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" - -[[package]] -name = "backtrace" -version = "0.3.71" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26b05800d2e817c8b3b4b54abd461726265fa9789ae34330622f2db9ee696f9d" -dependencies = [ - "addr2line", - "cc", - "cfg-if", - "libc", - "miniz_oxide", - "object", - "rustc-demangle", -] - -[[package]] -name = "base16ct" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" - -[[package]] -name = "base64" -version = "0.22.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" - -[[package]] -name = "base64ct" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" - -[[package]] -name = "bincode" -version = "1.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" -dependencies = [ - "serde", -] - -[[package]] -name = "bit-set" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" -dependencies = [ - "bit-vec", -] - -[[package]] -name = "bit-vec" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" - -[[package]] -name = "bitflags" -version = "2.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" - -[[package]] -name = "bitvec" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" -dependencies = [ - "funty", - "radium", - "tap", - "wyz", -] - -[[package]] -name = "block-buffer" -version = "0.10.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" -dependencies = [ - "generic-array", -] - -[[package]] -name = "blst" -version = "0.3.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4378725facc195f1a538864863f6de233b500a8862747e7f165078a419d5e874" -dependencies = [ - "cc", - "glob", - "threadpool", - "zeroize", -] - -[[package]] -name = "bumpalo" -version = "3.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" - -[[package]] -name = "byte-slice-cast" -version = "1.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3ac9f8b63eca6fd385229b3675f6cc0dc5c8a5c8a54a59d4f52ffd670d87b0c" - -[[package]] -name = "byteorder" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" - -[[package]] -name = "bytes" -version = "1.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b" -dependencies = [ - "serde", -] - -[[package]] -name = "c-kzg" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0307f72feab3300336fb803a57134159f6e20139af1357f36c54cb90d8e8928" -dependencies = [ - "blst", - "cc", - "glob", - "hex", - "libc", - "once_cell", - "serde", -] - -[[package]] -name = "cc" -version = "1.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c31a0499c1dc64f458ad13872de75c0eb7e3fdb0e67964610c914b034fc5956e" -dependencies = [ - "shlex", -] - -[[package]] -name = "cfg-if" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" - -[[package]] -name = "color-eyre" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55146f5e46f237f7423d74111267d4597b59b0dad0ffaf7303bce9945d843ad5" -dependencies = [ - "backtrace", - "color-spantrace", - "eyre", - "indenter", - "once_cell", - "owo-colors", - "tracing-error", -] - -[[package]] -name = "color-spantrace" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd6be1b2a7e382e2b98b43b2adcca6bb0e465af0bdd38123873ae61eb17a72c2" -dependencies = [ - "once_cell", - "owo-colors", - "tracing-core", - "tracing-error", -] - -[[package]] -name = "const-hex" -version = "1.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b0485bab839b018a8f1723fc5391819fea5f8f0f32288ef8a735fd096b6160c" -dependencies = [ - "cfg-if", - "cpufeatures", - "hex", - "proptest", - "serde", -] - -[[package]] -name = "const-oid" -version = "0.9.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" - -[[package]] -name = "core-foundation" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "core-foundation-sys" -version = "0.8.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" - -[[package]] -name = "cpufeatures" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16b80225097f2e5ae4e7179dd2266824648f3e2f49d9134d584b76389d31c4c3" -dependencies = [ - "libc", -] - -[[package]] -name = "crossbeam-utils" -version = "0.8.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" - -[[package]] -name = "crunchy" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" - -[[package]] -name = "crypto-bigint" -version = "0.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" -dependencies = [ - "generic-array", - "rand_core", - "subtle", - "zeroize", -] - -[[package]] -name = "crypto-common" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" -dependencies = [ - "generic-array", - "typenum", -] - -[[package]] -name = "dashmap" -version = "6.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5041cc499144891f3790297212f32a74fb938e5136a14943f338ef9e0ae276cf" -dependencies = [ - "cfg-if", - "crossbeam-utils", - "hashbrown 0.14.5", - "lock_api", - "once_cell", - "parking_lot_core", -] - -[[package]] -name = "der" -version = "0.7.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0" -dependencies = [ - "const-oid", - "zeroize", -] - -[[package]] -name = "derivative" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "derive_more" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a9b99b9cbbe49445b21764dc0625032a89b145a2642e67603e1c936f5458d05" -dependencies = [ - "derive_more-impl 1.0.0", -] - -[[package]] -name = "derive_more" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "093242cf7570c207c83073cf82f79706fe7b8317e98620a47d5be7c3d8497678" -dependencies = [ - "derive_more-impl 2.0.1", -] - -[[package]] -name = "derive_more-impl" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.100", - "unicode-xid", -] - -[[package]] -name = "derive_more-impl" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bda628edc44c4bb645fbe0f758797143e4e07926f7ebf4e9bdfbd3d2ce621df3" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.100", - "unicode-xid", -] - -[[package]] -name = "digest" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" -dependencies = [ - "generic-array", -] - -[[package]] -name = "digest" -version = "0.10.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" -dependencies = [ - "block-buffer", - "const-oid", - "crypto-common", - "subtle", -] - -[[package]] -name = "displaydoc" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.100", -] - -[[package]] -name = "dunce" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" - -[[package]] -name = "ecdsa" -version = "0.16.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" -dependencies = [ - "der", - "digest 0.10.7", - "elliptic-curve", - "rfc6979", - "signature", - "spki", -] - -[[package]] -name = "either" -version = "1.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" - -[[package]] -name = "elliptic-curve" -version = "0.13.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" -dependencies = [ - "base16ct", - "crypto-bigint", - "digest 0.10.7", - "ff", - "generic-array", - "group", - "pkcs8", - "rand_core", - "sec1", - "subtle", - "zeroize", -] - -[[package]] -name = "equivalent" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" - -[[package]] -name = "errno" -version = "0.3.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" -dependencies = [ - "libc", - "windows-sys 0.59.0", -] - -[[package]] -name = "eyre" -version = "0.6.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cd915d99f24784cdc19fd37ef22b97e3ff0ae756c7e492e9fbfe897d61e2aec" -dependencies = [ - "indenter", - "once_cell", -] - -[[package]] -name = "fastrand" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" - -[[package]] -name = "fastrlp" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "139834ddba373bbdd213dffe02c8d110508dcf1726c2be27e8d1f7d7e1856418" -dependencies = [ - "arrayvec", - "auto_impl", - "bytes", -] - -[[package]] -name = "fastrlp" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce8dba4714ef14b8274c371879b175aa55b16b30f269663f19d576f380018dc4" -dependencies = [ - "arrayvec", - "auto_impl", - "bytes", -] - -[[package]] -name = "ff" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" -dependencies = [ - "rand_core", - "subtle", -] - -[[package]] -name = "fixed-hash" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "835c052cb0c08c1acf6ffd71c022172e18723949c8282f2b9f27efbc51e64534" -dependencies = [ - "byteorder", - "rand", - "rustc-hex", - "static_assertions", -] - -[[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - -[[package]] -name = "foldhash" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0d2fde1f7b3d48b8395d5f2de76c18a528bd6a9cdde438df747bfcba3e05d6f" - -[[package]] -name = "foreign-types" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" -dependencies = [ - "foreign-types-shared", -] - -[[package]] -name = "foreign-types-shared" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" - -[[package]] -name = "form_urlencoded" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" -dependencies = [ - "percent-encoding", -] - -[[package]] -name = "funty" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" - -[[package]] -name = "futures" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" -dependencies = [ - "futures-channel", - "futures-core", - "futures-executor", - "futures-io", - "futures-sink", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-channel" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" -dependencies = [ - "futures-core", - "futures-sink", -] - -[[package]] -name = "futures-core" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" - -[[package]] -name = "futures-executor" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" -dependencies = [ - "futures-core", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-io" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" - -[[package]] -name = "futures-macro" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.100", -] - -[[package]] -name = "futures-sink" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" - -[[package]] -name = "futures-task" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" - -[[package]] -name = "futures-util" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" -dependencies = [ - "futures-channel", - "futures-core", - "futures-io", - "futures-macro", - "futures-sink", - "futures-task", - "memchr", - "pin-project-lite", - "pin-utils", - "slab", -] - -[[package]] -name = "futures-utils-wasm" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42012b0f064e01aa58b545fe3727f90f7dd4020f4a3ea735b50344965f5a57e9" - -[[package]] -name = "generic-array" -version = "0.14.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" -dependencies = [ - "typenum", - "version_check", - "zeroize", -] - -[[package]] -name = "getrandom" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" -dependencies = [ - "cfg-if", - "libc", - "wasi", -] - -[[package]] -name = "gimli" -version = "0.28.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" - -[[package]] -name = "glob" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" - -[[package]] -name = "group" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" -dependencies = [ - "ff", - "rand_core", - "subtle", -] - -[[package]] -name = "hashbrown" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" - -[[package]] -name = "hashbrown" -version = "0.14.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" -dependencies = [ - "ahash", -] - -[[package]] -name = "hashbrown" -version = "0.15.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" -dependencies = [ - "allocator-api2", - "equivalent", - "foldhash", - "serde", -] - -[[package]] -name = "heck" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" - -[[package]] -name = "hermit-abi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" - -[[package]] -name = "hex" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" -dependencies = [ - "serde", -] - -[[package]] -name = "hmac" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" -dependencies = [ - "digest 0.10.7", -] - -[[package]] -name = "http" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f16ca2af56261c99fba8bac40a10251ce8188205a4c448fbb745a2e4daa76fea" -dependencies = [ - "bytes", - "fnv", - "itoa", -] - -[[package]] -name = "http-body" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" -dependencies = [ - "bytes", - "http", -] - -[[package]] -name = "http-body-util" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f" -dependencies = [ - "bytes", - "futures-util", - "http", - "http-body", - "pin-project-lite", -] - -[[package]] -name = "httparse" -version = "1.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d71d3574edd2771538b901e6549113b4006ece66150fb69c0fb6d9a2adae946" - -[[package]] -name = "hyper" -version = "1.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "256fb8d4bd6413123cc9d91832d78325c48ff41677595be797d90f42969beae0" -dependencies = [ - "bytes", - "futures-channel", - "futures-util", - "http", - "http-body", - "httparse", - "itoa", - "pin-project-lite", - "smallvec", - "tokio", - "want", -] - -[[package]] -name = "hyper-tls" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" -dependencies = [ - "bytes", - "http-body-util", - "hyper", - "hyper-util", - "native-tls", - "tokio", - "tokio-native-tls", - "tower-service", -] - -[[package]] -name = "hyper-util" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df2dcfbe0677734ab2f3ffa7fa7bfd4706bfdc1ef393f2ee30184aed67e631b4" -dependencies = [ - "bytes", - "futures-channel", - "futures-util", - "http", - "http-body", - "hyper", - "pin-project-lite", - "socket2", - "tokio", - "tower-service", - "tracing", -] - -[[package]] -name = "hyperware_process_lib" -version = "1.2.0" -dependencies = [ - "alloy", - "alloy-primitives", - "alloy-sol-macro", - "alloy-sol-types", - "anyhow", - "base64", - "bincode", - "color-eyre", - "hex", - "http", - "mime_guess", - "rand", - "regex", - "rmp-serde", - "serde", - "serde_json", - "sha3", - "thiserror 1.0.69", - "tracing", - "tracing-error", - "tracing-subscriber", - "url", - "wit-bindgen", -] - -[[package]] -name = "icu_collections" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" -dependencies = [ - "displaydoc", - "yoke", - "zerofrom", - "zerovec", -] - -[[package]] -name = "icu_locid" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" -dependencies = [ - "displaydoc", - "litemap", - "tinystr", - "writeable", - "zerovec", -] - -[[package]] -name = "icu_locid_transform" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" -dependencies = [ - "displaydoc", - "icu_locid", - "icu_locid_transform_data", - "icu_provider", - "tinystr", - "zerovec", -] - -[[package]] -name = "icu_locid_transform_data" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e" - -[[package]] -name = "icu_normalizer" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" -dependencies = [ - "displaydoc", - "icu_collections", - "icu_normalizer_data", - "icu_properties", - "icu_provider", - "smallvec", - "utf16_iter", - "utf8_iter", - "write16", - "zerovec", -] - -[[package]] -name = "icu_normalizer_data" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516" - -[[package]] -name = "icu_properties" -version = "1.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" -dependencies = [ - "displaydoc", - "icu_collections", - "icu_locid_transform", - "icu_properties_data", - "icu_provider", - "tinystr", - "zerovec", -] - -[[package]] -name = "icu_properties_data" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569" - -[[package]] -name = "icu_provider" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" -dependencies = [ - "displaydoc", - "icu_locid", - "icu_provider_macros", - "stable_deref_trait", - "tinystr", - "writeable", - "yoke", - "zerofrom", - "zerovec", -] - -[[package]] -name = "icu_provider_macros" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.100", -] - -[[package]] -name = "id-arena" -version = "2.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25a2bc672d1148e28034f176e01fffebb08b35768468cc954630da77a1449005" - -[[package]] -name = "idna" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" -dependencies = [ - "idna_adapter", - "smallvec", - "utf8_iter", -] - -[[package]] -name = "idna_adapter" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" -dependencies = [ - "icu_normalizer", - "icu_properties", -] - -[[package]] -name = "impl-codec" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba6a270039626615617f3f36d15fc827041df3b78c439da2cadfa47455a77f2f" -dependencies = [ - "parity-scale-codec", -] - -[[package]] -name = "impl-trait-for-tuples" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0eb5a3343abf848c0984fe4604b2b105da9539376e24fc0a3b0007411ae4fd9" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.100", -] - -[[package]] -name = "indenter" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" - -[[package]] -name = "indexmap" -version = "2.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62f822373a4fe84d4bb149bf54e584a7f4abec90e072ed49cda0edea5b95471f" -dependencies = [ - "equivalent", - "hashbrown 0.15.2", - "serde", -] - -[[package]] -name = "ipnet" -version = "2.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddc24109865250148c2e0f3d25d4f0f479571723792d3802153c60922a4fb708" - -[[package]] -name = "itertools" -version = "0.10.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" -dependencies = [ - "either", -] - -[[package]] -name = "itertools" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" -dependencies = [ - "either", -] - -[[package]] -name = "itoa" -version = "1.0.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" - -[[package]] -name = "js-sys" -version = "0.3.76" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6717b6b5b077764fb5966237269cb3c64edddde4b14ce42647430a78ced9e7b7" -dependencies = [ - "once_cell", - "wasm-bindgen", -] - -[[package]] -name = "k256" -version = "0.13.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6e3919bbaa2945715f0bb6d3934a173d1e9a59ac23767fbaaef277265a7411b" -dependencies = [ - "cfg-if", - "ecdsa", - "elliptic-curve", - "once_cell", - "sha2", -] - -[[package]] -name = "keccak" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecc2af9a1119c51f12a14607e783cb977bde58bc069ff0c3da1095e635d70654" -dependencies = [ - "cpufeatures", -] - -[[package]] -name = "keccak-asm" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "505d1856a39b200489082f90d897c3f07c455563880bc5952e38eabf731c83b6" -dependencies = [ - "digest 0.10.7", - "sha3-asm", -] - -[[package]] -name = "lazy_static" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" - -[[package]] -name = "leb128" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67" - -[[package]] -name = "libc" -version = "0.2.169" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" - -[[package]] -name = "libm" -version = "0.2.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8355be11b20d696c8f18f6cc018c4e372165b1fa8126cef092399c9951984ffa" - -[[package]] -name = "linux-raw-sys" -version = "0.4.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" - -[[package]] -name = "litemap" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ee93343901ab17bd981295f2cf0026d4ad018c7c31ba84549a4ddbb47a45104" - -[[package]] -name = "lock_api" -version = "0.4.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" -dependencies = [ - "autocfg", - "scopeguard", -] - -[[package]] -name = "log" -version = "0.4.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" - -[[package]] -name = "lru" -version = "0.12.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "234cf4f4a04dc1f57e24b96cc0cd600cf2af460d4161ac5ecdd0af8e1f3b2a38" -dependencies = [ - "hashbrown 0.15.2", -] - -[[package]] -name = "macro-string" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b27834086c65ec3f9387b096d66e99f221cf081c2b738042aa252bcd41204e3" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.100", -] - -[[package]] -name = "matchers" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" -dependencies = [ - "regex-automata 0.1.10", -] - -[[package]] -name = "memchr" -version = "2.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" - -[[package]] -name = "mime" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" - -[[package]] -name = "mime_guess" -version = "2.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e" -dependencies = [ - "mime", - "unicase", -] - -[[package]] -name = "miniz_oxide" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" -dependencies = [ - "adler", -] - -[[package]] -name = "mio" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" -dependencies = [ - "libc", - "wasi", - "windows-sys 0.52.0", -] - -[[package]] -name = "native-tls" -version = "0.2.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8614eb2c83d59d1c8cc974dd3f920198647674a0a035e1af1fa58707e317466" -dependencies = [ - "libc", - "log", - "openssl", - "openssl-probe", - "openssl-sys", - "schannel", - "security-framework", - "security-framework-sys", - "tempfile", -] - -[[package]] -name = "nu-ansi-term" -version = "0.46.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" -dependencies = [ - "overload", - "winapi", -] - -[[package]] -name = "num-bigint" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" -dependencies = [ - "num-integer", - "num-traits", -] - -[[package]] -name = "num-integer" -version = "0.1.46" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" -dependencies = [ - "num-traits", -] - -[[package]] -name = "num-traits" -version = "0.2.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" -dependencies = [ - "autocfg", - "libm", -] - -[[package]] -name = "num_cpus" -version = "1.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" -dependencies = [ - "hermit-abi", - "libc", -] - -[[package]] -name = "num_enum" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e613fc340b2220f734a8595782c551f1250e969d87d3be1ae0579e8d4065179" -dependencies = [ - "num_enum_derive", -] - -[[package]] -name = "num_enum_derive" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.100", -] - -[[package]] -name = "nybbles" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55a62e678a89501192cc5ebf47dcbc656b608ae5e1c61c9251fe35230f119fe3" -dependencies = [ - "alloy-rlp", - "const-hex", - "proptest", - "serde", - "smallvec", -] - -[[package]] -name = "object" -version = "0.32.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" -dependencies = [ - "memchr", -] - -[[package]] -name = "once_cell" -version = "1.20.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" - -[[package]] -name = "openssl" -version = "0.10.68" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6174bc48f102d208783c2c84bf931bb75927a617866870de8a4ea85597f871f5" -dependencies = [ - "bitflags", - "cfg-if", - "foreign-types", - "libc", - "once_cell", - "openssl-macros", - "openssl-sys", -] - -[[package]] -name = "openssl-macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.100", -] - -[[package]] -name = "openssl-probe" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" - -[[package]] -name = "openssl-sys" -version = "0.9.104" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45abf306cbf99debc8195b66b7346498d7b10c210de50418b5ccd7ceba08c741" -dependencies = [ - "cc", - "libc", - "pkg-config", - "vcpkg", -] - -[[package]] -name = "overload" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" - -[[package]] -name = "owo-colors" -version = "3.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f" - -[[package]] -name = "parity-scale-codec" -version = "3.6.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "306800abfa29c7f16596b5970a588435e3d5b3149683d00c12b699cc19f895ee" -dependencies = [ - "arrayvec", - "bitvec", - "byte-slice-cast", - "impl-trait-for-tuples", - "parity-scale-codec-derive", - "serde", -] - -[[package]] -name = "parity-scale-codec-derive" -version = "3.6.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d830939c76d294956402033aee57a6da7b438f2294eb94864c37b0569053a42c" -dependencies = [ - "proc-macro-crate", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "parking_lot" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" -dependencies = [ - "lock_api", - "parking_lot_core", -] - -[[package]] -name = "parking_lot_core" -version = "0.9.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" -dependencies = [ - "cfg-if", - "libc", - "redox_syscall", - "smallvec", - "windows-targets", -] - -[[package]] -name = "paste" -version = "1.0.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" - -[[package]] -name = "percent-encoding" -version = "2.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" - -[[package]] -name = "pest" -version = "2.7.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b7cafe60d6cf8e62e1b9b2ea516a089c008945bb5a275416789e7db0bc199dc" -dependencies = [ - "memchr", - "thiserror 2.0.9", - "ucd-trie", -] - -[[package]] -name = "pin-project" -version = "1.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be57f64e946e500c8ee36ef6331845d40a93055567ec57e8fae13efd33759b95" -dependencies = [ - "pin-project-internal", -] - -[[package]] -name = "pin-project-internal" -version = "1.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c0f5fad0874fc7abcd4d750e76917eaebbecaa2c20bde22e1dbeeba8beb758c" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.100", -] - -[[package]] -name = "pin-project-lite" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "915a1e146535de9163f3987b8944ed8cf49a18bb0056bcebcdcece385cece4ff" - -[[package]] -name = "pin-utils" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" - -[[package]] -name = "pkcs8" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" -dependencies = [ - "der", - "spki", -] - -[[package]] -name = "pkg-config" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" - -[[package]] -name = "ppv-lite86" -version = "0.2.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" -dependencies = [ - "zerocopy", -] - -[[package]] -name = "prettyplease" -version = "0.2.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64d1ec885c64d0457d564db4ec299b2dae3f9c02808b8ad9c3a089c591b18033" -dependencies = [ - "proc-macro2", - "syn 2.0.100", -] - -[[package]] -name = "primitive-types" -version = "0.12.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b34d9fd68ae0b74a41b21c03c2f62847aa0ffea044eee893b4c140b37e244e2" -dependencies = [ - "fixed-hash", - "impl-codec", - "uint", -] - -[[package]] -name = "proc-macro-crate" -version = "3.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ecf48c7ca261d60b74ab1a7b20da18bede46776b2e55535cb958eb595c5fa7b" -dependencies = [ - "toml_edit", -] - -[[package]] -name = "proc-macro-error-attr2" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5" -dependencies = [ - "proc-macro2", - "quote", -] - -[[package]] -name = "proc-macro-error2" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802" -dependencies = [ - "proc-macro-error-attr2", - "proc-macro2", - "quote", - "syn 2.0.100", -] - -[[package]] -name = "proc-macro2" -version = "1.0.92" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "proptest" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4c2511913b88df1637da85cc8d96ec8e43a3f8bb8ccb71ee1ac240d6f3df58d" -dependencies = [ - "bit-set", - "bit-vec", - "bitflags", - "lazy_static", - "num-traits", - "rand", - "rand_chacha", - "rand_xorshift", - "regex-syntax 0.8.5", - "rusty-fork", - "tempfile", - "unarray", -] - -[[package]] -name = "quick-error" -version = "1.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" - -[[package]] -name = "quote" -version = "1.0.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "radium" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" - -[[package]] -name = "rand" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" -dependencies = [ - "libc", - "rand_chacha", - "rand_core", - "serde", -] - -[[package]] -name = "rand_chacha" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" -dependencies = [ - "ppv-lite86", - "rand_core", -] - -[[package]] -name = "rand_core" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" -dependencies = [ - "getrandom", -] - -[[package]] -name = "rand_xorshift" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d25bf25ec5ae4a3f1b92f929810509a2f53d7dca2f50b794ff57e3face536c8f" -dependencies = [ - "rand_core", -] - -[[package]] -name = "redox_syscall" -version = "0.5.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03a862b389f93e68874fbf580b9de08dd02facb9a788ebadaf4a3fd33cf58834" -dependencies = [ - "bitflags", -] - -[[package]] -name = "regex" -version = "1.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" -dependencies = [ - "aho-corasick", - "memchr", - "regex-automata 0.4.9", - "regex-syntax 0.8.5", -] - -[[package]] -name = "regex-automata" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" -dependencies = [ - "regex-syntax 0.6.29", -] - -[[package]] -name = "regex-automata" -version = "0.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax 0.8.5", -] - -[[package]] -name = "regex-syntax" -version = "0.6.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" - -[[package]] -name = "regex-syntax" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" - -[[package]] -name = "reqwest" -version = "0.12.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a77c62af46e79de0a562e1a9849205ffcb7fc1238876e9bd743357570e04046f" -dependencies = [ - "base64", - "bytes", - "futures-core", - "futures-util", - "http", - "http-body", - "http-body-util", - "hyper", - "hyper-tls", - "hyper-util", - "ipnet", - "js-sys", - "log", - "mime", - "native-tls", - "once_cell", - "percent-encoding", - "pin-project-lite", - "rustls-pemfile", - "serde", - "serde_json", - "serde_urlencoded", - "sync_wrapper", - "tokio", - "tokio-native-tls", - "tower-service", - "url", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", - "windows-registry", -] - -[[package]] -name = "rfc6979" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" -dependencies = [ - "hmac", - "subtle", -] - -[[package]] -name = "rlp" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb919243f34364b6bd2fc10ef797edbfa75f33c252e7998527479c6d6b47e1ec" -dependencies = [ - "bytes", - "rustc-hex", -] - -[[package]] -name = "rmp" -version = "0.8.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "228ed7c16fa39782c3b3468e974aec2795e9089153cd08ee2e9aefb3613334c4" -dependencies = [ - "byteorder", - "num-traits", - "paste", -] - -[[package]] -name = "rmp-serde" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52e599a477cf9840e92f2cde9a7189e67b42c57532749bf90aea6ec10facd4db" -dependencies = [ - "byteorder", - "rmp", - "serde", -] - -[[package]] -name = "ruint" -version = "1.12.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5ef8fb1dd8de3870cb8400d51b4c2023854bbafd5431a3ac7e7317243e22d2f" -dependencies = [ - "alloy-rlp", - "ark-ff 0.3.0", - "ark-ff 0.4.2", - "bytes", - "fastrlp 0.3.1", - "fastrlp 0.4.0", - "num-bigint", - "num-integer", - "num-traits", - "parity-scale-codec", - "primitive-types", - "proptest", - "rand", - "rlp", - "ruint-macro", - "serde", - "valuable", - "zeroize", -] - -[[package]] -name = "ruint-macro" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48fd7bd8a6377e15ad9d42a8ec25371b94ddc67abe7c8b9127bec79bebaaae18" - -[[package]] -name = "rustc-demangle" -version = "0.1.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" - -[[package]] -name = "rustc-hash" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7fb8039b3032c191086b10f11f319a6e99e1e82889c5cc6046f515c9db1d497" - -[[package]] -name = "rustc-hex" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" - -[[package]] -name = "rustc_version" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0dfe2087c51c460008730de8b57e6a320782fbfb312e1f4d520e6c6fae155ee" -dependencies = [ - "semver 0.11.0", -] - -[[package]] -name = "rustc_version" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" -dependencies = [ - "semver 1.0.24", -] - -[[package]] -name = "rustix" -version = "0.38.42" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f93dc38ecbab2eb790ff964bb77fa94faf256fd3e73285fd7ba0903b76bedb85" -dependencies = [ - "bitflags", - "errno", - "libc", - "linux-raw-sys", - "windows-sys 0.59.0", -] - -[[package]] -name = "rustls-pemfile" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" -dependencies = [ - "rustls-pki-types", -] - -[[package]] -name = "rustls-pki-types" -version = "1.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2bf47e6ff922db3825eb750c4e2ff784c6ff8fb9e13046ef6a1d1c5401b0b37" - -[[package]] -name = "rustversion" -version = "1.0.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e819f2bc632f285be6d7cd36e25940d45b2391dd6d9b939e79de557f7014248" - -[[package]] -name = "rusty-fork" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb3dcc6e454c328bb824492db107ab7c0ae8fcffe4ad210136ef014458c1bc4f" -dependencies = [ - "fnv", - "quick-error", - "tempfile", - "wait-timeout", -] - -[[package]] -name = "ryu" -version = "1.0.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" - -[[package]] -name = "schannel" -version = "0.1.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f29ebaa345f945cec9fbbc532eb307f0fdad8161f281b6369539c8d84876b3d" -dependencies = [ - "windows-sys 0.59.0", -] - -[[package]] -name = "schnellru" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9a8ef13a93c54d20580de1e5c413e624e53121d42fc7e2c11d10ef7f8b02367" -dependencies = [ - "ahash", - "cfg-if", - "hashbrown 0.13.2", -] - -[[package]] -name = "scopeguard" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" - -[[package]] -name = "sec1" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" -dependencies = [ - "base16ct", - "der", - "generic-array", - "pkcs8", - "subtle", - "zeroize", -] - -[[package]] -name = "security-framework" -version = "2.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" -dependencies = [ - "bitflags", - "core-foundation", - "core-foundation-sys", - "libc", - "security-framework-sys", -] - -[[package]] -name = "security-framework-sys" -version = "2.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1863fd3768cd83c56a7f60faa4dc0d403f1b6df0a38c3c25f44b7894e45370d5" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "semver" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6" -dependencies = [ - "semver-parser", -] - -[[package]] -name = "semver" -version = "1.0.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3cb6eb87a131f756572d7fb904f6e7b68633f09cca868c5df1c4b8d1a694bbba" - -[[package]] -name = "semver-parser" -version = "0.10.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9900206b54a3527fdc7b8a938bffd94a568bac4f4aa8113b209df75a09c0dec2" -dependencies = [ - "pest", -] - -[[package]] -name = "serde" -version = "1.0.216" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b9781016e935a97e8beecf0c933758c97a5520d32930e460142b4cd80c6338e" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde_derive" -version = "1.0.216" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46f859dbbf73865c6627ed570e78961cd3ac92407a2d117204c49232485da55e" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.100", -] - -[[package]] -name = "serde_json" -version = "1.0.134" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d00f4175c42ee48b15416f6193a959ba3a0d67fc699a0db9ad12df9f83991c7d" -dependencies = [ - "itoa", - "memchr", - "ryu", - "serde", -] - -[[package]] -name = "serde_urlencoded" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" -dependencies = [ - "form_urlencoded", - "itoa", - "ryu", - "serde", -] - -[[package]] -name = "sha2" -version = "0.10.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest 0.10.7", -] - -[[package]] -name = "sha3" -version = "0.10.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" -dependencies = [ - "digest 0.10.7", - "keccak", -] - -[[package]] -name = "sha3-asm" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c28efc5e327c837aa837c59eae585fc250715ef939ac32881bcc11677cd02d46" -dependencies = [ - "cc", - "cfg-if", -] - -[[package]] -name = "sharded-slab" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" -dependencies = [ - "lazy_static", -] - -[[package]] -name = "shlex" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" - -[[package]] -name = "signature" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" -dependencies = [ - "digest 0.10.7", - "rand_core", -] - -[[package]] -name = "slab" -version = "0.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" -dependencies = [ - "autocfg", -] - -[[package]] -name = "smallvec" -version = "1.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" -dependencies = [ - "serde", -] - -[[package]] -name = "socket2" -version = "0.5.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8" -dependencies = [ - "libc", - "windows-sys 0.52.0", -] - -[[package]] -name = "spdx" -version = "0.10.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bae30cc7bfe3656d60ee99bf6836f472b0c53dddcbf335e253329abb16e535a2" -dependencies = [ - "smallvec", -] - -[[package]] -name = "spki" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" -dependencies = [ - "base64ct", - "der", -] - -[[package]] -name = "stable_deref_trait" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" - -[[package]] -name = "static_assertions" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" - -[[package]] -name = "strum" -version = "0.26.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" -dependencies = [ - "strum_macros", -] - -[[package]] -name = "strum_macros" -version = "0.26.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" -dependencies = [ - "heck", - "proc-macro2", - "quote", - "rustversion", - "syn 2.0.100", -] - -[[package]] -name = "subtle" -version = "2.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" - -[[package]] -name = "syn" -version = "1.0.109" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "syn" -version = "2.0.100" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "syn-solidity" -version = "0.8.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4560533fbd6914b94a8fb5cc803ed6801c3455668db3b810702c57612bac9412" -dependencies = [ - "paste", - "proc-macro2", - "quote", - "syn 2.0.100", -] - -[[package]] -name = "sync_wrapper" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" -dependencies = [ - "futures-core", -] - -[[package]] -name = "synstructure" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.100", -] - -[[package]] -name = "tap" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" - -[[package]] -name = "tempfile" -version = "3.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28cce251fcbc87fac86a866eeb0d6c2d536fc16d06f184bb61aeae11aa4cee0c" -dependencies = [ - "cfg-if", - "fastrand", - "once_cell", - "rustix", - "windows-sys 0.59.0", -] - -[[package]] -name = "thiserror" -version = "1.0.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" -dependencies = [ - "thiserror-impl 1.0.69", -] - -[[package]] -name = "thiserror" -version = "2.0.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f072643fd0190df67a8bab670c20ef5d8737177d6ac6b2e9a236cb096206b2cc" -dependencies = [ - "thiserror-impl 2.0.9", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.100", -] - -[[package]] -name = "thiserror-impl" -version = "2.0.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b50fa271071aae2e6ee85f842e2e28ba8cd2c5fb67f11fcb1fd70b276f9e7d4" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.100", -] - -[[package]] -name = "thread_local" -version = "1.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" -dependencies = [ - "cfg-if", - "once_cell", -] - -[[package]] -name = "threadpool" -version = "1.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d050e60b33d41c19108b32cea32164033a9013fe3b46cbd4457559bfbf77afaa" -dependencies = [ - "num_cpus", -] - -[[package]] -name = "tiny-keccak" -version = "2.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" -dependencies = [ - "crunchy", -] - -[[package]] -name = "tinystr" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" -dependencies = [ - "displaydoc", - "zerovec", -] - -[[package]] -name = "tokio" -version = "1.42.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cec9b21b0450273377fc97bd4c33a8acffc8c996c987a7c5b319a0083707551" -dependencies = [ - "backtrace", - "bytes", - "libc", - "mio", - "pin-project-lite", - "socket2", - "tokio-macros", - "windows-sys 0.52.0", -] - -[[package]] -name = "tokio-macros" -version = "2.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.100", -] - -[[package]] -name = "tokio-native-tls" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" -dependencies = [ - "native-tls", - "tokio", -] - -[[package]] -name = "tokio-stream" -version = "0.1.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eca58d7bba4a75707817a2c44174253f9236b2d5fbd055602e9d5c07c139a047" -dependencies = [ - "futures-core", - "pin-project-lite", - "tokio", - "tokio-util", -] - -[[package]] -name = "tokio-util" -version = "0.7.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7fcaa8d55a2bdd6b83ace262b016eca0d79ee02818c5c1bcdf0305114081078" -dependencies = [ - "bytes", - "futures-core", - "futures-sink", - "pin-project-lite", - "tokio", -] - -[[package]] -name = "toml_datetime" -version = "0.6.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" - -[[package]] -name = "toml_edit" -version = "0.22.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" -dependencies = [ - "indexmap", - "toml_datetime", - "winnow 0.6.20", -] - -[[package]] -name = "tower" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" -dependencies = [ - "futures-core", - "futures-util", - "pin-project-lite", - "sync_wrapper", - "tower-layer", - "tower-service", -] - -[[package]] -name = "tower-layer" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" - -[[package]] -name = "tower-service" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" - -[[package]] -name = "tracing" -version = "0.1.41" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" -dependencies = [ - "pin-project-lite", - "tracing-attributes", - "tracing-core", -] - -[[package]] -name = "tracing-attributes" -version = "0.1.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.100", -] - -[[package]] -name = "tracing-core" -version = "0.1.33" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" -dependencies = [ - "once_cell", - "valuable", -] - -[[package]] -name = "tracing-error" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b1581020d7a273442f5b45074a6a57d5757ad0a47dac0e9f0bd57b81936f3db" -dependencies = [ - "tracing", - "tracing-subscriber", -] - -[[package]] -name = "tracing-log" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" -dependencies = [ - "log", - "once_cell", - "tracing-core", -] - -[[package]] -name = "tracing-serde" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "704b1aeb7be0d0a84fc9828cae51dab5970fee5088f83d1dd7ee6f6246fc6ff1" -dependencies = [ - "serde", - "tracing-core", -] - -[[package]] -name = "tracing-subscriber" -version = "0.3.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008" -dependencies = [ - "matchers", - "nu-ansi-term", - "once_cell", - "regex", - "serde", - "serde_json", - "sharded-slab", - "smallvec", - "thread_local", - "tracing", - "tracing-core", - "tracing-log", - "tracing-serde", -] - -[[package]] -name = "try-lock" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" - -[[package]] -name = "typenum" -version = "1.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" - -[[package]] -name = "ucd-trie" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971" - -[[package]] -name = "uint" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76f64bba2c53b04fcab63c01a7d7427eadc821e3bc48c34dc9ba29c501164b52" -dependencies = [ - "byteorder", - "crunchy", - "hex", - "static_assertions", -] - -[[package]] -name = "unarray" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" - -[[package]] -name = "unicase" -version = "2.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e51b68083f157f853b6379db119d1c1be0e6e4dec98101079dec41f6f5cf6df" - -[[package]] -name = "unicode-ident" -version = "1.0.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" - -[[package]] -name = "unicode-xid" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" - -[[package]] -name = "url" -version = "2.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" -dependencies = [ - "form_urlencoded", - "idna", - "percent-encoding", -] - -[[package]] -name = "utf16_iter" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" - -[[package]] -name = "utf8_iter" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" - -[[package]] -name = "valuable" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" - -[[package]] -name = "vcpkg" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" - -[[package]] -name = "version_check" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" - -[[package]] -name = "wait-timeout" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6" -dependencies = [ - "libc", -] - -[[package]] -name = "want" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" -dependencies = [ - "try-lock", -] - -[[package]] -name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" - -[[package]] -name = "wasm-bindgen" -version = "0.2.99" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a474f6281d1d70c17ae7aa6a613c87fce69a127e2624002df63dcb39d6cf6396" -dependencies = [ - "cfg-if", - "once_cell", - "wasm-bindgen-macro", -] - -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.99" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f89bb38646b4f81674e8f5c3fb81b562be1fd936d84320f3264486418519c79" -dependencies = [ - "bumpalo", - "log", - "proc-macro2", - "quote", - "syn 2.0.100", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-futures" -version = "0.4.49" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38176d9b44ea84e9184eff0bc34cc167ed044f816accfe5922e54d84cf48eca2" -dependencies = [ - "cfg-if", - "js-sys", - "once_cell", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.99" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2cc6181fd9a7492eef6fef1f33961e3695e4579b9872a6f7c83aee556666d4fe" -dependencies = [ - "quote", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.99" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30d7a95b763d3c45903ed6c81f156801839e5ee968bb07e534c44df0fcd330c2" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.100", - "wasm-bindgen-backend", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.99" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "943aab3fdaaa029a6e0271b35ea10b72b943135afe9bffca82384098ad0e06a6" - -[[package]] -name = "wasm-encoder" -version = "0.220.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebf48234b389415b226a4daef6562933d38c7b28a8b8f64c5c4130dad1561ab7" -dependencies = [ - "leb128", - "wasmparser", -] - -[[package]] -name = "wasm-metadata" -version = "0.220.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f3e5f5920c5abfc45573c89b07b38efdaae1515ef86f83dad12d60e50ecd62b" -dependencies = [ - "anyhow", - "indexmap", - "serde", - "serde_derive", - "serde_json", - "spdx", - "wasm-encoder", - "wasmparser", -] - -[[package]] -name = "wasmparser" -version = "0.220.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e246c2772ce3ebc83f89a2d4487ac5794cad6c309b2071818a88c7db7c36d87b" -dependencies = [ - "ahash", - "bitflags", - "hashbrown 0.14.5", - "indexmap", - "semver 1.0.24", -] - -[[package]] -name = "wasmtimer" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0048ad49a55b9deb3953841fa1fc5858f0efbcb7a18868c899a360269fac1b23" -dependencies = [ - "futures", - "js-sys", - "parking_lot", - "pin-utils", - "slab", - "wasm-bindgen", -] - -[[package]] -name = "web-sys" -version = "0.3.76" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04dd7223427d52553d3702c004d3b2fe07c148165faa56313cb00211e31c12bc" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - -[[package]] -name = "windows-registry" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e400001bb720a623c1c69032f8e3e4cf09984deec740f007dd2b03ec864804b0" -dependencies = [ - "windows-result", - "windows-strings", - "windows-targets", -] - -[[package]] -name = "windows-result" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" -dependencies = [ - "windows-targets", -] - -[[package]] -name = "windows-strings" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" -dependencies = [ - "windows-result", - "windows-targets", -] - -[[package]] -name = "windows-sys" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" -dependencies = [ - "windows-targets", -] - -[[package]] -name = "windows-sys" -version = "0.59.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" -dependencies = [ - "windows-targets", -] - -[[package]] -name = "windows-targets" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" -dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_gnullvm", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", -] - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" - -[[package]] -name = "windows_i686_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" - -[[package]] -name = "windows_i686_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" - -[[package]] -name = "windows_i686_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" - -[[package]] -name = "winnow" -version = "0.6.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b" -dependencies = [ - "memchr", -] - -[[package]] -name = "winnow" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e97b544156e9bebe1a0ffbc03484fc1ffe3100cbce3ffb17eac35f7cdd7ab36" -dependencies = [ - "memchr", -] - -[[package]] -name = "wit-bindgen" -version = "0.36.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a2b3e15cd6068f233926e7d8c7c588b2ec4fb7cc7bf3824115e7c7e2a8485a3" -dependencies = [ - "wit-bindgen-rt", - "wit-bindgen-rust-macro", -] - -[[package]] -name = "wit-bindgen-core" -version = "0.36.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b632a5a0fa2409489bd49c9e6d99fcc61bb3d4ce9d1907d44662e75a28c71172" -dependencies = [ - "anyhow", - "heck", - "wit-parser", -] - -[[package]] -name = "wit-bindgen-rt" -version = "0.36.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7947d0131c7c9da3f01dfde0ab8bd4c4cf3c5bd49b6dba0ae640f1fa752572ea" -dependencies = [ - "bitflags", -] - -[[package]] -name = "wit-bindgen-rust" -version = "0.36.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4329de4186ee30e2ef30a0533f9b3c123c019a237a7c82d692807bf1b3ee2697" -dependencies = [ - "anyhow", - "heck", - "indexmap", - "prettyplease", - "syn 2.0.100", - "wasm-metadata", - "wit-bindgen-core", - "wit-component", -] - -[[package]] -name = "wit-bindgen-rust-macro" -version = "0.36.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "177fb7ee1484d113b4792cc480b1ba57664bbc951b42a4beebe573502135b1fc" -dependencies = [ - "anyhow", - "prettyplease", - "proc-macro2", - "quote", - "syn 2.0.100", - "wit-bindgen-core", - "wit-bindgen-rust", -] - -[[package]] -name = "wit-component" -version = "0.220.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73ccedf54cc65f287da268d64d2bf4f7530d2cfb2296ffbe3ad5f65567e4cf53" -dependencies = [ - "anyhow", - "bitflags", - "indexmap", - "log", - "serde", - "serde_derive", - "serde_json", - "wasm-encoder", - "wasm-metadata", - "wasmparser", - "wit-parser", -] - -[[package]] -name = "wit-parser" -version = "0.220.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b7117ce3adc0b4354b46dc1cf3190b00b333e65243d244c613ffcc58bdec84d" -dependencies = [ - "anyhow", - "id-arena", - "indexmap", - "log", - "semver 1.0.24", - "serde", - "serde_derive", - "serde_json", - "unicode-xid", - "wasmparser", -] - -[[package]] -name = "write16" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" - -[[package]] -name = "writeable" -version = "0.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" - -[[package]] -name = "wyz" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" -dependencies = [ - "tap", -] - -[[package]] -name = "yoke" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40" -dependencies = [ - "serde", - "stable_deref_trait", - "yoke-derive", - "zerofrom", -] - -[[package]] -name = "yoke-derive" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.100", - "synstructure", -] - -[[package]] -name = "zerocopy" -version = "0.7.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" -dependencies = [ - "byteorder", - "zerocopy-derive", -] - -[[package]] -name = "zerocopy-derive" -version = "0.7.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.100", -] - -[[package]] -name = "zerofrom" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cff3ee08c995dee1859d998dea82f7374f2826091dd9cd47def953cae446cd2e" -dependencies = [ - "zerofrom-derive", -] - -[[package]] -name = "zerofrom-derive" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.100", - "synstructure", -] - -[[package]] -name = "zeroize" -version = "1.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" -dependencies = [ - "zeroize_derive", -] - -[[package]] -name = "zeroize_derive" -version = "1.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.100", -] - -[[package]] -name = "zerovec" -version = "0.10.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" -dependencies = [ - "yoke", - "zerofrom", - "zerovec-derive", -] - -[[package]] -name = "zerovec-derive" -version = "0.10.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.100", -] From 4fbdb38e65ffe8884bc62a9649a7acde71e175c8 Mon Sep 17 00:00:00 2001 From: hosted-fornet Date: Tue, 5 Aug 2025 11:35:18 -0700 Subject: [PATCH 87/96] hyperwallet: change types to remove generics --- .DS_Store | Bin 6148 -> 0 bytes .gitignore | 3 +- Cargo.lock | 4058 +++++++++++++++++++++++++++++++ src/.DS_Store | Bin 6148 -> 0 bytes src/hyperwallet_client/api.rs | 355 +-- src/hyperwallet_client/mod.rs | 68 +- src/hyperwallet_client/types.rs | 191 +- 7 files changed, 4391 insertions(+), 284 deletions(-) delete mode 100644 .DS_Store create mode 100644 Cargo.lock delete mode 100644 src/.DS_Store diff --git a/.DS_Store b/.DS_Store deleted file mode 100644 index 09bf23a6998f704b3768c0c09d9d572dd5aaa4dd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6148 zcmeHKJxc>Y5S`T!gCeA`P;3r6k%%dRh0Phx#!5(MrBR7uLV_lUg>ZH@>VL7f5w*3l zw@+aiL=fNXu5mYKf>?;i4D7zW`PhAV$=&3LNEKV#d7>;4WiS|1qnN%hj`K*^ko8Oe z74K24QPj#*30l^qO;u9>FfQn zAKxArZ|pkHhQbp{Y2@Qv+NBniz0+y@BAMZGxQ+`qv$vW<&px&KCzJkJ9v=#s&p3D- zQ-gNsfR1Pr^A6>(>Q#p3PX=eZJolg1FRfMs_IEjdufpfQ#{7rr5S+I>9n%lfpAnZ= zyVK=m>s$`WsCQn5`vs+`sa2{#C-1lOHudt@8+Xs{%q^X-w)J=C?cp-$m*en2&sks| zVn0&=`mtrk49YADhytQOR{?%L1Q?8=#n_;JbYRl20KgR7+OXEW0T{3Y7+Q=C!UI#L z6lhA7eZ^3w9R9%Og%)Fjrks>M8T;6im3>1|`gHgMolYt=D6=Ra3it{Po69Ws|CRpl z|9+8Ni2|a)zfwS@qEb}EN3y*&@!`1F1{fO{Y@8Px)Fqf~JJt(s#giD?5DRz#3@ye6 R;ep8?0WE_}qQI{z@DAL=wc7vy 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 new file mode 100644 index 0000000..40a8b47 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,4058 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "addr2line" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" + +[[package]] +name = "ahash" +version = "0.8.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" +dependencies = [ + "cfg-if", + "getrandom 0.3.3", + "once_cell", + "version_check", + "zerocopy", +] + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "allocator-api2" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" + +[[package]] +name = "alloy" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59febb24956a41c29bb5f450978fbe825bd6456b3f80586c8bd558dc882e7b6a" +dependencies = [ + "alloy-consensus", + "alloy-core", + "alloy-eips", + "alloy-genesis", + "alloy-json-rpc", + "alloy-network", + "alloy-provider", + "alloy-rpc-client", + "alloy-rpc-types", + "alloy-serde", + "alloy-signer", + "alloy-signer-local", + "alloy-transport", + "alloy-transport-http", +] + +[[package]] +name = "alloy-chains" +version = "0.1.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28e2652684758b0d9b389d248b209ed9fd9989ef489a550265fe4bb8454fe7eb" +dependencies = [ + "alloy-primitives", + "num_enum", + "strum", +] + +[[package]] +name = "alloy-consensus" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e88e1edea70787c33e11197d3f32ae380f3db19e6e061e539a5bcf8184a6b326" +dependencies = [ + "alloy-eips", + "alloy-primitives", + "alloy-rlp", + "alloy-serde", + "alloy-trie", + "auto_impl", + "c-kzg", + "derive_more 1.0.0", + "serde", +] + +[[package]] +name = "alloy-consensus-any" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57b1bb53f40c0273cd1975573cd457b39213e68584e36d1401d25fd0398a1d65" +dependencies = [ + "alloy-consensus", + "alloy-eips", + "alloy-primitives", + "alloy-rlp", + "alloy-serde", + "serde", +] + +[[package]] +name = "alloy-core" +version = "0.8.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d8bcce99ad10fe02640cfaec1c6bc809b837c783c1d52906aa5af66e2a196f6" +dependencies = [ + "alloy-dyn-abi", + "alloy-json-abi", + "alloy-primitives", + "alloy-rlp", + "alloy-sol-types", +] + +[[package]] +name = "alloy-dyn-abi" +version = "0.8.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb8e762aefd39a397ff485bc86df673465c4ad3ec8819cc60833a8a3ba5cdc87" +dependencies = [ + "alloy-json-abi", + "alloy-primitives", + "alloy-sol-type-parser", + "alloy-sol-types", + "const-hex", + "itoa", + "serde", + "serde_json", + "winnow", +] + +[[package]] +name = "alloy-eip2930" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0069cf0642457f87a01a014f6dc29d5d893cd4fd8fddf0c3cdfad1bb3ebafc41" +dependencies = [ + "alloy-primitives", + "alloy-rlp", + "serde", +] + +[[package]] +name = "alloy-eip7702" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c986539255fb839d1533c128e190e557e52ff652c9ef62939e233a81dd93f7e" +dependencies = [ + "alloy-primitives", + "alloy-rlp", + "derive_more 1.0.0", + "serde", +] + +[[package]] +name = "alloy-eips" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f9fadfe089e9ccc0650473f2d4ef0a28bc015bbca5631d9f0f09e49b557fdb3" +dependencies = [ + "alloy-eip2930", + "alloy-eip7702", + "alloy-primitives", + "alloy-rlp", + "alloy-serde", + "c-kzg", + "derive_more 1.0.0", + "once_cell", + "serde", + "sha2", +] + +[[package]] +name = "alloy-genesis" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b2a4cf7b70f3495788e74ce1c765260ffe38820a2a774ff4aacb62e31ea73f9" +dependencies = [ + "alloy-primitives", + "alloy-serde", + "alloy-trie", + "serde", +] + +[[package]] +name = "alloy-json-abi" +version = "0.8.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe6beff64ad0aa6ad1019a3db26fef565aefeb011736150ab73ed3366c3cfd1b" +dependencies = [ + "alloy-primitives", + "alloy-sol-type-parser", + "serde", + "serde_json", +] + +[[package]] +name = "alloy-json-rpc" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e29040b9d5fe2fb70415531882685b64f8efd08dfbd6cc907120650504821105" +dependencies = [ + "alloy-primitives", + "alloy-sol-types", + "serde", + "serde_json", + "thiserror 2.0.12", + "tracing", +] + +[[package]] +name = "alloy-network" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "510cc00b318db0dfccfdd2d032411cfae64fc144aef9679409e014145d3dacc4" +dependencies = [ + "alloy-consensus", + "alloy-consensus-any", + "alloy-eips", + "alloy-json-rpc", + "alloy-network-primitives", + "alloy-primitives", + "alloy-rpc-types-any", + "alloy-rpc-types-eth", + "alloy-serde", + "alloy-signer", + "alloy-sol-types", + "async-trait", + "auto_impl", + "futures-utils-wasm", + "serde", + "serde_json", + "thiserror 2.0.12", +] + +[[package]] +name = "alloy-network-primitives" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9081c099e798b8a2bba2145eb82a9a146f01fc7a35e9ab6e7b43305051f97550" +dependencies = [ + "alloy-consensus", + "alloy-eips", + "alloy-primitives", + "alloy-serde", + "serde", +] + +[[package]] +name = "alloy-primitives" +version = "0.8.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c77490fe91a0ce933a1f219029521f20fc28c2c0ca95d53fa4da9c00b8d9d4e" +dependencies = [ + "alloy-rlp", + "bytes", + "cfg-if", + "const-hex", + "derive_more 2.0.1", + "foldhash", + "hashbrown 0.15.4", + "indexmap", + "itoa", + "k256", + "keccak-asm", + "paste", + "proptest", + "rand 0.8.5", + "ruint", + "rustc-hash", + "serde", + "sha3", + "tiny-keccak", +] + +[[package]] +name = "alloy-provider" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc2dfaddd9a30aa870a78a4e1316e3e115ec1e12e552cbc881310456b85c1f24" +dependencies = [ + "alloy-chains", + "alloy-consensus", + "alloy-eips", + "alloy-json-rpc", + "alloy-network", + "alloy-network-primitives", + "alloy-primitives", + "alloy-rpc-client", + "alloy-rpc-types-eth", + "alloy-transport", + "alloy-transport-http", + "async-stream", + "async-trait", + "auto_impl", + "dashmap", + "futures", + "futures-utils-wasm", + "lru", + "parking_lot", + "pin-project", + "reqwest", + "schnellru", + "serde", + "serde_json", + "thiserror 2.0.12", + "tokio", + "tracing", + "url", + "wasmtimer", +] + +[[package]] +name = "alloy-rlp" +version = "0.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f70d83b765fdc080dbcd4f4db70d8d23fe4761f2f02ebfa9146b833900634b4" +dependencies = [ + "alloy-rlp-derive", + "arrayvec", + "bytes", +] + +[[package]] +name = "alloy-rlp-derive" +version = "0.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64b728d511962dda67c1bc7ea7c03736ec275ed2cf4c35d9585298ac9ccf3b73" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "alloy-rpc-client" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "531137b283547d5b9a5cafc96b006c64ef76810c681d606f28be9781955293b6" +dependencies = [ + "alloy-json-rpc", + "alloy-primitives", + "alloy-transport", + "alloy-transport-http", + "futures", + "pin-project", + "reqwest", + "serde", + "serde_json", + "tokio", + "tokio-stream", + "tower", + "tracing", + "url", + "wasmtimer", +] + +[[package]] +name = "alloy-rpc-types" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3410a472ce26c457e9780f708ee6bd540b30f88f1f31fdab7a11d00bd6aa1aee" +dependencies = [ + "alloy-primitives", + "alloy-rpc-types-eth", + "alloy-serde", + "serde", +] + +[[package]] +name = "alloy-rpc-types-any" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed98e1af55a7d856bfa385f30f63d8d56be2513593655c904a8f4a7ec963aa3e" +dependencies = [ + "alloy-consensus-any", + "alloy-rpc-types-eth", + "alloy-serde", +] + +[[package]] +name = "alloy-rpc-types-eth" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8737d7a6e37ca7bba9c23e9495c6534caec6760eb24abc9d5ffbaaba147818e1" +dependencies = [ + "alloy-consensus", + "alloy-consensus-any", + "alloy-eips", + "alloy-network-primitives", + "alloy-primitives", + "alloy-rlp", + "alloy-serde", + "alloy-sol-types", + "derive_more 1.0.0", + "itertools 0.13.0", + "serde", + "serde_json", +] + +[[package]] +name = "alloy-serde" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5851bf8d5ad33014bd0c45153c603303e730acc8a209450a7ae6b4a12c2789e2" +dependencies = [ + "alloy-primitives", + "serde", + "serde_json", +] + +[[package]] +name = "alloy-signer" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e10ca565da6500cca015ba35ee424d59798f2e1b85bc0dd8f81dafd401f029a" +dependencies = [ + "alloy-primitives", + "async-trait", + "auto_impl", + "elliptic-curve", + "k256", + "thiserror 2.0.12", +] + +[[package]] +name = "alloy-signer-local" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47fababf5a745133490cde927d48e50267f97d3d1209b9fc9f1d1d666964d172" +dependencies = [ + "alloy-consensus", + "alloy-network", + "alloy-primitives", + "alloy-signer", + "async-trait", + "k256", + "rand 0.8.5", + "thiserror 2.0.12", +] + +[[package]] +name = "alloy-sol-macro" +version = "0.8.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e10ae8e9a91d328ae954c22542415303919aabe976fe7a92eb06db1b68fd59f2" +dependencies = [ + "alloy-sol-macro-expander", + "alloy-sol-macro-input", + "proc-macro-error2", + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "alloy-sol-macro-expander" +version = "0.8.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83ad5da86c127751bc607c174d6c9fe9b85ef0889a9ca0c641735d77d4f98f26" +dependencies = [ + "alloy-sol-macro-input", + "const-hex", + "heck", + "indexmap", + "proc-macro-error2", + "proc-macro2", + "quote", + "syn 2.0.104", + "syn-solidity", + "tiny-keccak", +] + +[[package]] +name = "alloy-sol-macro-input" +version = "0.8.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3d30f0d3f9ba3b7686f3ff1de9ee312647aac705604417a2f40c604f409a9e" +dependencies = [ + "const-hex", + "dunce", + "heck", + "macro-string", + "proc-macro2", + "quote", + "syn 2.0.104", + "syn-solidity", +] + +[[package]] +name = "alloy-sol-type-parser" +version = "0.8.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d162f8524adfdfb0e4bd0505c734c985f3e2474eb022af32eef0d52a4f3935c" +dependencies = [ + "serde", + "winnow", +] + +[[package]] +name = "alloy-sol-types" +version = "0.8.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d43d5e60466a440230c07761aa67671d4719d46f43be8ea6e7ed334d8db4a9ab" +dependencies = [ + "alloy-json-abi", + "alloy-primitives", + "alloy-sol-macro", + "const-hex", + "serde", +] + +[[package]] +name = "alloy-transport" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "538a04a37221469cac0ce231b737fd174de2fdfcdd843bdd068cb39ed3e066ad" +dependencies = [ + "alloy-json-rpc", + "base64", + "futures-util", + "futures-utils-wasm", + "serde", + "serde_json", + "thiserror 2.0.12", + "tokio", + "tower", + "tracing", + "url", + "wasmtimer", +] + +[[package]] +name = "alloy-transport-http" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ed40eb1e1265b2911512f6aa1dcece9702d078f5a646730c45e39e2be00ac1c" +dependencies = [ + "alloy-json-rpc", + "alloy-transport", + "reqwest", + "serde_json", + "tower", + "tracing", + "url", +] + +[[package]] +name = "alloy-trie" +version = "0.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d95a94854e420f07e962f7807485856cde359ab99ab6413883e15235ad996e8b" +dependencies = [ + "alloy-primitives", + "alloy-rlp", + "arrayvec", + "derive_more 1.0.0", + "nybbles", + "serde", + "smallvec", + "tracing", +] + +[[package]] +name = "anyhow" +version = "1.0.98" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" + +[[package]] +name = "ark-ff" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b3235cc41ee7a12aaaf2c575a2ad7b46713a8a50bda2fc3b003a04845c05dd6" +dependencies = [ + "ark-ff-asm 0.3.0", + "ark-ff-macros 0.3.0", + "ark-serialize 0.3.0", + "ark-std 0.3.0", + "derivative", + "num-bigint", + "num-traits", + "paste", + "rustc_version 0.3.3", + "zeroize", +] + +[[package]] +name = "ark-ff" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec847af850f44ad29048935519032c33da8aa03340876d351dfab5660d2966ba" +dependencies = [ + "ark-ff-asm 0.4.2", + "ark-ff-macros 0.4.2", + "ark-serialize 0.4.2", + "ark-std 0.4.0", + "derivative", + "digest 0.10.7", + "itertools 0.10.5", + "num-bigint", + "num-traits", + "paste", + "rustc_version 0.4.1", + "zeroize", +] + +[[package]] +name = "ark-ff-asm" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db02d390bf6643fb404d3d22d31aee1c4bc4459600aef9113833d17e786c6e44" +dependencies = [ + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-ff-asm" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ed4aa4fe255d0bc6d79373f7e31d2ea147bcf486cba1be5ba7ea85abdb92348" +dependencies = [ + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-ff-macros" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db2fd794a08ccb318058009eefdf15bcaaaaf6f8161eb3345f907222bac38b20" +dependencies = [ + "num-bigint", + "num-traits", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-ff-macros" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7abe79b0e4288889c4574159ab790824d0033b9fdcb2a112a3182fac2e514565" +dependencies = [ + "num-bigint", + "num-traits", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-serialize" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d6c2b318ee6e10f8c2853e73a83adc0ccb88995aa978d8a3408d492ab2ee671" +dependencies = [ + "ark-std 0.3.0", + "digest 0.9.0", +] + +[[package]] +name = "ark-serialize" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adb7b85a02b83d2f22f89bd5cac66c9c89474240cb6207cb1efc16d098e822a5" +dependencies = [ + "ark-std 0.4.0", + "digest 0.10.7", + "num-bigint", +] + +[[package]] +name = "ark-std" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1df2c09229cbc5a028b1d70e00fdb2acee28b1055dfb5ca73eea49c5a25c4e7c" +dependencies = [ + "num-traits", + "rand 0.8.5", +] + +[[package]] +name = "ark-std" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94893f1e0c6eeab764ade8dc4c0db24caf4fe7cbbaafc0eba0a9030f447b5185" +dependencies = [ + "num-traits", + "rand 0.8.5", +] + +[[package]] +name = "arrayvec" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" +dependencies = [ + "serde", +] + +[[package]] +name = "async-stream" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b5a71a6f37880a80d1d7f19efd781e4b5de42c88f0722cc13bcb6cc2cfe8476" +dependencies = [ + "async-stream-impl", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-stream-impl" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "async-trait" +version = "0.1.88" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e539d3fca749fcee5236ab05e93a52867dd549cc157c8cb7f99595f3cedffdb5" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "auto_impl" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffdcb70bdbc4d478427380519163274ac86e52916e10f0a8889adf0f96d3fee7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "autocfg" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" + +[[package]] +name = "backtrace" +version = "0.3.75" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002" +dependencies = [ + "addr2line", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", + "windows-targets 0.52.6", +] + +[[package]] +name = "base16ct" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "base64ct" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55248b47b0caf0546f7988906588779981c43bb1bc9d0c44087278f80cdb44ba" + +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + +[[package]] +name = "bit-set" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3" +dependencies = [ + "bit-vec", +] + +[[package]] +name = "bit-vec" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" + +[[package]] +name = "bitflags" +version = "2.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" + +[[package]] +name = "bitvec" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" +dependencies = [ + "funty", + "radium", + "tap", + "wyz", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "blst" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fd49896f12ac9b6dcd7a5998466b9b58263a695a3dd1ecc1aaca2e12a90b080" +dependencies = [ + "cc", + "glob", + "threadpool", + "zeroize", +] + +[[package]] +name = "bumpalo" +version = "3.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" + +[[package]] +name = "byte-slice-cast" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7575182f7272186991736b70173b0ea045398f984bf5ebbb3804736ce1330c9d" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" +dependencies = [ + "serde", +] + +[[package]] +name = "c-kzg" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0307f72feab3300336fb803a57134159f6e20139af1357f36c54cb90d8e8928" +dependencies = [ + "blst", + "cc", + "glob", + "hex", + "libc", + "once_cell", + "serde", +] + +[[package]] +name = "cc" +version = "1.2.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3a42d84bb6b69d3a8b3eaacf0d88f179e1929695e1ad012b6cf64d9caaa5fd2" +dependencies = [ + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268" + +[[package]] +name = "color-eyre" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5920befb47832a6d61ee3a3a846565cfa39b331331e68a3b1d1116630f2f26d" +dependencies = [ + "backtrace", + "color-spantrace", + "eyre", + "indenter", + "once_cell", + "owo-colors", + "tracing-error", +] + +[[package]] +name = "color-spantrace" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8b88ea9df13354b55bc7234ebcce36e6ef896aca2e42a15de9e10edce01b427" +dependencies = [ + "once_cell", + "owo-colors", + "tracing-core", + "tracing-error", +] + +[[package]] +name = "const-hex" +version = "1.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83e22e0ed40b96a48d3db274f72fd365bd78f67af39b6bbd47e8a15e1c6207ff" +dependencies = [ + "cfg-if", + "cpufeatures", + "hex", + "proptest", + "serde", +] + +[[package]] +name = "const-oid" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" + +[[package]] +name = "const_format" +version = "0.2.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "126f97965c8ad46d6d9163268ff28432e8f6a1196a55578867832e3049df63dd" +dependencies = [ + "const_format_proc_macros", +] + +[[package]] +name = "const_format_proc_macros" +version = "0.2.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d57c2eccfb16dbac1f4e61e206105db5820c9d26c3c472bc17c774259ef7744" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + +[[package]] +name = "crunchy" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" + +[[package]] +name = "crypto-bigint" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" +dependencies = [ + "generic-array", + "rand_core 0.6.4", + "subtle", + "zeroize", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "dashmap" +version = "6.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5041cc499144891f3790297212f32a74fb938e5136a14943f338ef9e0ae276cf" +dependencies = [ + "cfg-if", + "crossbeam-utils", + "hashbrown 0.14.5", + "lock_api", + "once_cell", + "parking_lot_core", +] + +[[package]] +name = "der" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb" +dependencies = [ + "const-oid", + "zeroize", +] + +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "derive_more" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a9b99b9cbbe49445b21764dc0625032a89b145a2642e67603e1c936f5458d05" +dependencies = [ + "derive_more-impl 1.0.0", +] + +[[package]] +name = "derive_more" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "093242cf7570c207c83073cf82f79706fe7b8317e98620a47d5be7c3d8497678" +dependencies = [ + "derive_more-impl 2.0.1", +] + +[[package]] +name = "derive_more-impl" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", + "unicode-xid", +] + +[[package]] +name = "derive_more-impl" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bda628edc44c4bb645fbe0f758797143e4e07926f7ebf4e9bdfbd3d2ce621df3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", + "unicode-xid", +] + +[[package]] +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "const-oid", + "crypto-common", + "subtle", +] + +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "dunce" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" + +[[package]] +name = "ecdsa" +version = "0.16.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" +dependencies = [ + "der", + "digest 0.10.7", + "elliptic-curve", + "rfc6979", + "signature", + "spki", +] + +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + +[[package]] +name = "elliptic-curve" +version = "0.13.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" +dependencies = [ + "base16ct", + "crypto-bigint", + "digest 0.10.7", + "ff", + "generic-array", + "group", + "pkcs8", + "rand_core 0.6.4", + "sec1", + "subtle", + "zeroize", +] + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "errno" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "778e2ac28f6c47af28e4907f13ffd1e1ddbd400980a9abd7c8df189bf578a5ad" +dependencies = [ + "libc", + "windows-sys 0.60.2", +] + +[[package]] +name = "eyre" +version = "0.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cd915d99f24784cdc19fd37ef22b97e3ff0ae756c7e492e9fbfe897d61e2aec" +dependencies = [ + "indenter", + "once_cell", +] + +[[package]] +name = "fastrand" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" + +[[package]] +name = "fastrlp" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "139834ddba373bbdd213dffe02c8d110508dcf1726c2be27e8d1f7d7e1856418" +dependencies = [ + "arrayvec", + "auto_impl", + "bytes", +] + +[[package]] +name = "fastrlp" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce8dba4714ef14b8274c371879b175aa55b16b30f269663f19d576f380018dc4" +dependencies = [ + "arrayvec", + "auto_impl", + "bytes", +] + +[[package]] +name = "ff" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0b50bfb653653f9ca9095b427bed08ab8d75a137839d9ad64eb11810d5b6393" +dependencies = [ + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "fixed-hash" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "835c052cb0c08c1acf6ffd71c022172e18723949c8282f2b9f27efbc51e64534" +dependencies = [ + "byteorder", + "rand 0.8.5", + "rustc-hex", + "static_assertions", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "form_urlencoded" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "funty" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + +[[package]] +name = "futures" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" + +[[package]] +name = "futures-executor" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" + +[[package]] +name = "futures-macro" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "futures-sink" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" + +[[package]] +name = "futures-task" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" + +[[package]] +name = "futures-util" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "futures-utils-wasm" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42012b0f064e01aa58b545fe3727f90f7dd4020f4a3ea735b50344965f5a57e9" + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", + "zeroize", +] + +[[package]] +name = "getrandom" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.11.1+wasi-snapshot-preview1", +] + +[[package]] +name = "getrandom" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasi 0.14.2+wasi-0.2.4", +] + +[[package]] +name = "gimli" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" + +[[package]] +name = "glob" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" + +[[package]] +name = "group" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" +dependencies = [ + "ff", + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "hashbrown" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" + +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +dependencies = [ + "ahash", +] + +[[package]] +name = "hashbrown" +version = "0.15.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5" +dependencies = [ + "allocator-api2", + "equivalent", + "foldhash", + "serde", +] + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hermit-abi" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +dependencies = [ + "serde", +] + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest 0.10.7", +] + +[[package]] +name = "http" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +dependencies = [ + "bytes", + "http", +] + +[[package]] +name = "http-body-util" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" +dependencies = [ + "bytes", + "futures-core", + "http", + "http-body", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" + +[[package]] +name = "hyper" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc2b571658e38e0c01b1fdca3bbbe93c00d3d71693ff2770043f8c29bc7d6f80" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "http", + "http-body", + "httparse", + "itoa", + "pin-project-lite", + "smallvec", + "tokio", + "want", +] + +[[package]] +name = "hyper-tls" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" +dependencies = [ + "bytes", + "http-body-util", + "hyper", + "hyper-util", + "native-tls", + "tokio", + "tokio-native-tls", + "tower-service", +] + +[[package]] +name = "hyper-util" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d9b05277c7e8da2c93a568989bb6207bef0112e8d17df7a6eda4a3cf143bc5e" +dependencies = [ + "base64", + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "http", + "http-body", + "hyper", + "ipnet", + "libc", + "percent-encoding", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", +] + +[[package]] +name = "hyperware_process_lib" +version = "1.2.0" +dependencies = [ + "alloy", + "alloy-primitives", + "alloy-sol-macro", + "alloy-sol-types", + "anyhow", + "base64", + "bincode", + "color-eyre", + "hex", + "http", + "mime_guess", + "rand 0.8.5", + "regex", + "rmp-serde", + "serde", + "serde_json", + "sha3", + "thiserror 1.0.69", + "tracing", + "tracing-error", + "tracing-subscriber", + "url", + "wit-bindgen", +] + +[[package]] +name = "icu_collections" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "200072f5d0e3614556f94a9930d5dc3e0662a652823904c3a75dc3b0af7fee47" +dependencies = [ + "displaydoc", + "potential_utf", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locale_core" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cde2700ccaed3872079a65fb1a78f6c0a36c91570f28755dda67bc8f7d9f00a" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_normalizer" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "436880e8e18df4d7bbc06d58432329d6458cc84531f7ac5f024e93deadb37979" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00210d6893afc98edb752b664b8890f0ef174c8adbb8d0be9710fa66fbbf72d3" + +[[package]] +name = "icu_properties" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "016c619c1eeb94efb86809b015c58f479963de65bdb6253345c1a1276f22e32b" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_locale_core", + "icu_properties_data", + "icu_provider", + "potential_utf", + "zerotrie", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "298459143998310acd25ffe6810ed544932242d3f07083eee1084d83a71bd632" + +[[package]] +name = "icu_provider" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03c80da27b5f4187909049ee2d72f276f0d9f99a42c306bd0131ecfe04d8e5af" +dependencies = [ + "displaydoc", + "icu_locale_core", + "stable_deref_trait", + "tinystr", + "writeable", + "yoke", + "zerofrom", + "zerotrie", + "zerovec", +] + +[[package]] +name = "id-arena" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25a2bc672d1148e28034f176e01fffebb08b35768468cc954630da77a1449005" + +[[package]] +name = "idna" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + +[[package]] +name = "impl-codec" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba6a270039626615617f3f36d15fc827041df3b78c439da2cadfa47455a77f2f" +dependencies = [ + "parity-scale-codec", +] + +[[package]] +name = "impl-trait-for-tuples" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0eb5a3343abf848c0984fe4604b2b105da9539376e24fc0a3b0007411ae4fd9" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "indenter" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" + +[[package]] +name = "indexmap" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe4cd85333e22411419a0bcae1297d25e58c9443848b11dc6a86fefe8c78a661" +dependencies = [ + "equivalent", + "hashbrown 0.15.4", + "serde", +] + +[[package]] +name = "io-uring" +version = "0.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d93587f37623a1a17d94ef2bc9ada592f5465fe7732084ab7beefabe5c77c0c4" +dependencies = [ + "bitflags", + "cfg-if", + "libc", +] + +[[package]] +name = "ipnet" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" + +[[package]] +name = "iri-string" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbc5ebe9c3a1a7a5127f920a418f7585e9e758e911d0466ed004f393b0e380b2" +dependencies = [ + "memchr", + "serde", +] + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" + +[[package]] +name = "js-sys" +version = "0.3.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + +[[package]] +name = "k256" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6e3919bbaa2945715f0bb6d3934a173d1e9a59ac23767fbaaef277265a7411b" +dependencies = [ + "cfg-if", + "ecdsa", + "elliptic-curve", + "once_cell", + "sha2", +] + +[[package]] +name = "keccak" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc2af9a1119c51f12a14607e783cb977bde58bc069ff0c3da1095e635d70654" +dependencies = [ + "cpufeatures", +] + +[[package]] +name = "keccak-asm" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "505d1856a39b200489082f90d897c3f07c455563880bc5952e38eabf731c83b6" +dependencies = [ + "digest 0.10.7", + "sha3-asm", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "leb128" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67" + +[[package]] +name = "libc" +version = "0.2.174" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776" + +[[package]] +name = "libm" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de" + +[[package]] +name = "linux-raw-sys" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" + +[[package]] +name = "litemap" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956" + +[[package]] +name = "lock_api" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96936507f153605bddfcda068dd804796c84324ed2510809e5b2a624c81da765" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" + +[[package]] +name = "lru" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "234cf4f4a04dc1f57e24b96cc0cd600cf2af460d4161ac5ecdd0af8e1f3b2a38" +dependencies = [ + "hashbrown 0.15.4", +] + +[[package]] +name = "macro-string" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b27834086c65ec3f9387b096d66e99f221cf081c2b738042aa252bcd41204e3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "matchers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" +dependencies = [ + "regex-automata 0.1.10", +] + +[[package]] +name = "memchr" +version = "2.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" + +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "mime_guess" +version = "2.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e" +dependencies = [ + "mime", + "unicase", +] + +[[package]] +name = "miniz_oxide" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" +dependencies = [ + "adler2", +] + +[[package]] +name = "mio" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c" +dependencies = [ + "libc", + "wasi 0.11.1+wasi-snapshot-preview1", + "windows-sys 0.59.0", +] + +[[package]] +name = "native-tls" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87de3442987e9dbec73158d5c715e7ad9072fda936bb03d19d7fa10e00520f0e" +dependencies = [ + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + +[[package]] +name = "nu-ansi-term" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +dependencies = [ + "overload", + "winapi", +] + +[[package]] +name = "num-bigint" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +dependencies = [ + "num-integer", + "num-traits", +] + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", + "libm", +] + +[[package]] +name = "num_cpus" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91df4bbde75afed763b708b7eee1e8e7651e02d97f6d5dd763e89367e957b23b" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "num_enum" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a973b4e44ce6cad84ce69d797acf9a044532e4184c4f267913d1b546a0727b7a" +dependencies = [ + "num_enum_derive", + "rustversion", +] + +[[package]] +name = "num_enum_derive" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77e878c846a8abae00dd069496dbe8751b16ac1c3d6bd2a7283a938e8228f90d" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "nybbles" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8983bb634df7248924ee0c4c3a749609b5abcb082c28fffe3254b3eb3602b307" +dependencies = [ + "alloy-rlp", + "const-hex", + "proptest", + "serde", + "smallvec", +] + +[[package]] +name = "object" +version = "0.36.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + +[[package]] +name = "openssl" +version = "0.10.73" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8505734d46c8ab1e19a1dce3aef597ad87dcb4c37e7188231769bd6bd51cebf8" +dependencies = [ + "bitflags", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "openssl-probe" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" + +[[package]] +name = "openssl-sys" +version = "0.9.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90096e2e47630d78b7d1c20952dc621f957103f8bc2c8359ec81290d75238571" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "overload" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" + +[[package]] +name = "owo-colors" +version = "4.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48dd4f4a2c8405440fd0462561f0e5806bd0f77e86f51c761481bdd4018b545e" + +[[package]] +name = "parity-scale-codec" +version = "3.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "799781ae679d79a948e13d4824a40970bfa500058d245760dd857301059810fa" +dependencies = [ + "arrayvec", + "bitvec", + "byte-slice-cast", + "const_format", + "impl-trait-for-tuples", + "parity-scale-codec-derive", + "rustversion", + "serde", +] + +[[package]] +name = "parity-scale-codec-derive" +version = "3.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34b4653168b563151153c9e4c08ebed57fb8262bebfa79711552fa983c623e7a" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "parking_lot" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70d58bf43669b5795d1576d0641cfb6fbb2057bf629506267a92807158584a13" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets 0.52.6", +] + +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + +[[package]] +name = "pest" +version = "2.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1db05f56d34358a8b1066f67cbb203ee3e7ed2ba674a6263a1d5ec6db2204323" +dependencies = [ + "memchr", + "thiserror 2.0.12", + "ucd-trie", +] + +[[package]] +name = "pin-project" +version = "1.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677f1add503faace112b9f1373e43e9e054bfdd22ff1a63c1bc485eaec6a6a8a" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der", + "spki", +] + +[[package]] +name = "pkg-config" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" + +[[package]] +name = "potential_utf" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5a7c30837279ca13e7c867e9e40053bc68740f988cb07f7ca6df43cc734b585" +dependencies = [ + "zerovec", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "prettyplease" +version = "0.2.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff24dfcda44452b9816fff4cd4227e1bb73ff5a2f1bc1105aa92fb8565ce44d2" +dependencies = [ + "proc-macro2", + "syn 2.0.104", +] + +[[package]] +name = "primitive-types" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b34d9fd68ae0b74a41b21c03c2f62847aa0ffea044eee893b4c140b37e244e2" +dependencies = [ + "fixed-hash", + "impl-codec", + "uint", +] + +[[package]] +name = "proc-macro-crate" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edce586971a4dfaa28950c6f18ed55e0406c1ab88bbce2c6f6293a7aaba73d35" +dependencies = [ + "toml_edit", +] + +[[package]] +name = "proc-macro-error-attr2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5" +dependencies = [ + "proc-macro2", + "quote", +] + +[[package]] +name = "proc-macro-error2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802" +dependencies = [ + "proc-macro-error-attr2", + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "proc-macro2" +version = "1.0.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "proptest" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fcdab19deb5195a31cf7726a210015ff1496ba1464fd42cb4f537b8b01b471f" +dependencies = [ + "bit-set", + "bit-vec", + "bitflags", + "lazy_static", + "num-traits", + "rand 0.9.2", + "rand_chacha 0.9.0", + "rand_xorshift", + "regex-syntax 0.8.5", + "rusty-fork", + "tempfile", + "unarray", +] + +[[package]] +name = "quick-error" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" + +[[package]] +name = "quote" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + +[[package]] +name = "radium" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha 0.3.1", + "rand_core 0.6.4", + "serde", +] + +[[package]] +name = "rand" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" +dependencies = [ + "rand_chacha 0.9.0", + "rand_core 0.9.3", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core 0.9.3", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.16", +] + +[[package]] +name = "rand_core" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" +dependencies = [ + "getrandom 0.3.3", +] + +[[package]] +name = "rand_xorshift" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "513962919efc330f829edb2535844d1b912b0fbe2ca165d613e4e8788bb05a5a" +dependencies = [ + "rand_core 0.9.3", +] + +[[package]] +name = "redox_syscall" +version = "0.5.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5407465600fb0548f1442edf71dd20683c6ed326200ace4b1ef0763521bb3b77" +dependencies = [ + "bitflags", +] + +[[package]] +name = "regex" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata 0.4.9", + "regex-syntax 0.8.5", +] + +[[package]] +name = "regex-automata" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" +dependencies = [ + "regex-syntax 0.6.29", +] + +[[package]] +name = "regex-automata" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax 0.8.5", +] + +[[package]] +name = "regex-syntax" +version = "0.6.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" + +[[package]] +name = "regex-syntax" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" + +[[package]] +name = "reqwest" +version = "0.12.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbc931937e6ca3a06e3b6c0aa7841849b160a90351d6ab467a8b9b9959767531" +dependencies = [ + "base64", + "bytes", + "futures-core", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-tls", + "hyper-util", + "js-sys", + "log", + "native-tls", + "percent-encoding", + "pin-project-lite", + "rustls-pki-types", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper", + "tokio", + "tokio-native-tls", + "tower", + "tower-http", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "rfc6979" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" +dependencies = [ + "hmac", + "subtle", +] + +[[package]] +name = "rlp" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb919243f34364b6bd2fc10ef797edbfa75f33c252e7998527479c6d6b47e1ec" +dependencies = [ + "bytes", + "rustc-hex", +] + +[[package]] +name = "rmp" +version = "0.8.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "228ed7c16fa39782c3b3468e974aec2795e9089153cd08ee2e9aefb3613334c4" +dependencies = [ + "byteorder", + "num-traits", + "paste", +] + +[[package]] +name = "rmp-serde" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52e599a477cf9840e92f2cde9a7189e67b42c57532749bf90aea6ec10facd4db" +dependencies = [ + "byteorder", + "rmp", + "serde", +] + +[[package]] +name = "ruint" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ecb38f82477f20c5c3d62ef52d7c4e536e38ea9b73fb570a20c5cae0e14bcf6" +dependencies = [ + "alloy-rlp", + "ark-ff 0.3.0", + "ark-ff 0.4.2", + "bytes", + "fastrlp 0.3.1", + "fastrlp 0.4.0", + "num-bigint", + "num-integer", + "num-traits", + "parity-scale-codec", + "primitive-types", + "proptest", + "rand 0.8.5", + "rand 0.9.2", + "rlp", + "ruint-macro", + "serde", + "valuable", + "zeroize", +] + +[[package]] +name = "ruint-macro" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48fd7bd8a6377e15ad9d42a8ec25371b94ddc67abe7c8b9127bec79bebaaae18" + +[[package]] +name = "rustc-demangle" +version = "0.1.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace" + +[[package]] +name = "rustc-hash" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" + +[[package]] +name = "rustc-hex" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" + +[[package]] +name = "rustc_version" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0dfe2087c51c460008730de8b57e6a320782fbfb312e1f4d520e6c6fae155ee" +dependencies = [ + "semver 0.11.0", +] + +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver 1.0.26", +] + +[[package]] +name = "rustix" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11181fbabf243db407ef8df94a6ce0b2f9a733bd8be4ad02b4eda9602296cac8" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.60.2", +] + +[[package]] +name = "rustls-pki-types" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "229a4a4c221013e7e1f1a043678c5cc39fe5171437c88fb47151a21e6f5b5c79" +dependencies = [ + "zeroize", +] + +[[package]] +name = "rustversion" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d" + +[[package]] +name = "rusty-fork" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb3dcc6e454c328bb824492db107ab7c0ae8fcffe4ad210136ef014458c1bc4f" +dependencies = [ + "fnv", + "quick-error", + "tempfile", + "wait-timeout", +] + +[[package]] +name = "ryu" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" + +[[package]] +name = "schannel" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f29ebaa345f945cec9fbbc532eb307f0fdad8161f281b6369539c8d84876b3d" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "schnellru" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "356285bbf17bea63d9e52e96bd18f039672ac92b55b8cb997d6162a2a37d1649" +dependencies = [ + "ahash", + "cfg-if", + "hashbrown 0.13.2", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "sec1" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" +dependencies = [ + "base16ct", + "der", + "generic-array", + "pkcs8", + "subtle", + "zeroize", +] + +[[package]] +name = "security-framework" +version = "2.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" +dependencies = [ + "bitflags", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49db231d56a190491cb4aeda9527f1ad45345af50b0851622a7adb8c03b01c32" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "semver" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6" +dependencies = [ + "semver-parser", +] + +[[package]] +name = "semver" +version = "1.0.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0" + +[[package]] +name = "semver-parser" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9900206b54a3527fdc7b8a938bffd94a568bac4f4aa8113b209df75a09c0dec2" +dependencies = [ + "pest", +] + +[[package]] +name = "serde" +version = "1.0.219" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.219" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "serde_json" +version = "1.0.142" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "030fedb782600dcbd6f02d479bf0d817ac3bb40d644745b769d6a96bc3afc5a7" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "sha2" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest 0.10.7", +] + +[[package]] +name = "sha3" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" +dependencies = [ + "digest 0.10.7", + "keccak", +] + +[[package]] +name = "sha3-asm" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c28efc5e327c837aa837c59eae585fc250715ef939ac32881bcc11677cd02d46" +dependencies = [ + "cc", + "cfg-if", +] + +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "signature" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +dependencies = [ + "digest 0.10.7", + "rand_core 0.6.4", +] + +[[package]] +name = "slab" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04dc19736151f35336d325007ac991178d504a119863a2fcb3758cdb5e52c50d" + +[[package]] +name = "smallvec" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" +dependencies = [ + "serde", +] + +[[package]] +name = "socket2" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "233504af464074f9d066d7b5416c5f9b894a5862a6506e306f7b816cdd6f1807" +dependencies = [ + "libc", + "windows-sys 0.59.0", +] + +[[package]] +name = "spdx" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3e17e880bafaeb362a7b751ec46bdc5b61445a188f80e0606e68167cd540fa3" +dependencies = [ + "smallvec", +] + +[[package]] +name = "spki" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" +dependencies = [ + "base64ct", + "der", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "strum" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af23d6f6c1a224baef9d3f61e287d2761385a5b88fdab4eb4c6f11aeb54c4bcf" +dependencies = [ + "strum_macros", +] + +[[package]] +name = "strum_macros" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7695ce3845ea4b33927c055a39dc438a45b059f7c1b3d91d38d10355fb8cbca7" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.104" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn-solidity" +version = "0.8.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4560533fbd6914b94a8fb5cc803ed6801c3455668db3b810702c57612bac9412" +dependencies = [ + "paste", + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "sync_wrapper" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" +dependencies = [ + "futures-core", +] + +[[package]] +name = "synstructure" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + +[[package]] +name = "tempfile" +version = "3.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8a64e3985349f2441a1a9ef0b853f869006c3855f2cda6862a94d26ebb9d6a1" +dependencies = [ + "fastrand", + "getrandom 0.3.3", + "once_cell", + "rustix", + "windows-sys 0.59.0", +] + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl 1.0.69", +] + +[[package]] +name = "thiserror" +version = "2.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" +dependencies = [ + "thiserror-impl 2.0.12", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "thread_local" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "threadpool" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d050e60b33d41c19108b32cea32164033a9013fe3b46cbd4457559bfbf77afaa" +dependencies = [ + "num_cpus", +] + +[[package]] +name = "tiny-keccak" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" +dependencies = [ + "crunchy", +] + +[[package]] +name = "tinystr" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d4f6d1145dcb577acf783d4e601bc1d76a13337bb54e6233add580b07344c8b" +dependencies = [ + "displaydoc", + "zerovec", +] + +[[package]] +name = "tokio" +version = "1.47.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89e49afdadebb872d3145a5638b59eb0691ea23e46ca484037cfab3b76b95038" +dependencies = [ + "backtrace", + "bytes", + "io-uring", + "libc", + "mio", + "pin-project-lite", + "slab", + "socket2", + "tokio-macros", + "windows-sys 0.59.0", +] + +[[package]] +name = "tokio-macros" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "tokio-native-tls" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +dependencies = [ + "native-tls", + "tokio", +] + +[[package]] +name = "tokio-stream" +version = "0.1.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eca58d7bba4a75707817a2c44174253f9236b2d5fbd055602e9d5c07c139a047" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", + "tokio-util", +] + +[[package]] +name = "tokio-util" +version = "0.7.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14307c986784f72ef81c89db7d9e28d6ac26d16213b109ea501696195e6e3ce5" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "toml_datetime" +version = "0.6.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" + +[[package]] +name = "toml_edit" +version = "0.22.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" +dependencies = [ + "indexmap", + "toml_datetime", + "winnow", +] + +[[package]] +name = "tower" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" +dependencies = [ + "futures-core", + "futures-util", + "pin-project-lite", + "sync_wrapper", + "tokio", + "tower-layer", + "tower-service", +] + +[[package]] +name = "tower-http" +version = "0.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adc82fd73de2a9722ac5da747f12383d2bfdb93591ee6c58486e0097890f05f2" +dependencies = [ + "bitflags", + "bytes", + "futures-util", + "http", + "http-body", + "iri-string", + "pin-project-lite", + "tower", + "tower-layer", + "tower-service", +] + +[[package]] +name = "tower-layer" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" + +[[package]] +name = "tower-service" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" + +[[package]] +name = "tracing" +version = "0.1.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" +dependencies = [ + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "tracing-core" +version = "0.1.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-error" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b1581020d7a273442f5b45074a6a57d5757ad0a47dac0e9f0bd57b81936f3db" +dependencies = [ + "tracing", + "tracing-subscriber", +] + +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + +[[package]] +name = "tracing-serde" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "704b1aeb7be0d0a84fc9828cae51dab5970fee5088f83d1dd7ee6f6246fc6ff1" +dependencies = [ + "serde", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008" +dependencies = [ + "matchers", + "nu-ansi-term", + "once_cell", + "regex", + "serde", + "serde_json", + "sharded-slab", + "smallvec", + "thread_local", + "tracing", + "tracing-core", + "tracing-log", + "tracing-serde", +] + +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + +[[package]] +name = "typenum" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" + +[[package]] +name = "ucd-trie" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971" + +[[package]] +name = "uint" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76f64bba2c53b04fcab63c01a7d7427eadc821e3bc48c34dc9ba29c501164b52" +dependencies = [ + "byteorder", + "crunchy", + "hex", + "static_assertions", +] + +[[package]] +name = "unarray" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" + +[[package]] +name = "unicase" +version = "2.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75b844d17643ee918803943289730bec8aac480150456169e647ed0b576ba539" + +[[package]] +name = "unicode-ident" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" + +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + +[[package]] +name = "url" +version = "2.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + +[[package]] +name = "valuable" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "wait-timeout" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ac3b126d3914f9849036f826e054cbabdc8519970b8998ddaf3b5bd3c65f11" +dependencies = [ + "libc", +] + +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "wasi" +version = "0.14.2+wasi-0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" +dependencies = [ + "wit-bindgen-rt 0.39.0", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" +dependencies = [ + "bumpalo", + "log", + "proc-macro2", + "quote", + "syn 2.0.104", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61" +dependencies = [ + "cfg-if", + "js-sys", + "once_cell", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "wasm-encoder" +version = "0.220.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e913f9242315ca39eff82aee0e19ee7a372155717ff0eb082c741e435ce25ed1" +dependencies = [ + "leb128", + "wasmparser", +] + +[[package]] +name = "wasm-metadata" +version = "0.220.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "185dfcd27fa5db2e6a23906b54c28199935f71d9a27a1a27b3a88d6fee2afae7" +dependencies = [ + "anyhow", + "indexmap", + "serde", + "serde_derive", + "serde_json", + "spdx", + "wasm-encoder", + "wasmparser", +] + +[[package]] +name = "wasmparser" +version = "0.220.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d07b6a3b550fefa1a914b6d54fc175dd11c3392da11eee604e6ffc759805d25" +dependencies = [ + "ahash", + "bitflags", + "hashbrown 0.14.5", + "indexmap", + "semver 1.0.26", +] + +[[package]] +name = "wasmtimer" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8d49b5d6c64e8558d9b1b065014426f35c18de636895d24893dbbd329743446" +dependencies = [ + "futures", + "js-sys", + "parking_lot", + "pin-utils", + "slab", + "wasm-bindgen", +] + +[[package]] +name = "web-sys" +version = "0.3.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-link" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" +dependencies = [ + "windows-targets 0.53.3", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm 0.52.6", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.53.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5fe6031c4041849d7c496a8ded650796e7b6ecc19df1a431c1a363342e5dc91" +dependencies = [ + "windows-link", + "windows_aarch64_gnullvm 0.53.0", + "windows_aarch64_msvc 0.53.0", + "windows_i686_gnu 0.53.0", + "windows_i686_gnullvm 0.53.0", + "windows_i686_msvc 0.53.0", + "windows_x86_64_gnu 0.53.0", + "windows_x86_64_gnullvm 0.53.0", + "windows_x86_64_msvc 0.53.0", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_i686_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" + +[[package]] +name = "winnow" +version = "0.7.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3edebf492c8125044983378ecb5766203ad3b4c2f7a922bd7dd207f6d443e95" +dependencies = [ + "memchr", +] + +[[package]] +name = "wit-bindgen" +version = "0.36.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a2b3e15cd6068f233926e7d8c7c588b2ec4fb7cc7bf3824115e7c7e2a8485a3" +dependencies = [ + "wit-bindgen-rt 0.36.0", + "wit-bindgen-rust-macro", +] + +[[package]] +name = "wit-bindgen-core" +version = "0.36.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b632a5a0fa2409489bd49c9e6d99fcc61bb3d4ce9d1907d44662e75a28c71172" +dependencies = [ + "anyhow", + "heck", + "wit-parser", +] + +[[package]] +name = "wit-bindgen-rt" +version = "0.36.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7947d0131c7c9da3f01dfde0ab8bd4c4cf3c5bd49b6dba0ae640f1fa752572ea" +dependencies = [ + "bitflags", +] + +[[package]] +name = "wit-bindgen-rt" +version = "0.39.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" +dependencies = [ + "bitflags", +] + +[[package]] +name = "wit-bindgen-rust" +version = "0.36.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4329de4186ee30e2ef30a0533f9b3c123c019a237a7c82d692807bf1b3ee2697" +dependencies = [ + "anyhow", + "heck", + "indexmap", + "prettyplease", + "syn 2.0.104", + "wasm-metadata", + "wit-bindgen-core", + "wit-component", +] + +[[package]] +name = "wit-bindgen-rust-macro" +version = "0.36.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "177fb7ee1484d113b4792cc480b1ba57664bbc951b42a4beebe573502135b1fc" +dependencies = [ + "anyhow", + "prettyplease", + "proc-macro2", + "quote", + "syn 2.0.104", + "wit-bindgen-core", + "wit-bindgen-rust", +] + +[[package]] +name = "wit-component" +version = "0.220.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b505603761ed400c90ed30261f44a768317348e49f1864e82ecdc3b2744e5627" +dependencies = [ + "anyhow", + "bitflags", + "indexmap", + "log", + "serde", + "serde_derive", + "serde_json", + "wasm-encoder", + "wasm-metadata", + "wasmparser", + "wit-parser", +] + +[[package]] +name = "wit-parser" +version = "0.220.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae2a7999ed18efe59be8de2db9cb2b7f84d88b27818c79353dfc53131840fe1a" +dependencies = [ + "anyhow", + "id-arena", + "indexmap", + "log", + "semver 1.0.26", + "serde", + "serde_derive", + "serde_json", + "unicode-xid", + "wasmparser", +] + +[[package]] +name = "writeable" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb" + +[[package]] +name = "wyz" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" +dependencies = [ + "tap", +] + +[[package]] +name = "yoke" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f41bb01b8226ef4bfd589436a297c53d118f65921786300e427be8d487695cc" +dependencies = [ + "serde", + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", + "synstructure", +] + +[[package]] +name = "zerocopy" +version = "0.8.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1039dd0d3c310cf05de012d8a39ff557cb0d23087fd44cad61df08fc31907a2f" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "zerofrom" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", + "synstructure", +] + +[[package]] +name = "zeroize" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "zerotrie" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36f0bbd478583f79edad978b407914f61b2972f5af6fa089686016be8f9af595" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", +] + +[[package]] +name = "zerovec" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdbb9122ea75b11bf96e7492afb723e8a7fbe12c67417aa95e7e3d18144d37cd" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", +] diff --git a/src/.DS_Store b/src/.DS_Store deleted file mode 100644 index 5e14423ccbaece3851f9fc893214aa4b215f3025..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6148 zcmeHKO-sZu5Pi`-Ea+j69`g%aym+p43kr)aE+U?)`vER2xNKX``RTsNM6p;sN|Bk8 zyy;|SGI`LX0pRjxbq0(84A}&oC>` zagChz9rL_m$+(J7$xIt8Xs>8r5`VAhCe`#~-F+n5YBC0l0b{@z=nVrrvqgqSiq;zg z#(**K$$;z+o=q@I7!>2y!7g3_hy$9VP?x)e#Q20+!l1|ziqlY{hPrgca2ig#Pkvd# zps3++ac1U8Gk;uOT%4VDAKl?HMeB_LW1!8zp}vmf{4e;)bYAk?A@*hr7z6)|0XLY; zCpj0D&em_sle5;dJ+X<1Ul4^tz4a5o7uiQn;-HRu(dm~Z42qIP>@l2ZKLU{u>x_Xv GVBj75>NNxa diff --git a/src/hyperwallet_client/api.rs b/src/hyperwallet_client/api.rs index 11a2602..03fa821 100644 --- a/src/hyperwallet_client/api.rs +++ b/src/hyperwallet_client/api.rs @@ -1,9 +1,9 @@ use super::types::{ self, Balance, BuildAndSignUserOperationForPaymentRequest, BuildAndSignUserOperationResponse, CreateWalletRequest, ExportWalletResponse, GetTokenBalanceResponse, HyperwalletMessage, - HyperwalletRequest, HyperwalletResponse, ImportWalletRequest, ListWalletsResponse, + HyperwalletRequest, ImportWalletRequest, ListWalletsResponse, PaymasterConfig, RenameWalletRequest, SendEthRequest, SendTokenRequest, SessionId, - SubmitUserOperationResponse, TxReceipt, UnlockWalletRequest, UserOperationReceiptResponse, + TxReceipt, UnlockWalletRequest, UserOperationReceiptResponse, Wallet, }; use super::HyperwalletClientError; @@ -15,21 +15,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 +48,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 +82,84 @@ 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 +167,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 +189,24 @@ 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") + }), + )) + } } // === TRANSACTIONS === @@ -173,22 +217,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 +250,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 +284,20 @@ 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 +305,21 @@ 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,9 +334,9 @@ 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 { + HyperwalletRequest::BuildAndSignUserOperationForPayment(BuildAndSignUserOperationForPaymentRequest { eoa_wallet_id: eoa_wallet_id.to_string(), tba_address: tba_address.to_string(), target: target.to_string(), @@ -283,17 +344,16 @@ pub fn build_and_sign_user_operation_for_payment( 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 +362,44 @@ 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 { + HyperwalletRequest::SubmitUserOperation(types::SubmitUserOperationRequest { signed_user_operation, 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 === @@ -425,9 +484,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..6d8ec8f 100644 --- a/src/hyperwallet_client/mod.rs +++ b/src/hyperwallet_client/mod.rs @@ -9,11 +9,11 @@ pub use types::{ CreateWalletResponse, DeleteWalletRequest, DeleteWalletResponse, ErrorCode, ExecuteViaTbaResponse, ExportWalletRequest, ExportWalletResponse, GetBalanceRequest, GetBalanceResponse, GetTokenBalanceRequest, GetTokenBalanceResponse, GetWalletInfoRequest, - GetWalletInfoResponse, HandshakeConfig, HandshakeRequest, HandshakeStep, HyperwalletMessage, - HyperwalletRequest, HyperwalletResponse, HyperwalletResponseData, ImportWalletRequest, - ImportWalletResponse, ListWalletsResponse, Operation, OperationCategory, OperationError, - PaymasterConfig, ProcessAddress, ProcessPermissions, RenameWalletRequest, SendEthRequest, - SendEthResponse, SendTokenRequest, SendTokenResponse, SessionId, SessionInfo, SpendingLimits, + GetWalletInfoResponse, HandshakeConfig, HandshakeStep, HyperwalletMessage, HyperwalletRequest, + HyperwalletResponse, HyperwalletResponseData, ImportWalletRequest, ImportWalletResponse, + ListWalletsResponse, Operation, OperationCategory, OperationError, PaymasterConfig, + ProcessAddress, ProcessPermissions, RenameWalletRequest, SendEthRequest, SendEthResponse, + SendTokenRequest, SendTokenResponse, SessionId, SessionInfo, SpendingLimits, SubmitUserOperationResponse, TxReceipt, UnlockWalletResponse, UpdatableSetting, UserOperationHash, UserOperationReceiptResponse, WalletAddress, }; @@ -49,17 +49,23 @@ pub fn initialize(config: HandshakeConfig) -> Result = - send_message(hello_message)?; + let welcome_response = 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 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 supported_operations = match welcome_step { types::HandshakeStep::ServerWelcome { @@ -93,18 +99,23 @@ pub fn initialize(config: HandshakeConfig) -> Result = - send_message(register_message)?; + 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 { @@ -129,12 +140,9 @@ 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")) .body(serde_json::to_vec(&message).map_err(HyperwalletClientError::Serialization)?) @@ -142,7 +150,7 @@ where .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/types.rs b/src/hyperwallet_client/types.rs index f18925c..8833d88 100644 --- a/src/hyperwallet_client/types.rs +++ b/src/hyperwallet_client/types.rs @@ -270,153 +270,146 @@ pub enum HandshakeStep { } #[derive(Debug, Clone, Serialize, Deserialize)] -pub struct HyperwalletRequest { - pub data: T, +pub struct HyperwalletMessage { + pub request: HyperwalletRequest, 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 { +pub enum HyperwalletRequest { // Session Management (Unauthenticated) - Handshake(HandshakeRequest), + Handshake(HandshakeStep), // Session Management (Authenticated) - UnlockWallet(HyperwalletRequest), + UnlockWallet(UnlockWalletRequest), // Wallet Lifecycle Management - CreateWallet(HyperwalletRequest), - ImportWallet(HyperwalletRequest), - DeleteWallet(HyperwalletRequest), - RenameWallet(HyperwalletRequest), - ExportWallet(HyperwalletRequest), - ListWallets(HyperwalletRequest<()>), // No parameters needed - GetWalletInfo(HyperwalletRequest), + CreateWallet(CreateWalletRequest), + ImportWallet(ImportWalletRequest), + DeleteWallet(DeleteWalletRequest), + RenameWallet(RenameWalletRequest), + ExportWallet(ExportWalletRequest), + ListWallets, + GetWalletInfo(GetWalletInfoRequest), // 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), + SendEth(SendEthRequest), + SendToken(SendTokenRequest), + ApproveToken(ApproveTokenRequest), + GetBalance(GetBalanceRequest), + GetTokenBalance(GetTokenBalanceRequest), + CallContract(CallContractRequest), + SignTransaction(SignTransactionRequest), + SignMessage(SignMessageRequest), + GetTransactionHistory(GetTransactionHistoryRequest), + EstimateGas(EstimateGasRequest), + GetGasPrice, + GetTransactionReceipt(GetTransactionReceiptRequest), // Token Bound Account Operations - ExecuteViaTba(HyperwalletRequest), - CheckTbaOwnership(HyperwalletRequest), - SetupTbaDelegation(HyperwalletRequest), + ExecuteViaTba(ExecuteViaTbaRequest), + CheckTbaOwnership(CheckTbaOwnershipRequest), + SetupTbaDelegation(SetupTbaDelegationRequest), // Account Abstraction (ERC-4337) - BuildAndSignUserOperationForPayment( - HyperwalletRequest, - ), - SubmitUserOperation(HyperwalletRequest), - GetUserOperationReceipt(HyperwalletRequest), - BuildUserOperation(HyperwalletRequest), - SignUserOperation(HyperwalletRequest), - BuildAndSignUserOperation(HyperwalletRequest), - EstimateUserOperationGas(HyperwalletRequest), - ConfigurePaymaster(HyperwalletRequest), + BuildAndSignUserOperationForPayment(BuildAndSignUserOperationForPaymentRequest), + SubmitUserOperation(SubmitUserOperationRequest), + GetUserOperationReceipt(GetUserOperationReceiptRequest), + BuildUserOperation(BuildUserOperationRequest), + SignUserOperation(SignUserOperationRequest), + BuildAndSignUserOperation(BuildAndSignUserOperationRequest), + EstimateUserOperationGas(EstimateUserOperationGasRequest), + ConfigurePaymaster(ConfigurePaymasterRequest), // Hypermap Operations - ResolveIdentity(HyperwalletRequest), - CreateNote(HyperwalletRequest), - ReadNote(HyperwalletRequest), - SetupDelegation(HyperwalletRequest), - VerifyDelegation(HyperwalletRequest), - MintEntry(HyperwalletRequest), + ResolveIdentity(ResolveIdentityRequest), + CreateNote(CreateNoteRequest), + ReadNote(ReadNoteRequest), + SetupDelegation(SetupDelegationRequest), + VerifyDelegation(VerifyDelegationRequest), + MintEntry(MintEntryRequest), // Process Management (Legacy) - UpdateSpendingLimits(HyperwalletRequest), + UpdateSpendingLimits(UpdateSpendingLimitsRequest), } impl HyperwalletMessage { /// Get the operation type for this message - used for permission checking and routing pub fn operation_type(&self) -> Operation { - match self { + match self.request { // Session Management - HyperwalletMessage::Handshake(_) => Operation::Handshake, - HyperwalletMessage::UnlockWallet(_) => Operation::UnlockWallet, + HyperwalletRequest::Handshake(_) => Operation::Handshake, + HyperwalletRequest::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, + 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 - 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, + 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 - HyperwalletMessage::ExecuteViaTba(_) => Operation::ExecuteViaTba, - HyperwalletMessage::CheckTbaOwnership(_) => Operation::CheckTbaOwnership, - HyperwalletMessage::SetupTbaDelegation(_) => Operation::SetupTbaDelegation, + HyperwalletRequest::ExecuteViaTba(_) => Operation::ExecuteViaTba, + HyperwalletRequest::CheckTbaOwnership(_) => Operation::CheckTbaOwnership, + HyperwalletRequest::SetupTbaDelegation(_) => Operation::SetupTbaDelegation, // Account Abstraction (ERC-4337) - HyperwalletMessage::BuildAndSignUserOperationForPayment(_) => { + HyperwalletRequest::BuildAndSignUserOperationForPayment(_) => { Operation::BuildAndSignUserOperationForPayment } - HyperwalletMessage::SubmitUserOperation(_) => Operation::SubmitUserOperation, - HyperwalletMessage::GetUserOperationReceipt(_) => Operation::GetUserOperationReceipt, - HyperwalletMessage::BuildUserOperation(_) => Operation::BuildUserOperation, - HyperwalletMessage::SignUserOperation(_) => Operation::SignUserOperation, - HyperwalletMessage::BuildAndSignUserOperation(_) => { + HyperwalletRequest::SubmitUserOperation(_) => Operation::SubmitUserOperation, + HyperwalletRequest::GetUserOperationReceipt(_) => Operation::GetUserOperationReceipt, + HyperwalletRequest::BuildUserOperation(_) => Operation::BuildUserOperation, + HyperwalletRequest::SignUserOperation(_) => Operation::SignUserOperation, + HyperwalletRequest::BuildAndSignUserOperation(_) => { Operation::BuildAndSignUserOperation } - HyperwalletMessage::EstimateUserOperationGas(_) => Operation::EstimateUserOperationGas, - HyperwalletMessage::ConfigurePaymaster(_) => Operation::ConfigurePaymaster, + HyperwalletRequest::EstimateUserOperationGas(_) => Operation::EstimateUserOperationGas, + HyperwalletRequest::ConfigurePaymaster(_) => Operation::ConfigurePaymaster, // 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, + 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) - HyperwalletMessage::UpdateSpendingLimits(_) => Operation::UpdateSpendingLimits, + HyperwalletRequest::UpdateSpendingLimits(_) => Operation::UpdateSpendingLimits, } } } /// Unified response type #[derive(Debug, Clone, Serialize, Deserialize)] -pub struct HyperwalletResponse { +pub struct HyperwalletResponse { pub success: bool, - pub data: Option, + pub data: Option, pub error: Option, pub request_id: Option, } -impl HyperwalletResponse { - pub fn success(data: T) -> Self { +impl HyperwalletResponse { + pub fn success(data: HyperwalletResponseData) -> Self { Self { success: true, data: Some(data), @@ -433,18 +426,6 @@ 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)] From 3243620a972fb2d56207b41812987c7f5697a8ba Mon Sep 17 00:00:00 2001 From: hosted-fornet Date: Tue, 5 Aug 2025 15:54:19 -0700 Subject: [PATCH 88/96] hyperwallet: wit-ify --- hyperware-wit/hyperwallet.wit | 631 ++++++++ hyperware-wit/process-lib.wit | 1 + src/hyperwallet_client/api.rs | 77 +- src/hyperwallet_client/mod.rs | 27 +- src/hyperwallet_client/serde_impls.rs | 1348 +++++++++++++++++ .../serde_request_response_impls.rs | 130 ++ src/hyperwallet_client/serde_variant_impls.rs | 427 ++++++ src/hyperwallet_client/types.rs | 1182 ++++----------- src/signer.rs | 1 + src/wallet.rs | 28 +- 10 files changed, 2864 insertions(+), 988 deletions(-) create mode 100644 hyperware-wit/hyperwallet.wit create mode 100644 src/hyperwallet_client/serde_impls.rs create mode 100644 src/hyperwallet_client/serde_request_response_impls.rs create mode 100644 src/hyperwallet_client/serde_variant_impls.rs diff --git a/hyperware-wit/hyperwallet.wit b/hyperware-wit/hyperwallet.wit new file mode 100644 index 0000000..1e67bc3 --- /dev/null +++ b/hyperware-wit/hyperwallet.wit @@ -0,0 +1,631 @@ +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), + + // 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), + + // 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, + } + + 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, + } + + 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, + } +} 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/hyperwallet_client/api.rs b/src/hyperwallet_client/api.rs index 03fa821..d92e6e4 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, ImportWalletRequest, ListWalletsResponse, - PaymasterConfig, RenameWalletRequest, SendEthRequest, SendTokenRequest, SessionId, - TxReceipt, UnlockWalletRequest, UserOperationReceiptResponse, - Wallet, + HyperwalletRequest, ImportWalletRequest, ListWalletsResponse, PaymasterConfig, + RenameWalletRequest, SendEthRequest, SendTokenRequest, SessionId, TxReceipt, + UnlockWalletRequest, UserOperationReceiptResponse, Wallet, }; use super::HyperwalletClientError; use crate::wallet; @@ -125,17 +124,15 @@ pub fn get_wallet_info( 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, - }) - } + 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"), )), @@ -293,7 +290,9 @@ pub fn get_balance( let response = super::send_message(message)?; match response.data { - Some(types::HyperwalletResponseData::GetBalance(balance_response)) => Ok(balance_response.balance), + Some(types::HyperwalletResponseData::GetBalance(balance_response)) => { + Ok(balance_response.balance) + } _ => Err(HyperwalletClientError::ServerError( types::OperationError::internal_error("Missing balance data in response"), )), @@ -315,7 +314,9 @@ pub fn get_token_balance( let response = super::send_message(message)?; match response.data { - Some(types::HyperwalletResponseData::GetTokenBalance(balance_response)) => Ok(balance_response), + Some(types::HyperwalletResponseData::GetTokenBalance(balance_response)) => { + Ok(balance_response) + } _ => Err(HyperwalletClientError::ServerError( types::OperationError::internal_error("Missing token balance data in response"), )), @@ -336,20 +337,24 @@ pub fn build_and_sign_user_operation_for_payment( ) -> Result { let message = build_message( session_id, - 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()), - }), + 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 response = super::send_message(message)?; match response.data { - Some(types::HyperwalletResponseData::BuildAndSignUserOperationForPayment(build_response)) => Ok(build_response), + Some(types::HyperwalletResponseData::BuildAndSignUserOperationForPayment( + build_response, + )) => Ok(build_response), _ => Err(HyperwalletClientError::ServerError( types::OperationError::internal_error("Missing UserOperation build response data"), )), @@ -365,7 +370,8 @@ pub fn submit_user_operation( let message = build_message( session_id, HyperwalletRequest::SubmitUserOperation(types::SubmitUserOperationRequest { - signed_user_operation, + 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()), }), @@ -395,7 +401,9 @@ pub fn get_user_operation_receipt( let response = super::send_message(message)?; match response.data { - Some(types::HyperwalletResponseData::GetUserOperationReceipt(receipt_response)) => Ok(receipt_response), + Some(types::HyperwalletResponseData::GetUserOperationReceipt(receipt_response)) => { + Ok(receipt_response) + } _ => Err(HyperwalletClientError::ServerError( types::OperationError::internal_error("Missing UserOperation receipt data in response"), )), @@ -428,7 +436,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 )?; @@ -445,10 +454,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) } diff --git a/src/hyperwallet_client/mod.rs b/src/hyperwallet_client/mod.rs index 6d8ec8f..43a3ff2 100644 --- a/src/hyperwallet_client/mod.rs +++ b/src/hyperwallet_client/mod.rs @@ -2,6 +2,9 @@ use crate::Request; use thiserror::Error; pub mod api; +pub mod serde_impls; +mod serde_request_response_impls; +mod serde_variant_impls; pub mod types; pub use types::{ Balance, BuildAndSignUserOperationForPaymentRequest, BuildAndSignUserOperationResponse, @@ -45,10 +48,10 @@ pub enum HyperwalletClientError { pub fn initialize(config: HandshakeConfig) -> Result { let client_name = config.client_name.expect("Client name is required"); - let hello_step = types::HandshakeStep::ClientHello { + let hello_step = types::HandshakeStep::ClientHello(types::ClientHello { client_version: "0.1.0".to_string(), client_name, - }; + }); let hello_message = types::HyperwalletMessage { request: types::HyperwalletRequest::Handshake(hello_step), session_id: String::new(), @@ -68,10 +71,7 @@ pub fn initialize(config: HandshakeConfig) -> Result supported_operations, + types::HandshakeStep::ServerWelcome(server_welcome) => server_welcome.supported_operations, _ => { return Err(HyperwalletClientError::ServerError( types::OperationError::internal_error( @@ -94,10 +94,10 @@ pub fn initialize(config: HandshakeConfig) -> Result Result { - Ok(SessionInfo { + types::HandshakeStep::Complete(complete_handshake) => { + Ok(types::SessionInfo { server_version: "0.1.0".to_string(), //lol, server should send it's version - session_id, - registered_permissions, + session_id: complete_handshake.session_id, + registered_permissions: complete_handshake.registered_permissions, initial_chain_id: config.initial_chain_id, }) } diff --git a/src/hyperwallet_client/serde_impls.rs b/src/hyperwallet_client/serde_impls.rs new file mode 100644 index 0000000..c56e71a --- /dev/null +++ b/src/hyperwallet_client/serde_impls.rs @@ -0,0 +1,1348 @@ +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 = Some(map.next_value()?); + } + Field::Error => { + if error.is_some() { + return Err(de::Error::duplicate_field("error")); + } + error = Some(map.next_value()?); + } + Field::RequestId => { + if request_id.is_some() { + return Err(de::Error::duplicate_field("request_id")); + } + request_id = Some(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")); + } + details = Some(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, + { + let mut state = serializer.serialize_struct("CreateWalletRequest", 2)?; + state.serialize_field("name", &self.name)?; + state.serialize_field("password", &self.password)?; + 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..0cb407a --- /dev/null +++ b/src/hyperwallet_client/serde_request_response_impls.rs @@ -0,0 +1,130 @@ +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>, + { + let _val = serde_json::Value::deserialize(deserializer)?; + Err(de::Error::custom(concat!($name, " deserialization not yet implemented"))) + } + } + }; +} + +// 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_variant_impls.rs b/src/hyperwallet_client/serde_variant_impls.rs new file mode 100644 index 0000000..81f5e4f --- /dev/null +++ b/src/hyperwallet_client/serde_variant_impls.rs @@ -0,0 +1,427 @@ +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)?; + } + } + + 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)) + } + "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), + _ => { + // For unimplemented variants, return an error + Err(de::Error::unknown_variant(&variant_type, &[])) + } + } + } + } + + 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)?; + } + } + + 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)) + } + _ => { + // For unimplemented variants, return an error + Err(de::Error::unknown_variant(&variant_type, &[])) + } + } + } + } + + 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 8833d88..d2aeb2e 100644 --- a/src/hyperwallet_client/types.rs +++ b/src/hyperwallet_client/types.rs @@ -1,192 +1,76 @@ -//! Shared types for the Hyperwallet protocol, used by both the client and the server. - -use serde::{Deserialize, Serialize}; -use std::collections::HashSet; - -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, -} - -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] -pub enum OperationCategory { - System, - ProcessManagement, - WalletManagement, - Ethereum, - TokenBoundAccount, - ERC4337, - Hypermap, - Query, - Advanced, -} - -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, - ] +//! 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, +}; + +// 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(), + } } +} - 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, +// Re-export request types +pub use wit::{ + ApproveTokenRequest, BuildAndSignUserOperationForPaymentRequest, + BuildAndSignUserOperationRequest, BuildUserOperationRequest, CallContractRequest, + CheckTbaOwnershipRequest, ConfigurePaymasterRequest, CreateNoteRequest, EstimateGasRequest, + EstimateUserOperationGasRequest, ExecuteViaTbaRequest, GetTransactionHistoryRequest, + GetTransactionReceiptRequest, GetUserOperationReceiptRequest, MintEntryRequest, + ReadNoteRequest, ResolveIdentityRequest, SetupDelegationRequest, SetupTbaDelegationRequest, + SignMessageRequest, SignTransactionRequest, SignUserOperationRequest, + SubmitUserOperationRequest, UpdateSpendingLimitsRequest, VerifyDelegationRequest, +}; + +// Re-export response types +pub use wit::{ + BuildAndSignUserOperationResponse, CheckTbaOwnershipResponse, SubmitUserOperationResponse, +}; + +// Type aliases for compatibility +pub type ProcessAddress = crate::Address; - Operation::ResolveIdentity - | Operation::CreateNote - | Operation::ReadNote - | Operation::SetupDelegation - | Operation::VerifyDelegation - | Operation::MintEntry => OperationCategory::Hypermap, +// Additional types that need conversion or special handling +pub use wit::HyperwalletMessage; +pub use wit::ProcessPermissions; - Operation::BatchOperations - | Operation::ScheduleOperation - | Operation::CancelOperation => OperationCategory::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, } +/// 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 +79,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 +93,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,177 +130,179 @@ 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 HyperwalletMessage { - pub request: HyperwalletRequest, - pub session_id: SessionId, -} - -/// Typed message enum for type-safe communication -#[derive(Debug, Clone, Serialize, Deserialize)] -#[serde(tag = "operation")] -pub enum HyperwalletRequest { - // Session Management (Unauthenticated) - Handshake(HandshakeStep), - - // Session Management (Authenticated) - UnlockWallet(UnlockWalletRequest), - - // Wallet Lifecycle Management - CreateWallet(CreateWalletRequest), - ImportWallet(ImportWalletRequest), - DeleteWallet(DeleteWalletRequest), - RenameWallet(RenameWalletRequest), - ExportWallet(ExportWalletRequest), - ListWallets, - GetWalletInfo(GetWalletInfoRequest), - - // Ethereum Operations - SendEth(SendEthRequest), - SendToken(SendTokenRequest), - ApproveToken(ApproveTokenRequest), - GetBalance(GetBalanceRequest), - GetTokenBalance(GetTokenBalanceRequest), - CallContract(CallContractRequest), - SignTransaction(SignTransactionRequest), - SignMessage(SignMessageRequest), - GetTransactionHistory(GetTransactionHistoryRequest), - EstimateGas(EstimateGasRequest), - GetGasPrice, - GetTransactionReceipt(GetTransactionReceiptRequest), - - // Token Bound Account Operations - ExecuteViaTba(ExecuteViaTbaRequest), - CheckTbaOwnership(CheckTbaOwnershipRequest), - SetupTbaDelegation(SetupTbaDelegationRequest), - - // Account Abstraction (ERC-4337) - BuildAndSignUserOperationForPayment(BuildAndSignUserOperationForPaymentRequest), - SubmitUserOperation(SubmitUserOperationRequest), - GetUserOperationReceipt(GetUserOperationReceiptRequest), - BuildUserOperation(BuildUserOperationRequest), - SignUserOperation(SignUserOperationRequest), - BuildAndSignUserOperation(BuildAndSignUserOperationRequest), - EstimateUserOperationGas(EstimateUserOperationGasRequest), - ConfigurePaymaster(ConfigurePaymasterRequest), - - // Hypermap Operations - ResolveIdentity(ResolveIdentityRequest), - CreateNote(CreateNoteRequest), - ReadNote(ReadNoteRequest), - SetupDelegation(SetupDelegationRequest), - VerifyDelegation(VerifyDelegationRequest), - MintEntry(MintEntryRequest), - - // Process Management (Legacy) - UpdateSpendingLimits(UpdateSpendingLimitsRequest), -} - -impl HyperwalletMessage { - /// Get the operation type for this message - used for permission checking and routing - pub fn operation_type(&self) -> Operation { - match self.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, +/// 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 - HyperwalletRequest::ResolveIdentity(_) => Operation::ResolveIdentity, - HyperwalletRequest::CreateNote(_) => Operation::CreateNote, - HyperwalletRequest::ReadNote(_) => Operation::ReadNote, - HyperwalletRequest::SetupDelegation(_) => Operation::SetupDelegation, - HyperwalletRequest::VerifyDelegation(_) => Operation::VerifyDelegation, - HyperwalletRequest::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) - HyperwalletRequest::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, + + // 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 { +// Helper functions for HyperwalletResponse +impl wit::HyperwalletResponse { pub fn success(data: HyperwalletResponseData) -> Self { Self { success: true, @@ -428,286 +322,8 @@ impl HyperwalletResponse { } } -#[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_verification_gas: "0x7a120".to_string(), // 500000 - paymaster_post_op_gas: "0x493e0".to_string(), // 300000 - } - } -} - -#[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, @@ -800,54 +416,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() @@ -858,258 +439,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/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( From ac4d308855aa73cdb2eb4fdc98230fb97b81863f Mon Sep 17 00:00:00 2001 From: hosted-fornet Date: Tue, 5 Aug 2025 16:02:15 -0700 Subject: [PATCH 89/96] hyperwallet: gate behind a feature flag --- Cargo.toml | 9 +++++---- src/lib.rs | 3 +++ 2 files changed, 8 insertions(+), 4 deletions(-) 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/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; From 4764ce3dd090d3e7de7a43cd479001c26f450cd4 Mon Sep 17 00:00:00 2001 From: hosted-fornet Date: Tue, 5 Aug 2025 16:35:01 -0700 Subject: [PATCH 90/96] hyperwallet: consolidate wit file --- hyperware-wit/{hyperwallet.wit => hyperwallet:sys-v0.wit} | 5 +++++ 1 file changed, 5 insertions(+) rename hyperware-wit/{hyperwallet.wit => hyperwallet:sys-v0.wit} (99%) diff --git a/hyperware-wit/hyperwallet.wit b/hyperware-wit/hyperwallet:sys-v0.wit similarity index 99% rename from hyperware-wit/hyperwallet.wit rename to hyperware-wit/hyperwallet:sys-v0.wit index 1e67bc3..a65942e 100644 --- a/hyperware-wit/hyperwallet.wit +++ b/hyperware-wit/hyperwallet:sys-v0.wit @@ -629,3 +629,8 @@ interface hyperwallet { is-owned: bool, } } + +world hyperwallet-sys-v0 { + import hyperwallet; + include process-v1; +} From 1500de250995aefc3c4d7f44dc0bcaf189e9708d Mon Sep 17 00:00:00 2001 From: Hallmane Date: Wed, 6 Aug 2025 22:36:13 +0200 Subject: [PATCH 91/96] hyperware pm tested unsuccessfully --- .DS_Store | Bin 6148 -> 6148 bytes Cargo.lock | 4058 +++++++++++++++++++++++++++++++ src/hyperwallet_client/types.rs | 5 +- 3 files changed, 4061 insertions(+), 2 deletions(-) create mode 100644 Cargo.lock diff --git a/.DS_Store b/.DS_Store index 09bf23a6998f704b3768c0c09d9d572dd5aaa4dd..7b01a1d8a3876b0e83d857366cd3653f3982b55d 100644 GIT binary patch delta 452 zcmZoMXfc@JFDk*nz`)4BAi%&-%8067L`odCmR9u~n9pVUtI(BQaoeGfM^|GY1Rz}_~dprS#}@?%wEkV%Y5=s!o2YjElS delta 305 zcmZoMXfc@JFDSymz`)4BAi$85ZWx@LpIfk5kYhD7qsZhrERu0b=0}0jEDU-K=?s|+ zCAs-7E=f80NkB0Umk!-cjlYjMqROY>l`qJESOL_-z+n1dvoLE0BQx81>B)6$V)d`T zNA*JuWM(L4C}K#?%|{rADT`tlhXBY(Hii<0M4--ehE#?Un14VL|0I82MVCkR%y(^T imR_&d$0m!j%d!JGV74{8EHeXx!Nx)x#?9;;fB6A6DN{!P diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..8371f08 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,4058 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "addr2line" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" + +[[package]] +name = "ahash" +version = "0.8.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" +dependencies = [ + "cfg-if", + "getrandom 0.3.3", + "once_cell", + "version_check", + "zerocopy", +] + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "allocator-api2" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" + +[[package]] +name = "alloy" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59febb24956a41c29bb5f450978fbe825bd6456b3f80586c8bd558dc882e7b6a" +dependencies = [ + "alloy-consensus", + "alloy-core", + "alloy-eips", + "alloy-genesis", + "alloy-json-rpc", + "alloy-network", + "alloy-provider", + "alloy-rpc-client", + "alloy-rpc-types", + "alloy-serde", + "alloy-signer", + "alloy-signer-local", + "alloy-transport", + "alloy-transport-http", +] + +[[package]] +name = "alloy-chains" +version = "0.1.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28e2652684758b0d9b389d248b209ed9fd9989ef489a550265fe4bb8454fe7eb" +dependencies = [ + "alloy-primitives", + "num_enum", + "strum", +] + +[[package]] +name = "alloy-consensus" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e88e1edea70787c33e11197d3f32ae380f3db19e6e061e539a5bcf8184a6b326" +dependencies = [ + "alloy-eips", + "alloy-primitives", + "alloy-rlp", + "alloy-serde", + "alloy-trie", + "auto_impl", + "c-kzg", + "derive_more 1.0.0", + "serde", +] + +[[package]] +name = "alloy-consensus-any" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57b1bb53f40c0273cd1975573cd457b39213e68584e36d1401d25fd0398a1d65" +dependencies = [ + "alloy-consensus", + "alloy-eips", + "alloy-primitives", + "alloy-rlp", + "alloy-serde", + "serde", +] + +[[package]] +name = "alloy-core" +version = "0.8.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d8bcce99ad10fe02640cfaec1c6bc809b837c783c1d52906aa5af66e2a196f6" +dependencies = [ + "alloy-dyn-abi", + "alloy-json-abi", + "alloy-primitives", + "alloy-rlp", + "alloy-sol-types", +] + +[[package]] +name = "alloy-dyn-abi" +version = "0.8.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb8e762aefd39a397ff485bc86df673465c4ad3ec8819cc60833a8a3ba5cdc87" +dependencies = [ + "alloy-json-abi", + "alloy-primitives", + "alloy-sol-type-parser", + "alloy-sol-types", + "const-hex", + "itoa", + "serde", + "serde_json", + "winnow", +] + +[[package]] +name = "alloy-eip2930" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0069cf0642457f87a01a014f6dc29d5d893cd4fd8fddf0c3cdfad1bb3ebafc41" +dependencies = [ + "alloy-primitives", + "alloy-rlp", + "serde", +] + +[[package]] +name = "alloy-eip7702" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c986539255fb839d1533c128e190e557e52ff652c9ef62939e233a81dd93f7e" +dependencies = [ + "alloy-primitives", + "alloy-rlp", + "derive_more 1.0.0", + "serde", +] + +[[package]] +name = "alloy-eips" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f9fadfe089e9ccc0650473f2d4ef0a28bc015bbca5631d9f0f09e49b557fdb3" +dependencies = [ + "alloy-eip2930", + "alloy-eip7702", + "alloy-primitives", + "alloy-rlp", + "alloy-serde", + "c-kzg", + "derive_more 1.0.0", + "once_cell", + "serde", + "sha2", +] + +[[package]] +name = "alloy-genesis" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b2a4cf7b70f3495788e74ce1c765260ffe38820a2a774ff4aacb62e31ea73f9" +dependencies = [ + "alloy-primitives", + "alloy-serde", + "alloy-trie", + "serde", +] + +[[package]] +name = "alloy-json-abi" +version = "0.8.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe6beff64ad0aa6ad1019a3db26fef565aefeb011736150ab73ed3366c3cfd1b" +dependencies = [ + "alloy-primitives", + "alloy-sol-type-parser", + "serde", + "serde_json", +] + +[[package]] +name = "alloy-json-rpc" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e29040b9d5fe2fb70415531882685b64f8efd08dfbd6cc907120650504821105" +dependencies = [ + "alloy-primitives", + "alloy-sol-types", + "serde", + "serde_json", + "thiserror 2.0.12", + "tracing", +] + +[[package]] +name = "alloy-network" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "510cc00b318db0dfccfdd2d032411cfae64fc144aef9679409e014145d3dacc4" +dependencies = [ + "alloy-consensus", + "alloy-consensus-any", + "alloy-eips", + "alloy-json-rpc", + "alloy-network-primitives", + "alloy-primitives", + "alloy-rpc-types-any", + "alloy-rpc-types-eth", + "alloy-serde", + "alloy-signer", + "alloy-sol-types", + "async-trait", + "auto_impl", + "futures-utils-wasm", + "serde", + "serde_json", + "thiserror 2.0.12", +] + +[[package]] +name = "alloy-network-primitives" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9081c099e798b8a2bba2145eb82a9a146f01fc7a35e9ab6e7b43305051f97550" +dependencies = [ + "alloy-consensus", + "alloy-eips", + "alloy-primitives", + "alloy-serde", + "serde", +] + +[[package]] +name = "alloy-primitives" +version = "0.8.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c77490fe91a0ce933a1f219029521f20fc28c2c0ca95d53fa4da9c00b8d9d4e" +dependencies = [ + "alloy-rlp", + "bytes", + "cfg-if", + "const-hex", + "derive_more 2.0.1", + "foldhash", + "hashbrown 0.15.4", + "indexmap", + "itoa", + "k256", + "keccak-asm", + "paste", + "proptest", + "rand 0.8.5", + "ruint", + "rustc-hash", + "serde", + "sha3", + "tiny-keccak", +] + +[[package]] +name = "alloy-provider" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc2dfaddd9a30aa870a78a4e1316e3e115ec1e12e552cbc881310456b85c1f24" +dependencies = [ + "alloy-chains", + "alloy-consensus", + "alloy-eips", + "alloy-json-rpc", + "alloy-network", + "alloy-network-primitives", + "alloy-primitives", + "alloy-rpc-client", + "alloy-rpc-types-eth", + "alloy-transport", + "alloy-transport-http", + "async-stream", + "async-trait", + "auto_impl", + "dashmap", + "futures", + "futures-utils-wasm", + "lru", + "parking_lot", + "pin-project", + "reqwest", + "schnellru", + "serde", + "serde_json", + "thiserror 2.0.12", + "tokio", + "tracing", + "url", + "wasmtimer", +] + +[[package]] +name = "alloy-rlp" +version = "0.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f70d83b765fdc080dbcd4f4db70d8d23fe4761f2f02ebfa9146b833900634b4" +dependencies = [ + "alloy-rlp-derive", + "arrayvec", + "bytes", +] + +[[package]] +name = "alloy-rlp-derive" +version = "0.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64b728d511962dda67c1bc7ea7c03736ec275ed2cf4c35d9585298ac9ccf3b73" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "alloy-rpc-client" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "531137b283547d5b9a5cafc96b006c64ef76810c681d606f28be9781955293b6" +dependencies = [ + "alloy-json-rpc", + "alloy-primitives", + "alloy-transport", + "alloy-transport-http", + "futures", + "pin-project", + "reqwest", + "serde", + "serde_json", + "tokio", + "tokio-stream", + "tower", + "tracing", + "url", + "wasmtimer", +] + +[[package]] +name = "alloy-rpc-types" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3410a472ce26c457e9780f708ee6bd540b30f88f1f31fdab7a11d00bd6aa1aee" +dependencies = [ + "alloy-primitives", + "alloy-rpc-types-eth", + "alloy-serde", + "serde", +] + +[[package]] +name = "alloy-rpc-types-any" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed98e1af55a7d856bfa385f30f63d8d56be2513593655c904a8f4a7ec963aa3e" +dependencies = [ + "alloy-consensus-any", + "alloy-rpc-types-eth", + "alloy-serde", +] + +[[package]] +name = "alloy-rpc-types-eth" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8737d7a6e37ca7bba9c23e9495c6534caec6760eb24abc9d5ffbaaba147818e1" +dependencies = [ + "alloy-consensus", + "alloy-consensus-any", + "alloy-eips", + "alloy-network-primitives", + "alloy-primitives", + "alloy-rlp", + "alloy-serde", + "alloy-sol-types", + "derive_more 1.0.0", + "itertools 0.13.0", + "serde", + "serde_json", +] + +[[package]] +name = "alloy-serde" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5851bf8d5ad33014bd0c45153c603303e730acc8a209450a7ae6b4a12c2789e2" +dependencies = [ + "alloy-primitives", + "serde", + "serde_json", +] + +[[package]] +name = "alloy-signer" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e10ca565da6500cca015ba35ee424d59798f2e1b85bc0dd8f81dafd401f029a" +dependencies = [ + "alloy-primitives", + "async-trait", + "auto_impl", + "elliptic-curve", + "k256", + "thiserror 2.0.12", +] + +[[package]] +name = "alloy-signer-local" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47fababf5a745133490cde927d48e50267f97d3d1209b9fc9f1d1d666964d172" +dependencies = [ + "alloy-consensus", + "alloy-network", + "alloy-primitives", + "alloy-signer", + "async-trait", + "k256", + "rand 0.8.5", + "thiserror 2.0.12", +] + +[[package]] +name = "alloy-sol-macro" +version = "0.8.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e10ae8e9a91d328ae954c22542415303919aabe976fe7a92eb06db1b68fd59f2" +dependencies = [ + "alloy-sol-macro-expander", + "alloy-sol-macro-input", + "proc-macro-error2", + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "alloy-sol-macro-expander" +version = "0.8.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83ad5da86c127751bc607c174d6c9fe9b85ef0889a9ca0c641735d77d4f98f26" +dependencies = [ + "alloy-sol-macro-input", + "const-hex", + "heck", + "indexmap", + "proc-macro-error2", + "proc-macro2", + "quote", + "syn 2.0.104", + "syn-solidity", + "tiny-keccak", +] + +[[package]] +name = "alloy-sol-macro-input" +version = "0.8.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3d30f0d3f9ba3b7686f3ff1de9ee312647aac705604417a2f40c604f409a9e" +dependencies = [ + "const-hex", + "dunce", + "heck", + "macro-string", + "proc-macro2", + "quote", + "syn 2.0.104", + "syn-solidity", +] + +[[package]] +name = "alloy-sol-type-parser" +version = "0.8.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d162f8524adfdfb0e4bd0505c734c985f3e2474eb022af32eef0d52a4f3935c" +dependencies = [ + "serde", + "winnow", +] + +[[package]] +name = "alloy-sol-types" +version = "0.8.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d43d5e60466a440230c07761aa67671d4719d46f43be8ea6e7ed334d8db4a9ab" +dependencies = [ + "alloy-json-abi", + "alloy-primitives", + "alloy-sol-macro", + "const-hex", + "serde", +] + +[[package]] +name = "alloy-transport" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "538a04a37221469cac0ce231b737fd174de2fdfcdd843bdd068cb39ed3e066ad" +dependencies = [ + "alloy-json-rpc", + "base64", + "futures-util", + "futures-utils-wasm", + "serde", + "serde_json", + "thiserror 2.0.12", + "tokio", + "tower", + "tracing", + "url", + "wasmtimer", +] + +[[package]] +name = "alloy-transport-http" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ed40eb1e1265b2911512f6aa1dcece9702d078f5a646730c45e39e2be00ac1c" +dependencies = [ + "alloy-json-rpc", + "alloy-transport", + "reqwest", + "serde_json", + "tower", + "tracing", + "url", +] + +[[package]] +name = "alloy-trie" +version = "0.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d95a94854e420f07e962f7807485856cde359ab99ab6413883e15235ad996e8b" +dependencies = [ + "alloy-primitives", + "alloy-rlp", + "arrayvec", + "derive_more 1.0.0", + "nybbles", + "serde", + "smallvec", + "tracing", +] + +[[package]] +name = "anyhow" +version = "1.0.98" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" + +[[package]] +name = "ark-ff" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b3235cc41ee7a12aaaf2c575a2ad7b46713a8a50bda2fc3b003a04845c05dd6" +dependencies = [ + "ark-ff-asm 0.3.0", + "ark-ff-macros 0.3.0", + "ark-serialize 0.3.0", + "ark-std 0.3.0", + "derivative", + "num-bigint", + "num-traits", + "paste", + "rustc_version 0.3.3", + "zeroize", +] + +[[package]] +name = "ark-ff" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec847af850f44ad29048935519032c33da8aa03340876d351dfab5660d2966ba" +dependencies = [ + "ark-ff-asm 0.4.2", + "ark-ff-macros 0.4.2", + "ark-serialize 0.4.2", + "ark-std 0.4.0", + "derivative", + "digest 0.10.7", + "itertools 0.10.5", + "num-bigint", + "num-traits", + "paste", + "rustc_version 0.4.1", + "zeroize", +] + +[[package]] +name = "ark-ff-asm" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db02d390bf6643fb404d3d22d31aee1c4bc4459600aef9113833d17e786c6e44" +dependencies = [ + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-ff-asm" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ed4aa4fe255d0bc6d79373f7e31d2ea147bcf486cba1be5ba7ea85abdb92348" +dependencies = [ + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-ff-macros" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db2fd794a08ccb318058009eefdf15bcaaaaf6f8161eb3345f907222bac38b20" +dependencies = [ + "num-bigint", + "num-traits", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-ff-macros" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7abe79b0e4288889c4574159ab790824d0033b9fdcb2a112a3182fac2e514565" +dependencies = [ + "num-bigint", + "num-traits", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-serialize" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d6c2b318ee6e10f8c2853e73a83adc0ccb88995aa978d8a3408d492ab2ee671" +dependencies = [ + "ark-std 0.3.0", + "digest 0.9.0", +] + +[[package]] +name = "ark-serialize" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adb7b85a02b83d2f22f89bd5cac66c9c89474240cb6207cb1efc16d098e822a5" +dependencies = [ + "ark-std 0.4.0", + "digest 0.10.7", + "num-bigint", +] + +[[package]] +name = "ark-std" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1df2c09229cbc5a028b1d70e00fdb2acee28b1055dfb5ca73eea49c5a25c4e7c" +dependencies = [ + "num-traits", + "rand 0.8.5", +] + +[[package]] +name = "ark-std" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94893f1e0c6eeab764ade8dc4c0db24caf4fe7cbbaafc0eba0a9030f447b5185" +dependencies = [ + "num-traits", + "rand 0.8.5", +] + +[[package]] +name = "arrayvec" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" +dependencies = [ + "serde", +] + +[[package]] +name = "async-stream" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b5a71a6f37880a80d1d7f19efd781e4b5de42c88f0722cc13bcb6cc2cfe8476" +dependencies = [ + "async-stream-impl", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-stream-impl" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "async-trait" +version = "0.1.88" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e539d3fca749fcee5236ab05e93a52867dd549cc157c8cb7f99595f3cedffdb5" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "auto_impl" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffdcb70bdbc4d478427380519163274ac86e52916e10f0a8889adf0f96d3fee7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "autocfg" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" + +[[package]] +name = "backtrace" +version = "0.3.75" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002" +dependencies = [ + "addr2line", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", + "windows-targets 0.52.6", +] + +[[package]] +name = "base16ct" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "base64ct" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55248b47b0caf0546f7988906588779981c43bb1bc9d0c44087278f80cdb44ba" + +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + +[[package]] +name = "bit-set" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3" +dependencies = [ + "bit-vec", +] + +[[package]] +name = "bit-vec" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" + +[[package]] +name = "bitflags" +version = "2.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" + +[[package]] +name = "bitvec" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" +dependencies = [ + "funty", + "radium", + "tap", + "wyz", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "blst" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fd49896f12ac9b6dcd7a5998466b9b58263a695a3dd1ecc1aaca2e12a90b080" +dependencies = [ + "cc", + "glob", + "threadpool", + "zeroize", +] + +[[package]] +name = "bumpalo" +version = "3.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" + +[[package]] +name = "byte-slice-cast" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7575182f7272186991736b70173b0ea045398f984bf5ebbb3804736ce1330c9d" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" +dependencies = [ + "serde", +] + +[[package]] +name = "c-kzg" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0307f72feab3300336fb803a57134159f6e20139af1357f36c54cb90d8e8928" +dependencies = [ + "blst", + "cc", + "glob", + "hex", + "libc", + "once_cell", + "serde", +] + +[[package]] +name = "cc" +version = "1.2.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3a42d84bb6b69d3a8b3eaacf0d88f179e1929695e1ad012b6cf64d9caaa5fd2" +dependencies = [ + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268" + +[[package]] +name = "color-eyre" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5920befb47832a6d61ee3a3a846565cfa39b331331e68a3b1d1116630f2f26d" +dependencies = [ + "backtrace", + "color-spantrace", + "eyre", + "indenter", + "once_cell", + "owo-colors", + "tracing-error", +] + +[[package]] +name = "color-spantrace" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8b88ea9df13354b55bc7234ebcce36e6ef896aca2e42a15de9e10edce01b427" +dependencies = [ + "once_cell", + "owo-colors", + "tracing-core", + "tracing-error", +] + +[[package]] +name = "const-hex" +version = "1.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83e22e0ed40b96a48d3db274f72fd365bd78f67af39b6bbd47e8a15e1c6207ff" +dependencies = [ + "cfg-if", + "cpufeatures", + "hex", + "proptest", + "serde", +] + +[[package]] +name = "const-oid" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" + +[[package]] +name = "const_format" +version = "0.2.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "126f97965c8ad46d6d9163268ff28432e8f6a1196a55578867832e3049df63dd" +dependencies = [ + "const_format_proc_macros", +] + +[[package]] +name = "const_format_proc_macros" +version = "0.2.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d57c2eccfb16dbac1f4e61e206105db5820c9d26c3c472bc17c774259ef7744" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + +[[package]] +name = "crunchy" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" + +[[package]] +name = "crypto-bigint" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" +dependencies = [ + "generic-array", + "rand_core 0.6.4", + "subtle", + "zeroize", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "dashmap" +version = "6.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5041cc499144891f3790297212f32a74fb938e5136a14943f338ef9e0ae276cf" +dependencies = [ + "cfg-if", + "crossbeam-utils", + "hashbrown 0.14.5", + "lock_api", + "once_cell", + "parking_lot_core", +] + +[[package]] +name = "der" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb" +dependencies = [ + "const-oid", + "zeroize", +] + +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "derive_more" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a9b99b9cbbe49445b21764dc0625032a89b145a2642e67603e1c936f5458d05" +dependencies = [ + "derive_more-impl 1.0.0", +] + +[[package]] +name = "derive_more" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "093242cf7570c207c83073cf82f79706fe7b8317e98620a47d5be7c3d8497678" +dependencies = [ + "derive_more-impl 2.0.1", +] + +[[package]] +name = "derive_more-impl" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", + "unicode-xid", +] + +[[package]] +name = "derive_more-impl" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bda628edc44c4bb645fbe0f758797143e4e07926f7ebf4e9bdfbd3d2ce621df3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", + "unicode-xid", +] + +[[package]] +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "const-oid", + "crypto-common", + "subtle", +] + +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "dunce" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" + +[[package]] +name = "ecdsa" +version = "0.16.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" +dependencies = [ + "der", + "digest 0.10.7", + "elliptic-curve", + "rfc6979", + "signature", + "spki", +] + +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + +[[package]] +name = "elliptic-curve" +version = "0.13.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" +dependencies = [ + "base16ct", + "crypto-bigint", + "digest 0.10.7", + "ff", + "generic-array", + "group", + "pkcs8", + "rand_core 0.6.4", + "sec1", + "subtle", + "zeroize", +] + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "errno" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "778e2ac28f6c47af28e4907f13ffd1e1ddbd400980a9abd7c8df189bf578a5ad" +dependencies = [ + "libc", + "windows-sys 0.60.2", +] + +[[package]] +name = "eyre" +version = "0.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cd915d99f24784cdc19fd37ef22b97e3ff0ae756c7e492e9fbfe897d61e2aec" +dependencies = [ + "indenter", + "once_cell", +] + +[[package]] +name = "fastrand" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" + +[[package]] +name = "fastrlp" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "139834ddba373bbdd213dffe02c8d110508dcf1726c2be27e8d1f7d7e1856418" +dependencies = [ + "arrayvec", + "auto_impl", + "bytes", +] + +[[package]] +name = "fastrlp" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce8dba4714ef14b8274c371879b175aa55b16b30f269663f19d576f380018dc4" +dependencies = [ + "arrayvec", + "auto_impl", + "bytes", +] + +[[package]] +name = "ff" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0b50bfb653653f9ca9095b427bed08ab8d75a137839d9ad64eb11810d5b6393" +dependencies = [ + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "fixed-hash" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "835c052cb0c08c1acf6ffd71c022172e18723949c8282f2b9f27efbc51e64534" +dependencies = [ + "byteorder", + "rand 0.8.5", + "rustc-hex", + "static_assertions", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "form_urlencoded" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "funty" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + +[[package]] +name = "futures" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" + +[[package]] +name = "futures-executor" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" + +[[package]] +name = "futures-macro" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "futures-sink" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" + +[[package]] +name = "futures-task" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" + +[[package]] +name = "futures-util" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "futures-utils-wasm" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42012b0f064e01aa58b545fe3727f90f7dd4020f4a3ea735b50344965f5a57e9" + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", + "zeroize", +] + +[[package]] +name = "getrandom" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.11.1+wasi-snapshot-preview1", +] + +[[package]] +name = "getrandom" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasi 0.14.2+wasi-0.2.4", +] + +[[package]] +name = "gimli" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" + +[[package]] +name = "glob" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" + +[[package]] +name = "group" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" +dependencies = [ + "ff", + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "hashbrown" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" + +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +dependencies = [ + "ahash", +] + +[[package]] +name = "hashbrown" +version = "0.15.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5" +dependencies = [ + "allocator-api2", + "equivalent", + "foldhash", + "serde", +] + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hermit-abi" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +dependencies = [ + "serde", +] + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest 0.10.7", +] + +[[package]] +name = "http" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +dependencies = [ + "bytes", + "http", +] + +[[package]] +name = "http-body-util" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" +dependencies = [ + "bytes", + "futures-core", + "http", + "http-body", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" + +[[package]] +name = "hyper" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc2b571658e38e0c01b1fdca3bbbe93c00d3d71693ff2770043f8c29bc7d6f80" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "http", + "http-body", + "httparse", + "itoa", + "pin-project-lite", + "smallvec", + "tokio", + "want", +] + +[[package]] +name = "hyper-tls" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" +dependencies = [ + "bytes", + "http-body-util", + "hyper", + "hyper-util", + "native-tls", + "tokio", + "tokio-native-tls", + "tower-service", +] + +[[package]] +name = "hyper-util" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d9b05277c7e8da2c93a568989bb6207bef0112e8d17df7a6eda4a3cf143bc5e" +dependencies = [ + "base64", + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "http", + "http-body", + "hyper", + "ipnet", + "libc", + "percent-encoding", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", +] + +[[package]] +name = "hyperware_process_lib" +version = "1.2.0" +dependencies = [ + "alloy", + "alloy-primitives", + "alloy-sol-macro", + "alloy-sol-types", + "anyhow", + "base64", + "bincode", + "color-eyre", + "hex", + "http", + "mime_guess", + "rand 0.8.5", + "regex", + "rmp-serde", + "serde", + "serde_json", + "sha3", + "thiserror 1.0.69", + "tracing", + "tracing-error", + "tracing-subscriber", + "url", + "wit-bindgen", +] + +[[package]] +name = "icu_collections" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "200072f5d0e3614556f94a9930d5dc3e0662a652823904c3a75dc3b0af7fee47" +dependencies = [ + "displaydoc", + "potential_utf", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locale_core" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cde2700ccaed3872079a65fb1a78f6c0a36c91570f28755dda67bc8f7d9f00a" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_normalizer" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "436880e8e18df4d7bbc06d58432329d6458cc84531f7ac5f024e93deadb37979" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00210d6893afc98edb752b664b8890f0ef174c8adbb8d0be9710fa66fbbf72d3" + +[[package]] +name = "icu_properties" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "016c619c1eeb94efb86809b015c58f479963de65bdb6253345c1a1276f22e32b" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_locale_core", + "icu_properties_data", + "icu_provider", + "potential_utf", + "zerotrie", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "298459143998310acd25ffe6810ed544932242d3f07083eee1084d83a71bd632" + +[[package]] +name = "icu_provider" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03c80da27b5f4187909049ee2d72f276f0d9f99a42c306bd0131ecfe04d8e5af" +dependencies = [ + "displaydoc", + "icu_locale_core", + "stable_deref_trait", + "tinystr", + "writeable", + "yoke", + "zerofrom", + "zerotrie", + "zerovec", +] + +[[package]] +name = "id-arena" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25a2bc672d1148e28034f176e01fffebb08b35768468cc954630da77a1449005" + +[[package]] +name = "idna" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + +[[package]] +name = "impl-codec" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba6a270039626615617f3f36d15fc827041df3b78c439da2cadfa47455a77f2f" +dependencies = [ + "parity-scale-codec", +] + +[[package]] +name = "impl-trait-for-tuples" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0eb5a3343abf848c0984fe4604b2b105da9539376e24fc0a3b0007411ae4fd9" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "indenter" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" + +[[package]] +name = "indexmap" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe4cd85333e22411419a0bcae1297d25e58c9443848b11dc6a86fefe8c78a661" +dependencies = [ + "equivalent", + "hashbrown 0.15.4", + "serde", +] + +[[package]] +name = "io-uring" +version = "0.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d93587f37623a1a17d94ef2bc9ada592f5465fe7732084ab7beefabe5c77c0c4" +dependencies = [ + "bitflags", + "cfg-if", + "libc", +] + +[[package]] +name = "ipnet" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" + +[[package]] +name = "iri-string" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbc5ebe9c3a1a7a5127f920a418f7585e9e758e911d0466ed004f393b0e380b2" +dependencies = [ + "memchr", + "serde", +] + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" + +[[package]] +name = "js-sys" +version = "0.3.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + +[[package]] +name = "k256" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6e3919bbaa2945715f0bb6d3934a173d1e9a59ac23767fbaaef277265a7411b" +dependencies = [ + "cfg-if", + "ecdsa", + "elliptic-curve", + "once_cell", + "sha2", +] + +[[package]] +name = "keccak" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc2af9a1119c51f12a14607e783cb977bde58bc069ff0c3da1095e635d70654" +dependencies = [ + "cpufeatures", +] + +[[package]] +name = "keccak-asm" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "505d1856a39b200489082f90d897c3f07c455563880bc5952e38eabf731c83b6" +dependencies = [ + "digest 0.10.7", + "sha3-asm", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "leb128" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67" + +[[package]] +name = "libc" +version = "0.2.174" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776" + +[[package]] +name = "libm" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de" + +[[package]] +name = "linux-raw-sys" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" + +[[package]] +name = "litemap" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956" + +[[package]] +name = "lock_api" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96936507f153605bddfcda068dd804796c84324ed2510809e5b2a624c81da765" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" + +[[package]] +name = "lru" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "234cf4f4a04dc1f57e24b96cc0cd600cf2af460d4161ac5ecdd0af8e1f3b2a38" +dependencies = [ + "hashbrown 0.15.4", +] + +[[package]] +name = "macro-string" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b27834086c65ec3f9387b096d66e99f221cf081c2b738042aa252bcd41204e3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "matchers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" +dependencies = [ + "regex-automata 0.1.10", +] + +[[package]] +name = "memchr" +version = "2.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" + +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "mime_guess" +version = "2.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e" +dependencies = [ + "mime", + "unicase", +] + +[[package]] +name = "miniz_oxide" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" +dependencies = [ + "adler2", +] + +[[package]] +name = "mio" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c" +dependencies = [ + "libc", + "wasi 0.11.1+wasi-snapshot-preview1", + "windows-sys 0.59.0", +] + +[[package]] +name = "native-tls" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87de3442987e9dbec73158d5c715e7ad9072fda936bb03d19d7fa10e00520f0e" +dependencies = [ + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + +[[package]] +name = "nu-ansi-term" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +dependencies = [ + "overload", + "winapi", +] + +[[package]] +name = "num-bigint" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +dependencies = [ + "num-integer", + "num-traits", +] + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", + "libm", +] + +[[package]] +name = "num_cpus" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91df4bbde75afed763b708b7eee1e8e7651e02d97f6d5dd763e89367e957b23b" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "num_enum" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a973b4e44ce6cad84ce69d797acf9a044532e4184c4f267913d1b546a0727b7a" +dependencies = [ + "num_enum_derive", + "rustversion", +] + +[[package]] +name = "num_enum_derive" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77e878c846a8abae00dd069496dbe8751b16ac1c3d6bd2a7283a938e8228f90d" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "nybbles" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8983bb634df7248924ee0c4c3a749609b5abcb082c28fffe3254b3eb3602b307" +dependencies = [ + "alloy-rlp", + "const-hex", + "proptest", + "serde", + "smallvec", +] + +[[package]] +name = "object" +version = "0.36.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + +[[package]] +name = "openssl" +version = "0.10.73" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8505734d46c8ab1e19a1dce3aef597ad87dcb4c37e7188231769bd6bd51cebf8" +dependencies = [ + "bitflags", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "openssl-probe" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" + +[[package]] +name = "openssl-sys" +version = "0.9.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90096e2e47630d78b7d1c20952dc621f957103f8bc2c8359ec81290d75238571" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "overload" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" + +[[package]] +name = "owo-colors" +version = "4.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48dd4f4a2c8405440fd0462561f0e5806bd0f77e86f51c761481bdd4018b545e" + +[[package]] +name = "parity-scale-codec" +version = "3.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "799781ae679d79a948e13d4824a40970bfa500058d245760dd857301059810fa" +dependencies = [ + "arrayvec", + "bitvec", + "byte-slice-cast", + "const_format", + "impl-trait-for-tuples", + "parity-scale-codec-derive", + "rustversion", + "serde", +] + +[[package]] +name = "parity-scale-codec-derive" +version = "3.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34b4653168b563151153c9e4c08ebed57fb8262bebfa79711552fa983c623e7a" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "parking_lot" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70d58bf43669b5795d1576d0641cfb6fbb2057bf629506267a92807158584a13" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets 0.52.6", +] + +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + +[[package]] +name = "pest" +version = "2.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1db05f56d34358a8b1066f67cbb203ee3e7ed2ba674a6263a1d5ec6db2204323" +dependencies = [ + "memchr", + "thiserror 2.0.12", + "ucd-trie", +] + +[[package]] +name = "pin-project" +version = "1.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677f1add503faace112b9f1373e43e9e054bfdd22ff1a63c1bc485eaec6a6a8a" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der", + "spki", +] + +[[package]] +name = "pkg-config" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" + +[[package]] +name = "potential_utf" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5a7c30837279ca13e7c867e9e40053bc68740f988cb07f7ca6df43cc734b585" +dependencies = [ + "zerovec", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "prettyplease" +version = "0.2.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff24dfcda44452b9816fff4cd4227e1bb73ff5a2f1bc1105aa92fb8565ce44d2" +dependencies = [ + "proc-macro2", + "syn 2.0.104", +] + +[[package]] +name = "primitive-types" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b34d9fd68ae0b74a41b21c03c2f62847aa0ffea044eee893b4c140b37e244e2" +dependencies = [ + "fixed-hash", + "impl-codec", + "uint", +] + +[[package]] +name = "proc-macro-crate" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edce586971a4dfaa28950c6f18ed55e0406c1ab88bbce2c6f6293a7aaba73d35" +dependencies = [ + "toml_edit", +] + +[[package]] +name = "proc-macro-error-attr2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5" +dependencies = [ + "proc-macro2", + "quote", +] + +[[package]] +name = "proc-macro-error2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802" +dependencies = [ + "proc-macro-error-attr2", + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "proc-macro2" +version = "1.0.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "proptest" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fcdab19deb5195a31cf7726a210015ff1496ba1464fd42cb4f537b8b01b471f" +dependencies = [ + "bit-set", + "bit-vec", + "bitflags", + "lazy_static", + "num-traits", + "rand 0.9.2", + "rand_chacha 0.9.0", + "rand_xorshift", + "regex-syntax 0.8.5", + "rusty-fork", + "tempfile", + "unarray", +] + +[[package]] +name = "quick-error" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" + +[[package]] +name = "quote" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + +[[package]] +name = "radium" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha 0.3.1", + "rand_core 0.6.4", + "serde", +] + +[[package]] +name = "rand" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" +dependencies = [ + "rand_chacha 0.9.0", + "rand_core 0.9.3", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core 0.9.3", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.16", +] + +[[package]] +name = "rand_core" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" +dependencies = [ + "getrandom 0.3.3", +] + +[[package]] +name = "rand_xorshift" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "513962919efc330f829edb2535844d1b912b0fbe2ca165d613e4e8788bb05a5a" +dependencies = [ + "rand_core 0.9.3", +] + +[[package]] +name = "redox_syscall" +version = "0.5.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5407465600fb0548f1442edf71dd20683c6ed326200ace4b1ef0763521bb3b77" +dependencies = [ + "bitflags", +] + +[[package]] +name = "regex" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata 0.4.9", + "regex-syntax 0.8.5", +] + +[[package]] +name = "regex-automata" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" +dependencies = [ + "regex-syntax 0.6.29", +] + +[[package]] +name = "regex-automata" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax 0.8.5", +] + +[[package]] +name = "regex-syntax" +version = "0.6.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" + +[[package]] +name = "regex-syntax" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" + +[[package]] +name = "reqwest" +version = "0.12.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbc931937e6ca3a06e3b6c0aa7841849b160a90351d6ab467a8b9b9959767531" +dependencies = [ + "base64", + "bytes", + "futures-core", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-tls", + "hyper-util", + "js-sys", + "log", + "native-tls", + "percent-encoding", + "pin-project-lite", + "rustls-pki-types", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper", + "tokio", + "tokio-native-tls", + "tower", + "tower-http", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "rfc6979" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" +dependencies = [ + "hmac", + "subtle", +] + +[[package]] +name = "rlp" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb919243f34364b6bd2fc10ef797edbfa75f33c252e7998527479c6d6b47e1ec" +dependencies = [ + "bytes", + "rustc-hex", +] + +[[package]] +name = "rmp" +version = "0.8.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "228ed7c16fa39782c3b3468e974aec2795e9089153cd08ee2e9aefb3613334c4" +dependencies = [ + "byteorder", + "num-traits", + "paste", +] + +[[package]] +name = "rmp-serde" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52e599a477cf9840e92f2cde9a7189e67b42c57532749bf90aea6ec10facd4db" +dependencies = [ + "byteorder", + "rmp", + "serde", +] + +[[package]] +name = "ruint" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ecb38f82477f20c5c3d62ef52d7c4e536e38ea9b73fb570a20c5cae0e14bcf6" +dependencies = [ + "alloy-rlp", + "ark-ff 0.3.0", + "ark-ff 0.4.2", + "bytes", + "fastrlp 0.3.1", + "fastrlp 0.4.0", + "num-bigint", + "num-integer", + "num-traits", + "parity-scale-codec", + "primitive-types", + "proptest", + "rand 0.8.5", + "rand 0.9.2", + "rlp", + "ruint-macro", + "serde", + "valuable", + "zeroize", +] + +[[package]] +name = "ruint-macro" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48fd7bd8a6377e15ad9d42a8ec25371b94ddc67abe7c8b9127bec79bebaaae18" + +[[package]] +name = "rustc-demangle" +version = "0.1.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace" + +[[package]] +name = "rustc-hash" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" + +[[package]] +name = "rustc-hex" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" + +[[package]] +name = "rustc_version" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0dfe2087c51c460008730de8b57e6a320782fbfb312e1f4d520e6c6fae155ee" +dependencies = [ + "semver 0.11.0", +] + +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver 1.0.26", +] + +[[package]] +name = "rustix" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11181fbabf243db407ef8df94a6ce0b2f9a733bd8be4ad02b4eda9602296cac8" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.60.2", +] + +[[package]] +name = "rustls-pki-types" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "229a4a4c221013e7e1f1a043678c5cc39fe5171437c88fb47151a21e6f5b5c79" +dependencies = [ + "zeroize", +] + +[[package]] +name = "rustversion" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d" + +[[package]] +name = "rusty-fork" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb3dcc6e454c328bb824492db107ab7c0ae8fcffe4ad210136ef014458c1bc4f" +dependencies = [ + "fnv", + "quick-error", + "tempfile", + "wait-timeout", +] + +[[package]] +name = "ryu" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" + +[[package]] +name = "schannel" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f29ebaa345f945cec9fbbc532eb307f0fdad8161f281b6369539c8d84876b3d" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "schnellru" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "356285bbf17bea63d9e52e96bd18f039672ac92b55b8cb997d6162a2a37d1649" +dependencies = [ + "ahash", + "cfg-if", + "hashbrown 0.13.2", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "sec1" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" +dependencies = [ + "base16ct", + "der", + "generic-array", + "pkcs8", + "subtle", + "zeroize", +] + +[[package]] +name = "security-framework" +version = "2.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" +dependencies = [ + "bitflags", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49db231d56a190491cb4aeda9527f1ad45345af50b0851622a7adb8c03b01c32" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "semver" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6" +dependencies = [ + "semver-parser", +] + +[[package]] +name = "semver" +version = "1.0.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0" + +[[package]] +name = "semver-parser" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9900206b54a3527fdc7b8a938bffd94a568bac4f4aa8113b209df75a09c0dec2" +dependencies = [ + "pest", +] + +[[package]] +name = "serde" +version = "1.0.219" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.219" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "serde_json" +version = "1.0.142" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "030fedb782600dcbd6f02d479bf0d817ac3bb40d644745b769d6a96bc3afc5a7" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "sha2" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest 0.10.7", +] + +[[package]] +name = "sha3" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" +dependencies = [ + "digest 0.10.7", + "keccak", +] + +[[package]] +name = "sha3-asm" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c28efc5e327c837aa837c59eae585fc250715ef939ac32881bcc11677cd02d46" +dependencies = [ + "cc", + "cfg-if", +] + +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "signature" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +dependencies = [ + "digest 0.10.7", + "rand_core 0.6.4", +] + +[[package]] +name = "slab" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04dc19736151f35336d325007ac991178d504a119863a2fcb3758cdb5e52c50d" + +[[package]] +name = "smallvec" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" +dependencies = [ + "serde", +] + +[[package]] +name = "socket2" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "233504af464074f9d066d7b5416c5f9b894a5862a6506e306f7b816cdd6f1807" +dependencies = [ + "libc", + "windows-sys 0.59.0", +] + +[[package]] +name = "spdx" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3e17e880bafaeb362a7b751ec46bdc5b61445a188f80e0606e68167cd540fa3" +dependencies = [ + "smallvec", +] + +[[package]] +name = "spki" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" +dependencies = [ + "base64ct", + "der", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "strum" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af23d6f6c1a224baef9d3f61e287d2761385a5b88fdab4eb4c6f11aeb54c4bcf" +dependencies = [ + "strum_macros", +] + +[[package]] +name = "strum_macros" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7695ce3845ea4b33927c055a39dc438a45b059f7c1b3d91d38d10355fb8cbca7" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.104" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn-solidity" +version = "0.8.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4560533fbd6914b94a8fb5cc803ed6801c3455668db3b810702c57612bac9412" +dependencies = [ + "paste", + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "sync_wrapper" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" +dependencies = [ + "futures-core", +] + +[[package]] +name = "synstructure" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + +[[package]] +name = "tempfile" +version = "3.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8a64e3985349f2441a1a9ef0b853f869006c3855f2cda6862a94d26ebb9d6a1" +dependencies = [ + "fastrand", + "getrandom 0.3.3", + "once_cell", + "rustix", + "windows-sys 0.59.0", +] + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl 1.0.69", +] + +[[package]] +name = "thiserror" +version = "2.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" +dependencies = [ + "thiserror-impl 2.0.12", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "thread_local" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "threadpool" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d050e60b33d41c19108b32cea32164033a9013fe3b46cbd4457559bfbf77afaa" +dependencies = [ + "num_cpus", +] + +[[package]] +name = "tiny-keccak" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" +dependencies = [ + "crunchy", +] + +[[package]] +name = "tinystr" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d4f6d1145dcb577acf783d4e601bc1d76a13337bb54e6233add580b07344c8b" +dependencies = [ + "displaydoc", + "zerovec", +] + +[[package]] +name = "tokio" +version = "1.47.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89e49afdadebb872d3145a5638b59eb0691ea23e46ca484037cfab3b76b95038" +dependencies = [ + "backtrace", + "bytes", + "io-uring", + "libc", + "mio", + "pin-project-lite", + "slab", + "socket2", + "tokio-macros", + "windows-sys 0.59.0", +] + +[[package]] +name = "tokio-macros" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "tokio-native-tls" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +dependencies = [ + "native-tls", + "tokio", +] + +[[package]] +name = "tokio-stream" +version = "0.1.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eca58d7bba4a75707817a2c44174253f9236b2d5fbd055602e9d5c07c139a047" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", + "tokio-util", +] + +[[package]] +name = "tokio-util" +version = "0.7.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14307c986784f72ef81c89db7d9e28d6ac26d16213b109ea501696195e6e3ce5" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "toml_datetime" +version = "0.6.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" + +[[package]] +name = "toml_edit" +version = "0.22.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" +dependencies = [ + "indexmap", + "toml_datetime", + "winnow", +] + +[[package]] +name = "tower" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" +dependencies = [ + "futures-core", + "futures-util", + "pin-project-lite", + "sync_wrapper", + "tokio", + "tower-layer", + "tower-service", +] + +[[package]] +name = "tower-http" +version = "0.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adc82fd73de2a9722ac5da747f12383d2bfdb93591ee6c58486e0097890f05f2" +dependencies = [ + "bitflags", + "bytes", + "futures-util", + "http", + "http-body", + "iri-string", + "pin-project-lite", + "tower", + "tower-layer", + "tower-service", +] + +[[package]] +name = "tower-layer" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" + +[[package]] +name = "tower-service" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" + +[[package]] +name = "tracing" +version = "0.1.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" +dependencies = [ + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "tracing-core" +version = "0.1.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-error" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b1581020d7a273442f5b45074a6a57d5757ad0a47dac0e9f0bd57b81936f3db" +dependencies = [ + "tracing", + "tracing-subscriber", +] + +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + +[[package]] +name = "tracing-serde" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "704b1aeb7be0d0a84fc9828cae51dab5970fee5088f83d1dd7ee6f6246fc6ff1" +dependencies = [ + "serde", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008" +dependencies = [ + "matchers", + "nu-ansi-term", + "once_cell", + "regex", + "serde", + "serde_json", + "sharded-slab", + "smallvec", + "thread_local", + "tracing", + "tracing-core", + "tracing-log", + "tracing-serde", +] + +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + +[[package]] +name = "typenum" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" + +[[package]] +name = "ucd-trie" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971" + +[[package]] +name = "uint" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76f64bba2c53b04fcab63c01a7d7427eadc821e3bc48c34dc9ba29c501164b52" +dependencies = [ + "byteorder", + "crunchy", + "hex", + "static_assertions", +] + +[[package]] +name = "unarray" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" + +[[package]] +name = "unicase" +version = "2.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75b844d17643ee918803943289730bec8aac480150456169e647ed0b576ba539" + +[[package]] +name = "unicode-ident" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" + +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + +[[package]] +name = "url" +version = "2.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + +[[package]] +name = "valuable" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "wait-timeout" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ac3b126d3914f9849036f826e054cbabdc8519970b8998ddaf3b5bd3c65f11" +dependencies = [ + "libc", +] + +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "wasi" +version = "0.14.2+wasi-0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" +dependencies = [ + "wit-bindgen-rt 0.39.0", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" +dependencies = [ + "bumpalo", + "log", + "proc-macro2", + "quote", + "syn 2.0.104", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61" +dependencies = [ + "cfg-if", + "js-sys", + "once_cell", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "wasm-encoder" +version = "0.220.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e913f9242315ca39eff82aee0e19ee7a372155717ff0eb082c741e435ce25ed1" +dependencies = [ + "leb128", + "wasmparser", +] + +[[package]] +name = "wasm-metadata" +version = "0.220.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "185dfcd27fa5db2e6a23906b54c28199935f71d9a27a1a27b3a88d6fee2afae7" +dependencies = [ + "anyhow", + "indexmap", + "serde", + "serde_derive", + "serde_json", + "spdx", + "wasm-encoder", + "wasmparser", +] + +[[package]] +name = "wasmparser" +version = "0.220.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d07b6a3b550fefa1a914b6d54fc175dd11c3392da11eee604e6ffc759805d25" +dependencies = [ + "ahash", + "bitflags", + "hashbrown 0.14.5", + "indexmap", + "semver 1.0.26", +] + +[[package]] +name = "wasmtimer" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8d49b5d6c64e8558d9b1b065014426f35c18de636895d24893dbbd329743446" +dependencies = [ + "futures", + "js-sys", + "parking_lot", + "pin-utils", + "slab", + "wasm-bindgen", +] + +[[package]] +name = "web-sys" +version = "0.3.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-link" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" +dependencies = [ + "windows-targets 0.53.3", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm 0.52.6", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.53.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5fe6031c4041849d7c496a8ded650796e7b6ecc19df1a431c1a363342e5dc91" +dependencies = [ + "windows-link", + "windows_aarch64_gnullvm 0.53.0", + "windows_aarch64_msvc 0.53.0", + "windows_i686_gnu 0.53.0", + "windows_i686_gnullvm 0.53.0", + "windows_i686_msvc 0.53.0", + "windows_x86_64_gnu 0.53.0", + "windows_x86_64_gnullvm 0.53.0", + "windows_x86_64_msvc 0.53.0", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_i686_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" + +[[package]] +name = "winnow" +version = "0.7.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3edebf492c8125044983378ecb5766203ad3b4c2f7a922bd7dd207f6d443e95" +dependencies = [ + "memchr", +] + +[[package]] +name = "wit-bindgen" +version = "0.36.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a2b3e15cd6068f233926e7d8c7c588b2ec4fb7cc7bf3824115e7c7e2a8485a3" +dependencies = [ + "wit-bindgen-rt 0.36.0", + "wit-bindgen-rust-macro", +] + +[[package]] +name = "wit-bindgen-core" +version = "0.36.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b632a5a0fa2409489bd49c9e6d99fcc61bb3d4ce9d1907d44662e75a28c71172" +dependencies = [ + "anyhow", + "heck", + "wit-parser", +] + +[[package]] +name = "wit-bindgen-rt" +version = "0.36.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7947d0131c7c9da3f01dfde0ab8bd4c4cf3c5bd49b6dba0ae640f1fa752572ea" +dependencies = [ + "bitflags", +] + +[[package]] +name = "wit-bindgen-rt" +version = "0.39.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" +dependencies = [ + "bitflags", +] + +[[package]] +name = "wit-bindgen-rust" +version = "0.36.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4329de4186ee30e2ef30a0533f9b3c123c019a237a7c82d692807bf1b3ee2697" +dependencies = [ + "anyhow", + "heck", + "indexmap", + "prettyplease", + "syn 2.0.104", + "wasm-metadata", + "wit-bindgen-core", + "wit-component", +] + +[[package]] +name = "wit-bindgen-rust-macro" +version = "0.36.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "177fb7ee1484d113b4792cc480b1ba57664bbc951b42a4beebe573502135b1fc" +dependencies = [ + "anyhow", + "prettyplease", + "proc-macro2", + "quote", + "syn 2.0.104", + "wit-bindgen-core", + "wit-bindgen-rust", +] + +[[package]] +name = "wit-component" +version = "0.220.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b505603761ed400c90ed30261f44a768317348e49f1864e82ecdc3b2744e5627" +dependencies = [ + "anyhow", + "bitflags", + "indexmap", + "log", + "serde", + "serde_derive", + "serde_json", + "wasm-encoder", + "wasm-metadata", + "wasmparser", + "wit-parser", +] + +[[package]] +name = "wit-parser" +version = "0.220.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae2a7999ed18efe59be8de2db9cb2b7f84d88b27818c79353dfc53131840fe1a" +dependencies = [ + "anyhow", + "id-arena", + "indexmap", + "log", + "semver 1.0.26", + "serde", + "serde_derive", + "serde_json", + "unicode-xid", + "wasmparser", +] + +[[package]] +name = "writeable" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb" + +[[package]] +name = "wyz" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" +dependencies = [ + "tap", +] + +[[package]] +name = "yoke" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f41bb01b8226ef4bfd589436a297c53d118f65921786300e427be8d487695cc" +dependencies = [ + "serde", + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", + "synstructure", +] + +[[package]] +name = "zerocopy" +version = "0.8.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1039dd0d3c310cf05de012d8a39ff557cb0d23087fd44cad61df08fc31907a2f" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "zerofrom" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", + "synstructure", +] + +[[package]] +name = "zeroize" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "zerotrie" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36f0bbd478583f79edad978b407914f61b2972f5af6fa089686016be8f9af595" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", +] + +[[package]] +name = "zerovec" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7aa2bd55086f1ab526693ecbe444205da57e25f4489879da80635a46d90e73b" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", +] diff --git a/src/hyperwallet_client/types.rs b/src/hyperwallet_client/types.rs index f18925c..d9754c6 100644 --- a/src/hyperwallet_client/types.rs +++ b/src/hyperwallet_client/types.rs @@ -472,8 +472,9 @@ impl Default for PaymasterConfig { Self { is_circle_paymaster: true, paymaster_address: "0x0578cFB241215b77442a541325d6A4E6dFE700Ec".to_string(), // Base Circle paymaster - paymaster_verification_gas: "0x7a120".to_string(), // 500000 - paymaster_post_op_gas: "0x493e0".to_string(), // 300000 + //paymaster_address: "0x861a1Be40c595db980341e41A7a5D09C772f7c2b".to_string(), // Base Hyperware paymaster + paymaster_verification_gas: "0x7a120".to_string(), + paymaster_post_op_gas: "0x493e0".to_string(), } } } From 1b26075977d16bbe8561d7ce8fba3af4e9efed74 Mon Sep 17 00:00:00 2001 From: Hallmane Date: Thu, 7 Aug 2025 00:19:06 +0200 Subject: [PATCH 92/96] :zyzz --- src/hyperwallet_client/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hyperwallet_client/mod.rs b/src/hyperwallet_client/mod.rs index 43a3ff2..9374ae0 100644 --- a/src/hyperwallet_client/mod.rs +++ b/src/hyperwallet_client/mod.rs @@ -141,7 +141,7 @@ pub(crate) fn send_message( message: types::HyperwalletMessage, ) -> 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()))? From fb37552af48d5708602171733ddd9ec74b12c215 Mon Sep 17 00:00:00 2001 From: Hallmane Date: Fri, 8 Aug 2025 00:49:44 +0200 Subject: [PATCH 93/96] fixed request/response serde (without diff.diff) --- src/hyperwallet_client/mod.rs | 5 +- src/hyperwallet_client/serde_impls.rs | 14 +- .../serde_request_response_impls.rs | 130 -- .../serde_response_impls.rs | 1959 +++++++++++++++++ src/hyperwallet_client/serde_variant_impls.rs | 186 +- 5 files changed, 2154 insertions(+), 140 deletions(-) delete mode 100644 src/hyperwallet_client/serde_request_response_impls.rs create mode 100644 src/hyperwallet_client/serde_response_impls.rs diff --git a/src/hyperwallet_client/mod.rs b/src/hyperwallet_client/mod.rs index 9374ae0..04a2223 100644 --- a/src/hyperwallet_client/mod.rs +++ b/src/hyperwallet_client/mod.rs @@ -1,9 +1,10 @@ +use crate::println as kiprintln; use crate::Request; use thiserror::Error; pub mod api; pub mod serde_impls; -mod serde_request_response_impls; +mod serde_response_impls; // Proper implementations replacing broken stubs mod serde_variant_impls; pub mod types; pub use types::{ @@ -147,6 +148,8 @@ pub(crate) fn send_message( .map_err(|e| HyperwalletClientError::Communication(e.into()))? .map_err(|e| HyperwalletClientError::Communication(e.into()))?; + kiprintln!("Response: {:?}", response); + let hyperwallet_response: types::HyperwalletResponse = serde_json::from_slice(response.body()).map_err(HyperwalletClientError::Deserialization)?; diff --git a/src/hyperwallet_client/serde_impls.rs b/src/hyperwallet_client/serde_impls.rs index c56e71a..c140b6d 100644 --- a/src/hyperwallet_client/serde_impls.rs +++ b/src/hyperwallet_client/serde_impls.rs @@ -142,19 +142,19 @@ impl<'a> Deserialize<'a> for wit::HyperwalletResponse { if data.is_some() { return Err(de::Error::duplicate_field("data")); } - data = Some(map.next_value()?); + data = map.next_value()?; } Field::Error => { if error.is_some() { return Err(de::Error::duplicate_field("error")); } - error = Some(map.next_value()?); + error = map.next_value()?; } Field::RequestId => { if request_id.is_some() { return Err(de::Error::duplicate_field("request_id")); } - request_id = Some(map.next_value()?); + request_id = map.next_value()?; } } } @@ -338,9 +338,13 @@ impl Serialize for wit::CreateWalletRequest { where S: serde::ser::Serializer, { - let mut state = serializer.serialize_struct("CreateWalletRequest", 2)?; + // 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)?; - state.serialize_field("password", &self.password)?; + if let Some(ref pwd) = self.password { + state.serialize_field("password", pwd)?; + } state.end() } } diff --git a/src/hyperwallet_client/serde_request_response_impls.rs b/src/hyperwallet_client/serde_request_response_impls.rs deleted file mode 100644 index 0cb407a..0000000 --- a/src/hyperwallet_client/serde_request_response_impls.rs +++ /dev/null @@ -1,130 +0,0 @@ -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>, - { - let _val = serde_json::Value::deserialize(deserializer)?; - Err(de::Error::custom(concat!($name, " deserialization not yet implemented"))) - } - } - }; -} - -// 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..8eddf26 --- /dev/null +++ b/src/hyperwallet_client/serde_response_impls.rs @@ -0,0 +1,1959 @@ +// Proper serde implementations for request/response types + +use crate::hyperware::process::hyperwallet as wit; +use serde::de::{self, MapAccess, Visitor}; +use serde::ser::SerializeStruct; +use serde::{Deserialize, Serialize}; + +// ============== REQUEST TYPES ============== + +// ImportWalletRequest +impl Serialize for wit::ImportWalletRequest { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let mut state = serializer.serialize_struct("ImportWalletRequest", 3)?; + state.serialize_field("name", &self.name)?; + state.serialize_field("private_key", &self.private_key)?; + state.serialize_field("password", &self.password)?; + 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 mut state = serializer.serialize_struct("ExportWalletRequest", 2)?; + state.serialize_field("wallet_id", &self.wallet_id)?; + state.serialize_field("password", &self.password)?; + 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 ============== + +// 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 index 81f5e4f..e68852c 100644 --- a/src/hyperwallet_client/serde_variant_impls.rs +++ b/src/hyperwallet_client/serde_variant_impls.rs @@ -252,9 +252,83 @@ impl<'a> Deserialize<'a> for wit::HyperwalletRequest { 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)) + } + "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)) + } + "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)) + } _ => { - // For unimplemented variants, return an error - Err(de::Error::unknown_variant(&variant_type, &[])) + // For unimplemented variants, return an error with helpful message + Err(de::Error::unknown_variant( + &variant_type, + &[ + "Handshake", + "UnlockWallet", + "CreateWallet", + "ListWallets", + "GetWalletInfo", + "GetBalance", + "GetTokenBalance", + "BuildAndSignUserOperationForPayment", + "SubmitUserOperation", + "GetUserOperationReceipt", + ], + )) } } } @@ -409,9 +483,113 @@ impl<'a> Deserialize<'a> for wit::HyperwalletResponseData { })?; Ok(Handshake(step)) } + "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)) + } + "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)) + } _ => { - // For unimplemented variants, return an error - Err(de::Error::unknown_variant(&variant_type, &[])) + // For unimplemented variants, return an error with helpful message + Err(de::Error::unknown_variant( + &variant_type, + &[ + "Handshake", + "ListWallets", + "GetWalletInfo", + "CreateWallet", + "UnlockWallet", + "BuildAndSignUserOperationForPayment", + "SubmitUserOperation", + "GetUserOperationReceipt", + "GetBalance", + "GetTokenBalance", + ], + )) } } } From a75f550c64ebca42426ee215558f4c9ab052d852 Mon Sep 17 00:00:00 2001 From: Hallmane Date: Fri, 8 Aug 2025 16:20:27 +0200 Subject: [PATCH 94/96] fixed a bunch of ser-and-deserialization errors in requests and responses --- src/hyperwallet_client/mod.rs | 45 +- src/hyperwallet_client/serde_impls.rs | 3 +- .../serde_response_impls.rs | 16 +- src/hyperwallet_client/serde_variant_impls.rs | 387 ++++++++++++++++++ src/hyperwallet_client/types.rs | 18 + 5 files changed, 448 insertions(+), 21 deletions(-) diff --git a/src/hyperwallet_client/mod.rs b/src/hyperwallet_client/mod.rs index 04a2223..a577686 100644 --- a/src/hyperwallet_client/mod.rs +++ b/src/hyperwallet_client/mod.rs @@ -10,14 +10,14 @@ 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, HandshakeStep, HyperwalletMessage, HyperwalletRequest, HyperwalletResponse, HyperwalletResponseData, ImportWalletRequest, ImportWalletResponse, - ListWalletsResponse, Operation, OperationCategory, OperationError, PaymasterConfig, - ProcessAddress, ProcessPermissions, RenameWalletRequest, SendEthRequest, SendEthResponse, - SendTokenRequest, SendTokenResponse, SessionId, SessionInfo, SpendingLimits, + ListWalletsResponse, MessageType, Operation, OperationCategory, OperationError, + PaymasterConfig, ProcessAddress, ProcessPermissions, RenameWalletRequest, SendEthRequest, + SendEthResponse, SendTokenRequest, SendTokenResponse, SessionId, SessionInfo, SpendingLimits, SubmitUserOperationResponse, TxReceipt, UnlockWalletResponse, UpdatableSetting, UserOperationHash, UserOperationReceiptResponse, WalletAddress, }; @@ -47,10 +47,11 @@ 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(types::ClientHello { - client_version: "0.1.0".to_string(), + client_version: CLIENT_PROTOCOL_VERSION.to_string(), client_name, }); let hello_message = types::HyperwalletMessage { @@ -71,8 +72,11 @@ pub fn initialize(config: HandshakeConfig) -> Result server_welcome.supported_operations, + 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( @@ -82,6 +86,19 @@ pub fn initialize(config: HandshakeConfig) -> Result Result { - Ok(types::SessionInfo { - server_version: "0.1.0".to_string(), //lol, server should send it's version - session_id: complete_handshake.session_id, - registered_permissions: complete_handshake.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", diff --git a/src/hyperwallet_client/serde_impls.rs b/src/hyperwallet_client/serde_impls.rs index c140b6d..83b4111 100644 --- a/src/hyperwallet_client/serde_impls.rs +++ b/src/hyperwallet_client/serde_impls.rs @@ -240,7 +240,8 @@ impl<'a> Deserialize<'a> for wit::OperationError { if details.is_some() { return Err(de::Error::duplicate_field("details")); } - details = Some(map.next_value()?); + // Accept Option: null -> None + details = map.next_value()?; } } } diff --git a/src/hyperwallet_client/serde_response_impls.rs b/src/hyperwallet_client/serde_response_impls.rs index 8eddf26..08dbb69 100644 --- a/src/hyperwallet_client/serde_response_impls.rs +++ b/src/hyperwallet_client/serde_response_impls.rs @@ -1,7 +1,7 @@ // Proper serde implementations for request/response types use crate::hyperware::process::hyperwallet as wit; -use serde::de::{self, MapAccess, Visitor}; +// no direct use of serde::de; rely on derives and serde_json where necessary use serde::ser::SerializeStruct; use serde::{Deserialize, Serialize}; @@ -13,10 +13,13 @@ impl Serialize for wit::ImportWalletRequest { where S: serde::Serializer, { - let mut state = serializer.serialize_struct("ImportWalletRequest", 3)?; + 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)?; - state.serialize_field("password", &self.password)?; + if let Some(ref pwd) = self.password { + state.serialize_field("password", pwd)?; + } state.end() } } @@ -106,9 +109,12 @@ impl Serialize for wit::ExportWalletRequest { where S: serde::Serializer, { - let mut state = serializer.serialize_struct("ExportWalletRequest", 2)?; + 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)?; - state.serialize_field("password", &self.password)?; + if let Some(ref pwd) = self.password { + state.serialize_field("password", pwd)?; + } state.end() } } diff --git a/src/hyperwallet_client/serde_variant_impls.rs b/src/hyperwallet_client/serde_variant_impls.rs index e68852c..de1a8fa 100644 --- a/src/hyperwallet_client/serde_variant_impls.rs +++ b/src/hyperwallet_client/serde_variant_impls.rs @@ -231,6 +231,46 @@ impl<'a> Deserialize<'a> for wit::HyperwalletRequest { })?; 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| { @@ -262,6 +302,36 @@ impl<'a> Deserialize<'a> for wit::HyperwalletRequest { })?; 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| { @@ -282,6 +352,67 @@ impl<'a> Deserialize<'a> for wit::HyperwalletRequest { })?; 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| { @@ -312,21 +443,189 @@ impl<'a> Deserialize<'a> for wit::HyperwalletRequest { })?; Ok(GetUserOperationReceipt(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", ], )) } @@ -483,6 +782,36 @@ impl<'a> Deserialize<'a> for wit::HyperwalletResponseData { })?; 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| { @@ -523,6 +852,26 @@ impl<'a> Deserialize<'a> for wit::HyperwalletResponseData { })?; 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| { @@ -573,21 +922,59 @@ impl<'a> Deserialize<'a> for wit::HyperwalletResponseData { })?; Ok(GetTokenBalance(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", ], )) } diff --git a/src/hyperwallet_client/types.rs b/src/hyperwallet_client/types.rs index d2aeb2e..7d4dc04 100644 --- a/src/hyperwallet_client/types.rs +++ b/src/hyperwallet_client/types.rs @@ -22,6 +22,9 @@ pub use wit::{ Operation, OperationCategory, }; +// Additional WIT types needed by clients +pub use wit::Eip712Data; + // Implement Default for PaymasterConfig impl Default for wit::PaymasterConfig { fn default() -> Self { @@ -67,6 +70,21 @@ pub struct SessionInfo { pub initial_chain_id: ChainId, } +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) + } + + /// 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 { From eaa9799bd9bed2de4dc49fe6188cb2f2e12dde3d Mon Sep 17 00:00:00 2001 From: Hallmane Date: Sat, 9 Aug 2025 05:28:01 +0200 Subject: [PATCH 95/96] spending limits added to wit --- hyperware-wit/hyperwallet:sys-v0.wit | 15 +++ src/hyperwallet_client/api.rs | 26 +++- .../serde_request_response_impls.rs | 127 ++++++++++++++++++ .../serde_response_impls.rs | 63 +++++++++ src/hyperwallet_client/serde_variant_impls.rs | 28 ++++ src/hyperwallet_client/types.rs | 14 +- 6 files changed, 267 insertions(+), 6 deletions(-) create mode 100644 src/hyperwallet_client/serde_request_response_impls.rs diff --git a/hyperware-wit/hyperwallet:sys-v0.wit b/hyperware-wit/hyperwallet:sys-v0.wit index a65942e..e5f5048 100644 --- a/hyperware-wit/hyperwallet:sys-v0.wit +++ b/hyperware-wit/hyperwallet:sys-v0.wit @@ -165,6 +165,7 @@ interface hyperwallet { 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), @@ -256,6 +257,7 @@ interface hyperwallet { 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), @@ -311,6 +313,12 @@ interface hyperwallet { 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, } @@ -541,6 +549,13 @@ interface hyperwallet { 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, diff --git a/src/hyperwallet_client/api.rs b/src/hyperwallet_client/api.rs index d92e6e4..0496921 100644 --- a/src/hyperwallet_client/api.rs +++ b/src/hyperwallet_client/api.rs @@ -2,8 +2,8 @@ use super::types::{ self, Balance, BuildAndSignUserOperationForPaymentRequest, BuildAndSignUserOperationResponse, CreateWalletRequest, ExportWalletResponse, GetTokenBalanceResponse, HyperwalletMessage, HyperwalletRequest, ImportWalletRequest, ListWalletsResponse, PaymasterConfig, - RenameWalletRequest, SendEthRequest, SendTokenRequest, SessionId, TxReceipt, - UnlockWalletRequest, UserOperationReceiptResponse, Wallet, + RenameWalletRequest, SendEthRequest, SendTokenRequest, SessionId, SetWalletLimitsRequest, + SetWalletLimitsResponse, TxReceipt, UnlockWalletRequest, UserOperationReceiptResponse, Wallet, }; use super::HyperwalletClientError; use crate::wallet; @@ -206,6 +206,28 @@ pub fn 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 === pub fn send_eth( 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 index 08dbb69..2d4e764 100644 --- a/src/hyperwallet_client/serde_response_impls.rs +++ b/src/hyperwallet_client/serde_response_impls.rs @@ -6,6 +6,36 @@ 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 { @@ -405,6 +435,39 @@ impl<'de> Deserialize<'de> for wit::GetUserOperationReceiptRequest { } // ============== 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 { diff --git a/src/hyperwallet_client/serde_variant_impls.rs b/src/hyperwallet_client/serde_variant_impls.rs index de1a8fa..cca4380 100644 --- a/src/hyperwallet_client/serde_variant_impls.rs +++ b/src/hyperwallet_client/serde_variant_impls.rs @@ -173,6 +173,10 @@ impl Serialize for wit::HyperwalletRequest { state.serialize_field("type", "MintEntry")?; state.serialize_field("data", data)?; } + SetWalletLimits(data) => { + state.serialize_field("type", "SetWalletLimits")?; + state.serialize_field("data", data)?; + } } state.end() @@ -443,6 +447,16 @@ impl<'a> Deserialize<'a> for wit::HyperwalletRequest { })?; 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| { @@ -724,6 +738,10 @@ impl Serialize for wit::HyperwalletResponseData { state.serialize_field("type", "CheckTbaOwnership")?; state.serialize_field("data", data)?; } + SetWalletLimits(data) => { + state.serialize_field("type", "SetWalletLimits")?; + state.serialize_field("data", data)?; + } } state.end() @@ -922,6 +940,16 @@ impl<'a> Deserialize<'a> for wit::HyperwalletResponseData { })?; 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| { diff --git a/src/hyperwallet_client/types.rs b/src/hyperwallet_client/types.rs index 7d4dc04..b2f4119 100644 --- a/src/hyperwallet_client/types.rs +++ b/src/hyperwallet_client/types.rs @@ -44,14 +44,19 @@ pub use wit::{ CheckTbaOwnershipRequest, ConfigurePaymasterRequest, CreateNoteRequest, EstimateGasRequest, EstimateUserOperationGasRequest, ExecuteViaTbaRequest, GetTransactionHistoryRequest, GetTransactionReceiptRequest, GetUserOperationReceiptRequest, MintEntryRequest, - ReadNoteRequest, ResolveIdentityRequest, SetupDelegationRequest, SetupTbaDelegationRequest, - SignMessageRequest, SignTransactionRequest, SignUserOperationRequest, - SubmitUserOperationRequest, UpdateSpendingLimitsRequest, VerifyDelegationRequest, + 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, SubmitUserOperationResponse, + BuildAndSignUserOperationResponse, CheckTbaOwnershipResponse, SetWalletLimitsResponse, + SubmitUserOperationResponse, }; // Type aliases for compatibility @@ -305,6 +310,7 @@ pub fn operation_type(request: &HyperwalletRequest) -> Operation { HyperwalletRequest::BuildAndSignUserOperation(_) => Operation::BuildAndSignUserOperation, HyperwalletRequest::EstimateUserOperationGas(_) => Operation::EstimateUserOperationGas, HyperwalletRequest::ConfigurePaymaster(_) => Operation::ConfigurePaymaster, + HyperwalletRequest::SetWalletLimits(_) => Operation::SetWalletLimits, // Hypermap Operations HyperwalletRequest::ResolveIdentity(_) => Operation::ResolveIdentity, From 9a546cf1a7d1b5fdb3b0713b41c2f137f4d96d8b Mon Sep 17 00:00:00 2001 From: Hallmane Date: Tue, 12 Aug 2025 01:56:47 +0200 Subject: [PATCH 96/96] no --- src/hyperwallet_client/mod.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/hyperwallet_client/mod.rs b/src/hyperwallet_client/mod.rs index a577686..adad227 100644 --- a/src/hyperwallet_client/mod.rs +++ b/src/hyperwallet_client/mod.rs @@ -163,8 +163,6 @@ pub(crate) fn send_message( .map_err(|e| HyperwalletClientError::Communication(e.into()))? .map_err(|e| HyperwalletClientError::Communication(e.into()))?; - kiprintln!("Response: {:?}", response); - let hyperwallet_response: types::HyperwalletResponse = serde_json::from_slice(response.body()).map_err(HyperwalletClientError::Deserialization)?;