From 51e836e7418079090497be28e7663f3832b042bc Mon Sep 17 00:00:00 2001 From: Chris Smith <1979423+chris13524@users.noreply.github.com> Date: Thu, 26 Sep 2024 17:39:46 -0400 Subject: [PATCH] chore: refactor (#28) * chore: refactor * chore: remove more unwraps * chore: changes * chore: enable copy * chore: add license * chore: same entrypoint on all chains * feat: smart sessions module * fix: missing params * chore: set license in Cargo.toml * chore: remove dependency on pnpm * chore: use workspace license * chore: fixes --- .github/workflows/ci.yml | 12 - .gitmodules | 3 - Cargo.toml | 1 + LICENSE | 201 ++++++++++++++++ Makefile | 1 - README.md | 1 - crates/cli/Cargo.toml | 1 + crates/ffi/Cargo.toml | 1 + crates/yttrium/Cargo.toml | 1 + crates/yttrium/build.rs | 40 +--- crates/yttrium/safe7579 | 1 - crates/yttrium/src/bundler/client.rs | 2 +- crates/yttrium/src/bundler/pimlico/client.rs | 8 +- .../src/bundler/pimlico/paymaster/models.rs | 2 +- crates/yttrium/src/chain.rs | 12 +- crates/yttrium/src/entry_point.rs | 44 ++-- .../src/entry_point/get_sender_address.rs | 5 +- crates/yttrium/src/erc7579/mod.rs | 1 + .../yttrium/src/erc7579/smart_sessions/mod.rs | 52 ++++ crates/yttrium/src/lib.rs | 1 + crates/yttrium/src/signer.rs | 6 +- crates/yttrium/src/smart_accounts.rs | 1 + .../src/smart_accounts/account_address.rs | 44 ++++ crates/yttrium/src/smart_accounts/deployed.rs | 11 +- crates/yttrium/src/smart_accounts/nonce.rs | 34 ++- crates/yttrium/src/smart_accounts/safe.rs | 222 +++++++++++++++--- .../src/smart_accounts/simple_account.rs | 19 -- .../simple_account/sender_address.rs | 12 +- crates/yttrium/src/transaction.rs | 3 +- crates/yttrium/src/transaction/send.rs | 19 +- .../yttrium/src/transaction/send/safe_test.rs | 197 ++++++---------- .../send/send_tests/test_send_pimlico_v07.rs | 15 +- .../transaction/send/simple_account_test.rs | 27 +-- crates/yttrium/src/user_operation.rs | 29 ++- crates/yttrium/src/user_operation/hash.rs | 22 +- .../src/user_operation/hash/pack_v07.rs | 71 ++---- .../user_operation/hash/pack_v07/combine.rs | 44 ++-- .../hash/pack_v07/hashed_call_data.rs | 24 +- .../hash/pack_v07/hashed_init_code.rs | 21 +- .../pack_v07/hashed_paymaster_and_data.rs | 112 ++++----- ...riority_fee_per_gas_and_max_fee_per_gas.rs | 31 +-- ...ificaction_gas_limit_and_call_gas_limit.rs | 32 +-- 42 files changed, 804 insertions(+), 582 deletions(-) create mode 100644 LICENSE delete mode 160000 crates/yttrium/safe7579 create mode 100644 crates/yttrium/src/erc7579/mod.rs create mode 100644 crates/yttrium/src/erc7579/smart_sessions/mod.rs create mode 100644 crates/yttrium/src/smart_accounts/account_address.rs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ca58a333..3ec5738e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -17,9 +17,6 @@ jobs: - uses: actions/checkout@v4 - name: Run sccache-cache uses: mozilla-actions/sccache-action@v0.0.4 - - uses: pnpm/action-setup@v4 - with: - version: 9 - run: rustup update stable && rustup default stable - run: rustup toolchain install nightly -c rustfmt - run: git submodule update --init --recursive @@ -41,9 +38,6 @@ jobs: - uses: actions/checkout@v4 - name: Run sccache-cache uses: mozilla-actions/sccache-action@v0.0.4 - - uses: pnpm/action-setup@v4 - with: - version: 9 - run: rustup update stable && rustup default stable - run: rustup toolchain install nightly -c rustfmt - run: git submodule update --init --recursive @@ -58,9 +52,6 @@ jobs: # - uses: actions/checkout@v4 # - name: Run sccache-cache # uses: mozilla-actions/sccache-action@v0.0.4 - # - uses: pnpm/action-setup@v4 - # with: - # version: 9 # - run: rustup update stable && rustup default stable # - run: rustup target add wasm32-unknown-unknown # - run: git submodule update --init --recursive @@ -78,9 +69,6 @@ jobs: - uses: actions/checkout@v4 - name: Run sccache-cache uses: mozilla-actions/sccache-action@v0.0.4 - - uses: pnpm/action-setup@v4 - with: - version: 9 - run: rustup update stable && rustup default stable - run: git submodule update --init --recursive - run: make setup-thirdparty diff --git a/.gitmodules b/.gitmodules index 56f54a66..9c1ba801 100644 --- a/.gitmodules +++ b/.gitmodules @@ -7,9 +7,6 @@ [submodule "crates/yttrium/safe-modules"] path = crates/yttrium/safe-modules url = https://github.com/safe-global/safe-modules -[submodule "crates/yttrium/safe7579"] - path = crates/yttrium/safe7579 - url = https://github.com/rhinestonewtf/safe7579 [submodule "test/scripts/forked_state/mock-aa-environment"] path = test/scripts/forked_state/mock-aa-environment url = https://github.com/WalletConnect/mock-aa-environment.git diff --git a/Cargo.toml b/Cargo.toml index d29cfaee..e6a32d51 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,6 +6,7 @@ resolver = "2" version = "0.1.0" edition = "2021" rust-version = "1.79" +license = "Apache-2.0" [workspace.dependencies] # Errors/Result diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..0bd18f3e --- /dev/null +++ b/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2024 reown inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/Makefile b/Makefile index 010ac529..3cbb56fe 100644 --- a/Makefile +++ b/Makefile @@ -35,7 +35,6 @@ fetch-thirdparty: setup-thirdparty: cd crates/yttrium/src/contracts/ && yarn install --frozen-lockfile --immutable && yarn compile cd crates/yttrium/safe-smart-account/ && npm install - cd crates/yttrium/safe-modules/ && pnpm install build-ios-bindings: sh crates/ffi/build-rust-ios.sh diff --git a/README.md b/README.md index 79709f6b..a5297e82 100644 --- a/README.md +++ b/README.md @@ -81,7 +81,6 @@ To contribute to this project, ensure you have the following dependencies instal - `make` - `just` - `npm` -- `pnpm` - `yarn` ### Setup diff --git a/crates/cli/Cargo.toml b/crates/cli/Cargo.toml index 9a2c3439..b566a752 100644 --- a/crates/cli/Cargo.toml +++ b/crates/cli/Cargo.toml @@ -3,6 +3,7 @@ name = "cli" version.workspace = true edition.workspace = true rust-version.workspace = true +license.workspace = true # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/crates/ffi/Cargo.toml b/crates/ffi/Cargo.toml index de1c7d83..599d3db7 100644 --- a/crates/ffi/Cargo.toml +++ b/crates/ffi/Cargo.toml @@ -3,6 +3,7 @@ name = "ffi" version.workspace = true edition.workspace = true rust-version.workspace = true +license.workspace = true [lib] crate-type = ["staticlib"] diff --git a/crates/yttrium/Cargo.toml b/crates/yttrium/Cargo.toml index 354fc0ac..2524d287 100644 --- a/crates/yttrium/Cargo.toml +++ b/crates/yttrium/Cargo.toml @@ -3,6 +3,7 @@ name = "yttrium" version.workspace = true edition.workspace = true rust-version.workspace = true +license.workspace = true [features] full = [] diff --git a/crates/yttrium/build.rs b/crates/yttrium/build.rs index ed44465a..5a632633 100644 --- a/crates/yttrium/build.rs +++ b/crates/yttrium/build.rs @@ -1,7 +1,4 @@ -use { - // serde_json::Value, - std::process::{Command, Stdio}, -}; +use std::process::{Command, Stdio}; fn main() { build_contracts(); @@ -9,44 +6,17 @@ fn main() { fn build_contracts() { install_foundry(); - compile_contracts("crates/yttrium/safe-smart-account/contracts/proxies/SafeProxyFactory.sol", ); - compile_contracts("crates/yttrium/safe-smart-account/contracts/Safe.sol"); compile_contracts( - "crates/yttrium/safe-smart-account/contracts/libraries/MultiSend.sol", + "safe-smart-account/contracts/proxies/SafeProxyFactory.sol", ); + compile_contracts("safe-smart-account/contracts/Safe.sol"); + compile_contracts("safe-smart-account/contracts/libraries/MultiSend.sol"); compile_contracts( "safe-modules/modules/4337/contracts/SafeModuleSetup.sol", ); { - println!("cargo::rerun-if-changed=safe7579"); - let output = Command::new("pnpm") - .current_dir("safe7579") - .args(["install"]) - .stdout(Stdio::piped()) - .stderr(Stdio::piped()) - .spawn() - .unwrap() - .wait_with_output() - .unwrap(); - println!("`pnpm install` status: {:?}", output.status); - let stdout = String::from_utf8(output.stdout).unwrap(); - println!("`pnpm install` stdout: {stdout:?}"); - let stderr = String::from_utf8(output.stderr).unwrap(); - println!("`pnpm install` stderr: {stderr:?}"); - assert!(output.status.success()); - } - compile_contracts_with_args( - "safe7579/src/Safe7579Launchpad.sol", - &["--config-path=safe7579/foundry.toml".to_owned()], - ); - compile_contracts_with_args( - "safe7579/src/Safe7579.sol", - &["--config-path=safe7579/foundry.toml".to_owned()], - ); - - { - println!("cargo::rerun-if-changed=src/contracts"); + println!("cargo::rerun-if-changed=src/contracts/yarn.lock"); let output = Command::new("yarn") .current_dir("src/contracts") .args(["install"]) diff --git a/crates/yttrium/safe7579 b/crates/yttrium/safe7579 deleted file mode 160000 index 144eb335..00000000 --- a/crates/yttrium/safe7579 +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 144eb335f65997b6ac701e6390a274b3ca31c031 diff --git a/crates/yttrium/src/bundler/client.rs b/crates/yttrium/src/bundler/client.rs index faa269cb..58181153 100644 --- a/crates/yttrium/src/bundler/client.rs +++ b/crates/yttrium/src/bundler/client.rs @@ -269,7 +269,7 @@ mod tests { let bundler_client = setup_gas_estimation_bundler_mock().await?; let user_op = { - let sender: Address = sender; + let sender = sender.into(); let nonce: U256 = U256::from(0); let factory: Address = Address::ZERO; let factory_data: Bytes = Bytes::new(); diff --git a/crates/yttrium/src/bundler/pimlico/client.rs b/crates/yttrium/src/bundler/pimlico/client.rs index 159d513e..218d2a50 100644 --- a/crates/yttrium/src/bundler/pimlico/client.rs +++ b/crates/yttrium/src/bundler/pimlico/client.rs @@ -21,14 +21,13 @@ impl BundlerClient { use serde_json; - use crate::jsonrpc::{ - JSONRPCResponse, RequestWithEmptyParams, Response, - }; + use crate::jsonrpc::{JSONRPCResponse, Request, Response}; - let req_body = RequestWithEmptyParams { + let req_body = Request { jsonrpc: "2.0".into(), id: 1, method: "pimlico_getUserOperationGasPrice".into(), + params: [] as [(); 0], }; println!("req_body: {:?}", serde_json::to_string(&req_body)?); @@ -74,6 +73,7 @@ mod tests { "id": 1, "jsonrpc": "2.0", "method": "pimlico_getUserOperationGasPrice", + "params": [], }); let response_gas_price = GasPrice { diff --git a/crates/yttrium/src/bundler/pimlico/paymaster/models.rs b/crates/yttrium/src/bundler/pimlico/paymaster/models.rs index 7a7d7d16..7be7ee8f 100644 --- a/crates/yttrium/src/bundler/pimlico/paymaster/models.rs +++ b/crates/yttrium/src/bundler/pimlico/paymaster/models.rs @@ -39,7 +39,7 @@ pub struct UserOperationPreSponsorshipV07 { impl From for UserOperationPreSponsorshipV07 { fn from(user_op: UserOperationV07) -> Self { Self { - sender: user_op.sender, + sender: user_op.sender.into(), nonce: user_op.nonce, factory: user_op.factory, factory_data: user_op.factory_data, diff --git a/crates/yttrium/src/chain.rs b/crates/yttrium/src/chain.rs index 91e917de..f6e7895c 100644 --- a/crates/yttrium/src/chain.rs +++ b/crates/yttrium/src/chain.rs @@ -1,7 +1,7 @@ use crate::entry_point::{EntryPointConfig, EntryPointVersion}; use std::fmt; -#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] pub struct ChainId(u64); impl ChainId { @@ -9,6 +9,8 @@ impl ChainId { pub const ETHEREUM_SEPOLIA: Self = Self::new_eip155(11155111); + pub const BASE_SEPOLIA: Self = Self::new_eip155(84532); + pub const LOCAL_FOUNDRY_ETHEREUM_SEPOLIA: Self = Self::new_eip155(31337); pub const fn new_eip155(id: u64) -> Self { @@ -101,6 +103,12 @@ impl Chain { name: "Ethereum Sepolia", }; + pub const BASE_SEPOLIA_V07: Self = Self { + id: ChainId::BASE_SEPOLIA, + entry_point_version: EntryPointVersion::V07, + name: "Base Sepolia", + }; + pub const ETHEREUM_SEPOLIA_V06: Self = Self { id: ChainId::ETHEREUM_SEPOLIA, entry_point_version: EntryPointVersion::V06, @@ -123,7 +131,7 @@ impl Chain { impl Chain { pub fn entry_point_config(&self) -> EntryPointConfig { EntryPointConfig { - chain_id: self.id.clone(), + chain_id: self.id, version: self.entry_point_version, } } diff --git a/crates/yttrium/src/entry_point.rs b/crates/yttrium/src/entry_point.rs index d31a5e4f..2a95f16d 100644 --- a/crates/yttrium/src/entry_point.rs +++ b/crates/yttrium/src/entry_point.rs @@ -1,5 +1,8 @@ use crate::chain::ChainId; -use alloy::sol; +use alloy::{ + primitives::{address, Address}, + sol, +}; #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct EntryPointAddress(alloy::primitives::Address); @@ -32,10 +35,10 @@ impl From<&EntryPointAddress> for alloy::primitives::Address { } } -pub const ENTRYPOINT_ADDRESS_V06: &str = - "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789"; -pub const ENTRYPOINT_ADDRESS_V07: &str = - "0x0000000071727De22E5E9d8BAf0edAc6f37da032"; +pub const ENTRYPOINT_ADDRESS_V06: Address = + address!("5FF137D4b0FDCD49DcA30c7CF57E578a026d2789"); +pub const ENTRYPOINT_ADDRESS_V07: Address = + address!("0000000071727De22E5E9d8BAf0edAc6f37da032"); pub const ENTRYPOINT_V06_TYPE: &str = "v0.6"; pub const ENTRYPOINT_V07_TYPE: &str = "v0.7"; @@ -85,18 +88,15 @@ impl EntryPointConfig { }; pub fn address(&self) -> EntryPointAddress { - match self.chain_id { - ChainId::ETHEREUM_MAINNET - | ChainId::ETHEREUM_SEPOLIA - | ChainId::LOCAL_FOUNDRY_ETHEREUM_SEPOLIA => match self.version { - EntryPointVersion::V06 => EntryPointAddress::new( - ENTRYPOINT_ADDRESS_V06.parse().unwrap(), - ), - EntryPointVersion::V07 => EntryPointAddress::new( - ENTRYPOINT_ADDRESS_V07.parse().unwrap(), - ), - }, - _ => panic!("Unsupported chain ID"), + // assuming that the entrypoint address is the same for all chains, so + // not matching based on `chain_id` (anymore) + match self.version { + EntryPointVersion::V06 => { + EntryPointAddress::new(ENTRYPOINT_ADDRESS_V06) + } + EntryPointVersion::V07 => { + EntryPointAddress::new(ENTRYPOINT_ADDRESS_V07) + } } } @@ -147,14 +147,12 @@ impl From for EntryPointVersion { #[cfg(test)] mod tests { use super::*; - use alloy::primitives::Address; use eyre; #[test] fn test_address_type() -> eyre::Result<()> { { - let expected_v06_address: Address = - ENTRYPOINT_ADDRESS_V06.parse().unwrap(); + let expected_v06_address = ENTRYPOINT_ADDRESS_V06; let v06 = EntryPointVersion::V06; let mainnet_config = EntryPointConfig { chain_id: ChainId::ETHEREUM_MAINNET, @@ -168,8 +166,7 @@ mod tests { }; { - let expected_v07_address: Address = - ENTRYPOINT_ADDRESS_V07.parse().unwrap(); + let expected_v07_address = ENTRYPOINT_ADDRESS_V07; let v07 = EntryPointVersion::V07; let mainnet_config = EntryPointConfig { chain_id: ChainId::ETHEREUM_MAINNET, @@ -183,8 +180,7 @@ mod tests { }; { - let expected_v07_address: Address = - ENTRYPOINT_ADDRESS_V07.parse().unwrap(); + let expected_v07_address = ENTRYPOINT_ADDRESS_V07; let v07 = EntryPointVersion::V07; let local_sepolia_config = EntryPointConfig { chain_id: ChainId::LOCAL_FOUNDRY_ETHEREUM_SEPOLIA, diff --git a/crates/yttrium/src/entry_point/get_sender_address.rs b/crates/yttrium/src/entry_point/get_sender_address.rs index bca0c754..52006a0b 100644 --- a/crates/yttrium/src/entry_point/get_sender_address.rs +++ b/crates/yttrium/src/entry_point/get_sender_address.rs @@ -1,3 +1,4 @@ +use crate::smart_accounts::account_address::AccountAddress; use alloy::{ contract::Error as ContractError, primitives::{Address, Bytes}, @@ -43,7 +44,7 @@ pub async fn get_sender_address_v07( factory: Address, factory_data: Bytes, entrypoint: super::EntryPointAddress, -) -> eyre::Result
+) -> eyre::Result where T: alloy::contract::private::Transport + ::core::clone::Clone, P: alloy::contract::private::Provider, @@ -113,7 +114,7 @@ where println!("addr: {:?}", addr.clone()); - return Ok(addr); + return Ok(addr.into()); } else { return Err(eyre::eyre!("No data in error response")); }; diff --git a/crates/yttrium/src/erc7579/mod.rs b/crates/yttrium/src/erc7579/mod.rs new file mode 100644 index 00000000..b121dc74 --- /dev/null +++ b/crates/yttrium/src/erc7579/mod.rs @@ -0,0 +1 @@ +pub mod smart_sessions; diff --git a/crates/yttrium/src/erc7579/smart_sessions/mod.rs b/crates/yttrium/src/erc7579/smart_sessions/mod.rs new file mode 100644 index 00000000..75c44d6e --- /dev/null +++ b/crates/yttrium/src/erc7579/smart_sessions/mod.rs @@ -0,0 +1,52 @@ +use alloy::sol; + +sol! { + struct ChainDigest { + uint64 chainId; + bytes32 sessionDigest; + } + + struct PolicyData { + address policy; + bytes initData; + } + + struct ERC7739Data { + string[] allowedERC7739Content; + PolicyData[] erc1271Policies; + } + + struct ActionData { + bytes4 actionTargetSelector; + address actionTarget; + PolicyData[] actionPolicies; + } + + struct Session { + address sessionValidator; + bytes sessionValidatorInitData; + bytes32 salt; + PolicyData[] userOpPolicies; + ERC7739Data erc7739Policies; + ActionData[] actions; + } + + // https://github.com/erc7579/smartsessions/blob/b1624f851f56ec67cc677dce129e9caa12fcafd9/contracts/DataTypes.sol#L14 + struct EnableSession { + uint8 chainDigestIndex; + ChainDigest[] hashesAndChainIds; + Session sessionToEnable; + bytes permissionEnableSig; + } + + function enableSessionSig(EnableSession session, bytes signature); +} + +sol! { + type PermissionId is bytes32; + + #[sol(rpc)] + contract ISmartSession { + function isSessionEnabled(PermissionId permissionId, address account) external view returns (bool); + } +} diff --git a/crates/yttrium/src/lib.rs b/crates/yttrium/src/lib.rs index 32ea0ede..8dcb2efa 100644 --- a/crates/yttrium/src/lib.rs +++ b/crates/yttrium/src/lib.rs @@ -5,6 +5,7 @@ pub mod chain; pub mod config; pub mod eip7702; pub mod entry_point; +pub mod erc7579; pub mod error; pub mod jsonrpc; pub mod private_key_service; diff --git a/crates/yttrium/src/signer.rs b/crates/yttrium/src/signer.rs index 7a06a3bf..d8d69a1a 100644 --- a/crates/yttrium/src/signer.rs +++ b/crates/yttrium/src/signer.rs @@ -86,7 +86,7 @@ impl Signer { chain_id: u64, sign_service: &MutexGuard, ) -> eyre::Result { - let hash = uo.hash(ep, chain_id)?; + let hash = uo.hash(ep, chain_id); let message_bytes = hash.0.to_vec(); println!("message_bytes: {:?}", message_bytes.clone()); @@ -152,7 +152,7 @@ pub fn sign_user_operation_v07_with_ecdsa_and_sign_service( signer: PrivateKeySigner, sign_service: &Arc>, ) -> eyre::Result { - let hash = uo.hash(ep, chain_id)?; + let hash = uo.hash(ep, chain_id); println!("hash: {:?}", hash.clone()); @@ -204,7 +204,7 @@ pub fn sign_user_operation_v07_with_ecdsa( chain_id: u64, signer: PrivateKeySigner, ) -> eyre::Result { - let hash = uo.hash(ep, chain_id)?; + let hash = uo.hash(ep, chain_id); println!("hash: {:?}", hash.clone()); diff --git a/crates/yttrium/src/smart_accounts.rs b/crates/yttrium/src/smart_accounts.rs index 282ef6b8..cecb0e86 100644 --- a/crates/yttrium/src/smart_accounts.rs +++ b/crates/yttrium/src/smart_accounts.rs @@ -1,3 +1,4 @@ +pub mod account_address; pub mod deployed; pub mod nonce; pub mod safe; diff --git a/crates/yttrium/src/smart_accounts/account_address.rs b/crates/yttrium/src/smart_accounts/account_address.rs new file mode 100644 index 00000000..e44deb2c --- /dev/null +++ b/crates/yttrium/src/smart_accounts/account_address.rs @@ -0,0 +1,44 @@ +use alloy::primitives::Address; +use serde::{Deserialize, Serialize}; + +#[derive( + Debug, + Clone, + Copy, + PartialEq, + Eq, + Ord, + PartialOrd, + Serialize, + Deserialize, + Default, +)] +pub struct AccountAddress(Address); + +impl AccountAddress { + pub fn new(address: Address) -> Self { + Self(address) + } + + pub fn to_address(&self) -> Address { + self.0 + } +} + +impl From for Address { + fn from(val: AccountAddress) -> Self { + val.0 + } +} + +impl From
for AccountAddress { + fn from(val: Address) -> Self { + Self::new(val) + } +} + +impl std::fmt::Display for AccountAddress { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.0) + } +} diff --git a/crates/yttrium/src/smart_accounts/deployed.rs b/crates/yttrium/src/smart_accounts/deployed.rs index 0ff6db75..37fdea7c 100644 --- a/crates/yttrium/src/smart_accounts/deployed.rs +++ b/crates/yttrium/src/smart_accounts/deployed.rs @@ -1,19 +1,18 @@ -use alloy::{ - contract::private::{Network, Provider, Transport}, - primitives::Address, -}; +use super::account_address::AccountAddress; +use alloy::contract::private::{Network, Provider, Transport}; use core::clone::Clone; pub async fn is_smart_account_deployed( provider: &P, - sender_address: Address, + sender_address: AccountAddress, ) -> eyre::Result where T: Transport + Clone, P: Provider, N: Network, { - let contract_code = provider.get_code_at(sender_address).await?; + let contract_code = + provider.get_code_at(sender_address.to_address()).await?; if contract_code.len() > 2 { return Ok(true); diff --git a/crates/yttrium/src/smart_accounts/nonce.rs b/crates/yttrium/src/smart_accounts/nonce.rs index 8193f01c..799d0b8e 100644 --- a/crates/yttrium/src/smart_accounts/nonce.rs +++ b/crates/yttrium/src/smart_accounts/nonce.rs @@ -1,18 +1,35 @@ use crate::{ entry_point::{EntryPoint, EntryPointAddress}, - smart_accounts::simple_account::SimpleAccountAddress, + smart_accounts::account_address::AccountAddress, }; use alloy::{ - contract::private::{Network, Provider, Transport}, - primitives::aliases::U192, + contract::{ + private::{Network, Provider, Transport}, + Error, + }, + primitives::{aliases::U192, U256}, }; use core::clone::Clone; pub async fn get_nonce( provider: &P, - address: &SimpleAccountAddress, + address: AccountAddress, entry_point_address: &EntryPointAddress, -) -> eyre::Result +) -> Result +where + T: Transport + Clone, + P: Provider, + N: Network, +{ + get_nonce_with_key(provider, address, entry_point_address, U192::ZERO).await +} + +pub async fn get_nonce_with_key( + provider: &P, + address: AccountAddress, + entry_point_address: &EntryPointAddress, + key: U192, +) -> Result where T: Transport + Clone, P: Provider, @@ -20,14 +37,9 @@ where { let entry_point_instance = EntryPoint::new(entry_point_address.to_address(), provider); - let key = U192::ZERO; let get_nonce_call = entry_point_instance.getNonce(address.to_address(), key).call().await?; - let nonce_uint = get_nonce_call.nonce; - - let nonce: u64 = nonce_uint.to::(); - - Ok(nonce) + Ok(get_nonce_call.nonce) } diff --git a/crates/yttrium/src/smart_accounts/safe.rs b/crates/yttrium/src/smart_accounts/safe.rs index 69008731..9e329d94 100644 --- a/crates/yttrium/src/smart_accounts/safe.rs +++ b/crates/yttrium/src/smart_accounts/safe.rs @@ -1,6 +1,10 @@ +use crate::smart_accounts::account_address::AccountAddress; +use crate::transaction::Transaction; use alloy::{ dyn_abi::DynSolValue, - primitives::{address, keccak256, Address, Bytes, Uint, U256}, + primitives::{ + address, bytes, keccak256, Address, Bytes, FixedBytes, Uint, U256, + }, providers::ReqwestProvider, sol, sol_types::{SolCall, SolValue}, @@ -21,32 +25,53 @@ sol!( ".foundry/forge/out/Safe.sol/Safe.json" ); -sol!( - #[allow(clippy::too_many_arguments)] - #[allow(missing_docs)] - #[sol(rpc)] - Safe7579Launchpad, - ".foundry/forge/out/Safe7579Launchpad.sol/Safe7579Launchpad.json" -); +sol! { + contract Safe7579Launchpad { + struct ModuleInit { + address module; + bytes initData; + } -sol!( - #[allow(missing_docs)] - #[sol(rpc)] - Safe7579, - ".foundry/forge/out/Safe7579.sol/Safe7579.json" -); + struct InitData { + address singleton; + address[] owners; + uint256 threshold; + address setupTo; + bytes setupData; + address safe7579; + ModuleInit[] validators; + bytes callData; + } -// Had to copy from safe7579/artifacts/interfaces/IERC7579Account.json -// This struct doesn't seem to be in generated ABIs -sol!( - #[allow(missing_docs)] - #[sol(rpc, abi)] - struct Execution { - address target; - uint256 value; - bytes callData; + function initSafe7579( + address safe7579, + ModuleInit[] calldata executors, + ModuleInit[] calldata fallbacks, + ModuleInit[] calldata hooks, + address[] calldata attesters, + uint8 threshold + ); + + function setupSafe(InitData calldata initData); + + function preValidationSetup( + bytes32 initHash, + address to, + bytes calldata preInit + ); } -); +} + +sol! { + contract Safe7579 { + type ModeCode is bytes32; + + function execute( + ModeCode mode, + bytes calldata executionCalldata + ); + } +} // https://github.com/WalletConnect/secure-web3modal/blob/f1d16f973a313e598d124a0e4751aee12d5de628/src/core/SmartAccountSdk/utils.ts#L180 pub const SAFE_ERC_7579_LAUNCHPAD_ADDRESS: Address = @@ -93,7 +118,7 @@ sol!( ".foundry/forge/out/MultiSend.sol/MultiSend.json" ); -pub const DUMMY_SIGNATURE_HEX: &str = "0x000000000000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"; +pub const DUMMY_SIGNATURE: Bytes = bytes!("000000000000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); // https://github.com/WalletConnect/secure-web3modal/blob/c19a1e7b21c6188261728f4d521a17f94da4f055/src/core/SmartAccountSdk/constants.ts#L10 // const APPKIT_SALT: U256 = U256::from_str("zg3ijy0p46"); @@ -161,7 +186,7 @@ pub fn factory_data( pub async fn get_account_address( provider: ReqwestProvider, owners: Owners, -) -> Address { +) -> AccountAddress { let creation_code = SafeProxyFactory::new(SAFE_PROXY_FACTORY_ADDRESS, provider.clone()) .proxyCreationCode() @@ -188,5 +213,148 @@ pub async fn get_account_address( ]) .abi_encode_packed(), ); - SAFE_PROXY_FACTORY_ADDRESS.create2(salt, keccak256(deployment_code)) + SAFE_PROXY_FACTORY_ADDRESS.create2(salt, keccak256(deployment_code)).into() +} + +pub fn get_call_data(execution_calldata: Vec) -> Bytes { + let batch = execution_calldata.len() != 1; + let revert_on_error = false; + let selector = [0u8; 4]; + let context = [0u8; 22]; + + let mode = DynSolValue::Tuple(vec![ + DynSolValue::Uint(Uint::from(if batch { 0x01 } else { 0x00 }), 8), // DelegateCall is 0xFF + DynSolValue::Uint(Uint::from(revert_on_error as u8), 8), + DynSolValue::Bytes(vec![0u8; 4]), + DynSolValue::Bytes(selector.to_vec()), + DynSolValue::Bytes(context.to_vec()), + ]) + .abi_encode_packed(); + + let execution_calldata = encode_calls(execution_calldata); + + Safe7579::executeCall { + mode: FixedBytes::from_slice(&mode), + executionCalldata: execution_calldata, + } + .abi_encode() + .into() +} + +sol! { + function executionBatch((address, uint256, bytes)[]); +} + +fn encode_calls(calls: Vec) -> Bytes { + fn call(call: Transaction) -> (Address, U256, Bytes) { + (call.to, call.value, call.data) + } + + let tuples = calls.into_iter().map(call).collect::>(); + if tuples.len() == 1 { + tuples.abi_encode_packed() + } else { + let call = executionBatchCall { _0: tuples }; + + // encode without selector + let mut out = Vec::with_capacity(call.abi_encoded_size()); + call.abi_encode_raw(&mut out); + out + } + .into() +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn empty_execution_call_data() { + assert_eq!(encode_calls(vec![]), bytes!("00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000")); + } + + #[test] + fn single_execution_call_data_value() { + assert_eq!( + encode_calls(vec![Transaction { + to: address!("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"), + value: U256::from(19191919), + data: bytes!(""), + }]), + bytes!("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa000000000000000000000000000000000000000000000000000000000124d86f") + ); + } + + #[test] + fn single_execution_call_data_data() { + assert_eq!( + encode_calls(vec![Transaction { + to: address!("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"), + value: U256::ZERO, + data: bytes!("7777777777777777"), + }]), + bytes!("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa00000000000000000000000000000000000000000000000000000000000000007777777777777777") + ); + } + + #[test] + fn two_execution_call_data() { + assert_eq!( + encode_calls(vec![Transaction { + to: address!("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"), + value: U256::from(19191919), + data: bytes!(""), + }, Transaction { + to: address!("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"), + value: U256::ZERO, + data: bytes!("7777777777777777"), + }]), + bytes!("00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa000000000000000000000000000000000000000000000000000000000124d86f00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000087777777777777777000000000000000000000000000000000000000000000000") + ); + } + + #[test] + fn empty_call_data() { + assert_eq!(get_call_data(vec![]), bytes!("e9ae5c5301000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000")); + } + + #[test] + fn single_call_data_value() { + assert_eq!( + get_call_data(vec![Transaction { + to: address!("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"), + value: U256::from(19191919), + data: bytes!(""), + }]), + bytes!("e9ae5c53000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000034aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa000000000000000000000000000000000000000000000000000000000124d86f000000000000000000000000") + ); + } + + #[test] + fn single_call_data_data() { + assert_eq!( + get_call_data(vec![Transaction { + to: address!("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"), + value: U256::ZERO, + data: bytes!("7777777777777777"), + }]), + bytes!("e9ae5c5300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000003caaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0000000000000000000000000000000000000000000000000000000000000000777777777777777700000000") + ); + } + + #[test] + fn two_call_data() { + assert_eq!( + get_call_data(vec![Transaction { + to: address!("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"), + value: U256::from(19191919), + data: bytes!(""), + }, Transaction { + to: address!("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"), + value: U256::ZERO, + data: bytes!("7777777777777777"), + }]), + bytes!("e9ae5c530100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa000000000000000000000000000000000000000000000000000000000124d86f00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000087777777777777777000000000000000000000000000000000000000000000000") + ); + } } diff --git a/crates/yttrium/src/smart_accounts/simple_account.rs b/crates/yttrium/src/smart_accounts/simple_account.rs index d0599d1f..b8db743c 100644 --- a/crates/yttrium/src/smart_accounts/simple_account.rs +++ b/crates/yttrium/src/smart_accounts/simple_account.rs @@ -34,25 +34,6 @@ sol!( pub const DUMMY_SIGNATURE_HEX: &str = "0xfffffffffffffffffffffffffffffff0000000000000000000000000000000007aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa1c"; -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct SimpleAccountAddress(alloy::primitives::Address); - -impl SimpleAccountAddress { - pub fn new(address: alloy::primitives::Address) -> Self { - Self(address) - } - - pub fn to_address(&self) -> alloy::primitives::Address { - self.0 - } -} - -impl From for alloy::primitives::Address { - fn from(val: SimpleAccountAddress) -> Self { - val.0 - } -} - #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct OwnerAddress(alloy::primitives::Address); diff --git a/crates/yttrium/src/smart_accounts/simple_account/sender_address.rs b/crates/yttrium/src/smart_accounts/simple_account/sender_address.rs index 0e7c9371..df96dc34 100644 --- a/crates/yttrium/src/smart_accounts/simple_account/sender_address.rs +++ b/crates/yttrium/src/smart_accounts/simple_account/sender_address.rs @@ -4,8 +4,11 @@ use crate::{ entry_point::{ get_sender_address::get_sender_address_v07, EntryPointVersion, }, - smart_accounts::simple_account::{ - create_account::SimpleAccountCreate, factory::FactoryAddress, + smart_accounts::{ + account_address::AccountAddress, + simple_account::{ + create_account::SimpleAccountCreate, factory::FactoryAddress, + }, }, }; use alloy::{ @@ -17,14 +20,13 @@ pub async fn get_sender_address_with_signer( config: Config, chain_id: u64, signer: PrivateKeySigner, -) -> eyre::Result
{ +) -> eyre::Result { let _bundler_base_url = config.clone().endpoints.bundler.base_url; let _paymaster_base_url = config.clone().endpoints.paymaster.base_url; let rpc_base_url = config.clone().endpoints.rpc.base_url; let chain_id = ChainId::new_eip155(chain_id); - let chain = - crate::chain::Chain::new(chain_id.clone(), EntryPointVersion::V07, ""); + let chain = crate::chain::Chain::new(chain_id, EntryPointVersion::V07, ""); let entry_point_config = chain.entry_point_config(); diff --git a/crates/yttrium/src/transaction.rs b/crates/yttrium/src/transaction.rs index 79a20558..888e0c23 100644 --- a/crates/yttrium/src/transaction.rs +++ b/crates/yttrium/src/transaction.rs @@ -1,8 +1,9 @@ use alloy::primitives::{address, Address, Bytes, U256}; +use serde::{Deserialize, Serialize}; pub mod send; -#[derive(Debug, Clone, PartialEq)] +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)] pub struct Transaction { pub to: Address, pub value: U256, diff --git a/crates/yttrium/src/transaction/send.rs b/crates/yttrium/src/transaction/send.rs index cc2589bf..56f72b0b 100644 --- a/crates/yttrium/src/transaction/send.rs +++ b/crates/yttrium/src/transaction/send.rs @@ -1,4 +1,3 @@ -use crate::smart_accounts::safe::Execution; use crate::transaction::send::simple_account_test::send_transaction_with_signer; use crate::{ config::Config, transaction::Transaction, user_operation::UserOperationV07, @@ -89,11 +88,7 @@ pub async fn send_transaction_with_private_key_signer( let user_operation_hash = if safe { safe_test::send_transaction( - vec![Execution { - target: transaction.to, - value: transaction.value, - callData: transaction.data, - }], + vec![transaction], signer, None, None, @@ -129,7 +124,7 @@ mod tests { nonce::get_nonce, simple_account::{ create_account::SimpleAccountCreate, factory::FactoryAddress, - SimpleAccountAddress, SimpleAccountExecute, + SimpleAccountExecute, }, }, user_operation::UserOperationV07, @@ -266,16 +261,12 @@ mod tests { println!("Gas price: {:?}", gas_price); - let nonce = get_nonce( - &provider, - &SimpleAccountAddress::new(sender_address), - &entry_point_address, - ) - .await?; + let nonce = + get_nonce(&provider, sender_address, &entry_point_address).await?; let user_op = UserOperationV07 { sender: sender_address, - nonce: U256::from(nonce), + nonce, factory: None, factory_data: None, call_data: Bytes::from_str(&call_data_value_hex)?, diff --git a/crates/yttrium/src/transaction/send/safe_test.rs b/crates/yttrium/src/transaction/send/safe_test.rs index 3fbdc182..f00626fd 100644 --- a/crates/yttrium/src/transaction/send/safe_test.rs +++ b/crates/yttrium/src/transaction/send/safe_test.rs @@ -9,33 +9,31 @@ use crate::{ }, config::Config, smart_accounts::{ + account_address::AccountAddress, nonce::get_nonce, safe::{ - factory_data, get_account_address, Execution, Owners, Safe7579, - Safe7579Launchpad, SAFE_4337_MODULE_ADDRESS, + factory_data, get_account_address, get_call_data, Owners, + Safe7579Launchpad, DUMMY_SIGNATURE, SAFE_4337_MODULE_ADDRESS, SAFE_ERC_7579_LAUNCHPAD_ADDRESS, SAFE_PROXY_FACTORY_ADDRESS, SEPOLIA_SAFE_ERC_7579_SINGLETON_ADDRESS, }, - simple_account::{factory::FactoryAddress, SimpleAccountAddress}, + simple_account::factory::FactoryAddress, }, + transaction::Transaction, user_operation::{Authorization, UserOperationV07}, }; use alloy::{ - dyn_abi::{ - DynSolCall, DynSolReturns, DynSolType, DynSolValue, Eip712Domain, - }, + dyn_abi::{DynSolValue, Eip712Domain}, network::Ethereum, - primitives::{ - aliases::U48, keccak256, Address, Bytes, FixedBytes, Uint, U128, U256, - }, + primitives::{aliases::U48, Address, Bytes, Uint, U128, U256}, providers::{Provider, ReqwestProvider}, signers::{k256::ecdsa::SigningKey, local::LocalSigner, SignerSync}, sol, - sol_types::{SolCall, SolValue}, + sol_types::SolCall, }; use core::fmt; use serde_json::json; -use std::{ops::Not, str::FromStr}; +use std::ops::Not; #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] pub struct UserOperationEstimated(UserOperationV07); @@ -73,7 +71,7 @@ impl fmt::Display for SentUserOperationHash { pub async fn get_address( owner: LocalSigner, config: Config, -) -> eyre::Result
{ +) -> eyre::Result { let rpc_url = config.endpoints.rpc.base_url; let rpc_url: reqwest::Url = rpc_url.parse()?; let provider = ReqwestProvider::::new_http(rpc_url); @@ -84,9 +82,9 @@ pub async fn get_address( } pub async fn send_transaction( - execution_calldata: Vec, + execution_calldata: Vec, owner: LocalSigner, - address: Option
, + address: Option, authorization_list: Option>, config: Config, ) -> eyre::Result { @@ -124,65 +122,10 @@ pub async fn send_transaction( let account_address = if let Some(address) = address { address } else { contract_address }; - let call_data = { - let batch = execution_calldata.len() != 1; - let revert_on_error = false; - let selector = [0u8; 4]; - let context = [0u8; 22]; - - let mode = - DynSolValue::Tuple(vec![ - DynSolValue::Uint( - Uint::from(if batch { 0x01 } else { 0x00 }), - 8, - ), // DelegateCall is 0xFF - DynSolValue::Uint(Uint::from(revert_on_error as u8), 8), - DynSolValue::Bytes(vec![0u8; 4]), - DynSolValue::Bytes(selector.to_vec()), - DynSolValue::Bytes(context.to_vec()), - ]) - .abi_encode_packed(); - - let execution_calldata = if batch { - DynSolCall::new( - FixedBytes::from_slice( - &keccak256("executionBatch(tuple[])")[..4], - ), - vec![DynSolType::Array(Box::new(DynSolType::Tuple(vec![ - DynSolType::Address, - DynSolType::Uint(32), - DynSolType::Bytes, - ])))], - None, - DynSolReturns::new(vec![]), - ) - .abi_encode_input(&[DynSolValue::Array( - execution_calldata - .iter() - .map(|execution| { - DynSolValue::Tuple(vec![ - DynSolValue::Address(execution.target), - DynSolValue::Uint(Uint::from(execution.value), 32), - DynSolValue::Bytes(execution.callData.to_vec()), - ]) - }) - .collect(), - )])?[4..] - .to_vec() - } else { - execution_calldata.abi_encode_packed() - } - .into(); + let call_data = get_call_data(execution_calldata); - Safe7579::executeCall { - mode: FixedBytes::from_slice(&mode), - executionCalldata: execution_calldata, - } - .abi_encode() - .into() - }; - - let deployed = provider.get_code_at(account_address).await?.len() > 0; + let deployed = + provider.get_code_at(account_address.into()).await?.len() > 0; println!("Deployed: {}", deployed); // permissionless: signerToSafeSmartAccount -> encodeCallData let call_data = if deployed { @@ -217,22 +160,18 @@ pub async fn send_transaction( assert!(gas_price.fast.max_fee_per_gas > U256::from(1)); - let nonce = get_nonce( - &provider, - &SimpleAccountAddress::new(account_address), - &entry_point_address, - ) - .await?; + let nonce = + get_nonce(&provider, account_address, &entry_point_address).await?; let user_op = UserOperationV07 { sender: account_address, - nonce: U256::from(nonce), + nonce, factory: deployed.not().then(|| safe_factory_address.to_address()), factory_data: deployed.not().then(|| factory_data_value.into()), call_data, - call_gas_limit: U256::from(0), - verification_gas_limit: U256::from(0), - pre_verification_gas: U256::from(0), + call_gas_limit: U256::ZERO, + verification_gas_limit: U256::ZERO, + pre_verification_gas: U256::ZERO, max_fee_per_gas: gas_price.fast.max_fee_per_gas, max_priority_fee_per_gas: gas_price.fast.max_priority_fee_per_gas, paymaster: None, @@ -240,11 +179,7 @@ pub async fn send_transaction( paymaster_post_op_gas_limit: None, paymaster_data: None, // authorization_list: None, - signature: Bytes::from_str( - crate::smart_accounts::safe::DUMMY_SIGNATURE_HEX - .strip_prefix("0x") - .unwrap(), - )?, + signature: DUMMY_SIGNATURE, }; if let Some(authorization_list) = authorization_list { @@ -322,7 +257,7 @@ pub async fn send_transaction( } let message = SafeOp { - safe: account_address, + safe: account_address.into(), callData: sponsored_user_op.call_data.clone(), nonce: sponsored_user_op.nonce, initCode: deployed @@ -387,7 +322,7 @@ pub async fn send_transaction( let erc7579_launchpad_address = true; let verifying_contract = if erc7579_launchpad_address && !deployed { - sponsored_user_op.sender + sponsored_user_op.sender.into() } else { SAFE_4337_MODULE_ADDRESS }; @@ -460,7 +395,7 @@ pub async fn send_transaction( #[cfg(test)] mod tests { use super::*; - use crate::chain::ChainId; + use crate::{chain::ChainId, transaction::Transaction}; use alloy::{ consensus::{SignableTransaction, TxEip7702}, network::{EthereumWallet, TransactionBuilder, TxSignerSync}, @@ -474,7 +409,7 @@ mod tests { faucet: LocalSigner, amount: U256, to: Address, - ) -> eyre::Result<()> { + ) { // Basic check (which we can tune) to make sure we don't use excessive // amounts (e.g. 0.1) of test ETH. It is not infinite, so we should use // the minimum amount necessary. @@ -487,13 +422,13 @@ mod tests { .send_transaction( TransactionRequest::default().with_to(to).with_value(amount), ) - .await? + .await + .unwrap() .watch() - .await?; - let balance = provider.get_balance(to).await?; + .await + .unwrap(); + let balance = provider.get_balance(to).await.unwrap(); assert_eq!(balance, amount); - - Ok(()) } async fn test_send_transaction( @@ -519,14 +454,14 @@ mod tests { provider.clone(), faucet.clone(), U256::from(2), - sender_address, + sender_address.into(), ) - .await?; + .await; - let transaction = vec![Execution { - target: destination.address(), + let transaction = vec![Transaction { + to: destination.address(), value: Uint::from(1), - callData: Bytes::new(), + data: Bytes::new(), }]; let transaction_hash = send_transaction( @@ -543,10 +478,10 @@ mod tests { let balance = provider.get_balance(destination.address()).await?; assert_eq!(balance, Uint::from(1)); - let transaction = vec![Execution { - target: destination.address(), + let transaction = vec![Transaction { + to: destination.address(), value: Uint::from(1), - callData: Bytes::new(), + data: Bytes::new(), }]; let transaction_hash = @@ -611,15 +546,19 @@ mod tests { Owners { owners: vec![owner.address()], threshold: 1 }, ) .await; - assert!(provider.get_code_at(sender_address).await.unwrap().is_empty()); + assert!(provider + .get_code_at(sender_address.into()) + .await + .unwrap() + .is_empty()); use_faucet( provider.clone(), faucet.clone(), U256::from(3), - sender_address, + sender_address.into(), ) - .await?; + .await; let transaction = vec![]; @@ -635,7 +574,7 @@ mod tests { println!("Transaction sent: {}", transaction_hash); assert!(!provider - .get_code_at(sender_address) + .get_code_at(sender_address.into()) .await .unwrap() .is_empty()); @@ -666,20 +605,20 @@ mod tests { provider.clone(), faucet.clone(), U256::from(3), - sender_address, + sender_address.into(), ) - .await?; + .await; let transaction = vec![ - Execution { - target: destination1.address(), + Transaction { + to: destination1.address(), value: Uint::from(1), - callData: Bytes::new(), + data: Bytes::new(), }, - Execution { - target: destination2.address(), + Transaction { + to: destination2.address(), value: Uint::from(2), - callData: Bytes::new(), + data: Bytes::new(), }, ]; @@ -734,7 +673,7 @@ mod tests { let chain_id = ChainId::ETHEREUM_SEPOLIA.eip155_chain_id(); let auth_7702 = alloy::rpc::types::Authorization { chain_id: U256::from(chain_id), - address: contract_address, + address: contract_address.into(), nonce: provider.get_transaction_count(authority.address()).await?, }; @@ -763,7 +702,7 @@ mod tests { let transaction_hash = send_transaction( transaction, owner.clone(), - Some(authority.address()), + Some(authority.address().into()), Some(authorization_list.clone()), // None, config.clone(), @@ -774,23 +713,23 @@ mod tests { println!("contract address: {}", contract_address); println!( "contract code: {}", - provider.get_code_at(contract_address).await? + provider.get_code_at(contract_address.into()).await? ); println!( "authority code: {}", provider.get_code_at(authority.address()).await? ); - let transaction = vec![Execution { - target: destination.address(), + let transaction = vec![Transaction { + to: destination.address(), value: Uint::from(1), - callData: Bytes::new(), + data: Bytes::new(), }]; let transaction_hash = send_transaction( transaction, owner, - Some(authority.address()), + Some(authority.address().into()), // None, // Some(authorization_list.clone()), None, @@ -867,7 +806,7 @@ mod tests { let chain_id = ChainId::ETHEREUM_SEPOLIA.eip155_chain_id(); let auth_7702 = alloy::rpc::types::Authorization { chain_id: U256::from(chain_id), - address: contract_address, + address: contract_address.into(), nonce: provider.get_transaction_count(authority.address()).await?, }; @@ -920,23 +859,23 @@ mod tests { println!("contract address: {}", contract_address); println!( "contract code: {}", - provider.get_code_at(contract_address).await? + provider.get_code_at(contract_address.into()).await? ); println!( "authority code: {}", provider.get_code_at(authority.address()).await? ); - let transaction = vec![Execution { - target: destination.address(), + let transaction: Vec<_> = vec![Transaction { + to: destination.address(), value: Uint::from(1), - callData: Bytes::new(), + data: Bytes::new(), }]; let transaction_hash = send_transaction( transaction, owner, - Some(authority.address()), + Some(authority.address().into()), None, config, ) diff --git a/crates/yttrium/src/transaction/send/send_tests/test_send_pimlico_v07.rs b/crates/yttrium/src/transaction/send/send_tests/test_send_pimlico_v07.rs index 60f91807..3d1ff886 100644 --- a/crates/yttrium/src/transaction/send/send_tests/test_send_pimlico_v07.rs +++ b/crates/yttrium/src/transaction/send/send_tests/test_send_pimlico_v07.rs @@ -2,9 +2,12 @@ mod tests { use crate::{ bundler::{ - client::BundlerClient, config::BundlerConfig, - pimlico::client::BundlerClient as PimlicoBundlerClient, - pimlico::paymaster::client::PaymasterClient, + client::BundlerClient, + config::BundlerConfig, + pimlico::{ + client::BundlerClient as PimlicoBundlerClient, + paymaster::client::PaymasterClient, + }, }, entry_point::get_sender_address::get_sender_address_v07, smart_accounts::simple_account::{ @@ -158,16 +161,14 @@ mod tests { let nonce = crate::smart_accounts::nonce::get_nonce( &provider, - &crate::smart_accounts::simple_account::SimpleAccountAddress::new( - sender_address, - ), + sender_address, &entry_point_address, ) .await?; let user_op = UserOperationV07 { sender: sender_address, - nonce: U256::from(nonce), + nonce, factory: None, factory_data: None, call_data: Bytes::from_str(&call_data_value_hex).unwrap(), diff --git a/crates/yttrium/src/transaction/send/simple_account_test.rs b/crates/yttrium/src/transaction/send/simple_account_test.rs index d897f505..ab1850e6 100644 --- a/crates/yttrium/src/transaction/send/simple_account_test.rs +++ b/crates/yttrium/src/transaction/send/simple_account_test.rs @@ -15,7 +15,7 @@ use crate::{ nonce::get_nonce, simple_account::{ create_account::SimpleAccountCreate, factory::FactoryAddress, - SimpleAccountAddress, SimpleAccountExecute, + SimpleAccountExecute, }, }, transaction::Transaction, @@ -81,8 +81,7 @@ pub async fn send_transaction_with_signer( use crate::entry_point::EntryPointVersion; let chain_id = ChainId::new_eip155(chain_id); - let chain = - crate::chain::Chain::new(chain_id.clone(), EntryPointVersion::V07, ""); + let chain = crate::chain::Chain::new(chain_id, EntryPointVersion::V07, ""); let entry_point_config = chain.entry_point_config(); let entry_point_address = entry_point_config.address(); @@ -169,16 +168,12 @@ pub async fn send_transaction_with_signer( println!("Gas price: {:?}", gas_price); - let nonce = get_nonce( - &provider, - &SimpleAccountAddress::new(sender_address), - &entry_point_address, - ) - .await?; + let nonce = + get_nonce(&provider, sender_address, &entry_point_address).await?; let user_op = UserOperationV07 { sender: sender_address, - nonce: U256::from(nonce), + nonce, factory, factory_data, call_data: Bytes::from_str(&call_data_value_hex)?, @@ -269,7 +264,7 @@ mod tests { nonce::get_nonce, simple_account::{ create_account::SimpleAccountCreate, factory::FactoryAddress, - SimpleAccountAddress, SimpleAccountExecute, + SimpleAccountExecute, }, }, transaction::Transaction, @@ -372,16 +367,12 @@ mod tests { println!("Gas price: {:?}", gas_price); - let nonce = get_nonce( - &provider, - &SimpleAccountAddress::new(sender_address), - &entry_point_address, - ) - .await?; + let nonce = + get_nonce(&provider, sender_address, &entry_point_address).await?; let user_op = UserOperationV07 { sender: sender_address, - nonce: U256::from(nonce), + nonce, factory: Some(simple_account_factory_address.to_address()), factory_data: Some(factory_data_value.into()), call_data: Bytes::from_str(&call_data_value_hex)?, diff --git a/crates/yttrium/src/user_operation.rs b/crates/yttrium/src/user_operation.rs index b763db12..c6334d66 100644 --- a/crates/yttrium/src/user_operation.rs +++ b/crates/yttrium/src/user_operation.rs @@ -1,18 +1,24 @@ -use alloy::primitives::{Address, Bytes, U256}; +use crate::{ + smart_accounts::account_address::AccountAddress, + user_operation::{ + hash::get_user_operation_hash_v07, + user_operation_hash::UserOperationHash, + }, +}; +use alloy::primitives::{address, Address, Bytes, U256}; use serde::{Deserialize, Serialize}; pub mod hash; pub mod user_operation_hash; -use crate::user_operation::{ - hash::get_user_operation_hash_v07, user_operation_hash::UserOperationHash, -}; - -pub fn as_checksum_addr(val: &Address, s: S) -> Result +pub fn as_checksum_addr( + val: &AccountAddress, + s: S, +) -> Result where S: serde::Serializer, { - let address_checksum: String = val.to_checksum(None); + let address_checksum: String = val.to_address().to_checksum(None); serde::Serialize::serialize(&address_checksum, s) } @@ -51,7 +57,7 @@ pub struct Authorization { #[serde(rename_all = "camelCase")] pub struct UserOperationV07 { #[serde(serialize_with = "as_checksum_addr")] - pub sender: Address, + pub sender: AccountAddress, pub nonce: U256, pub factory: Option
, pub factory_data: Option, @@ -76,7 +82,7 @@ impl UserOperationV07 { &self, entry_point: &Address, chain_id: u64, - ) -> eyre::Result { + ) -> UserOperationHash { get_user_operation_hash_v07(self, entry_point, chain_id) } } @@ -85,9 +91,8 @@ impl UserOperationV07 { pub fn mock() -> Self { use std::str::FromStr; - let sender = "0xa3aBDC7f6334CD3EE466A115f30522377787c024" - .parse::
() - .unwrap(); + let sender = + address!("a3aBDC7f6334CD3EE466A115f30522377787c024").into(); let nonce = U256::from(16); let factory: Option
= None; let factory_data: Option = None; diff --git a/crates/yttrium/src/user_operation/hash.rs b/crates/yttrium/src/user_operation/hash.rs index bc2e1539..eeb95697 100644 --- a/crates/yttrium/src/user_operation/hash.rs +++ b/crates/yttrium/src/user_operation/hash.rs @@ -12,9 +12,9 @@ pub fn get_user_operation_hash_v07( user_operation: &UserOperationV07, entry_point: &Address, chain_id: u64, -) -> eyre::Result { +) -> UserOperationHash { let packed_user_operation = { - let packed = pack_v07::pack_user_operation_v07(user_operation)?; + let packed = pack_v07::pack_user_operation_v07(user_operation); println!("packed: {:?}", packed); keccak256(packed) }; @@ -32,8 +32,7 @@ pub fn get_user_operation_hash_v07( let encoded: Bytes = abi_encoded.into(); let hash_bytes = keccak256(encoded); let hash = B256::from_slice(hash_bytes.as_slice()); - let user_op_hash = UserOperationHash(hash); - Ok(user_op_hash) + UserOperationHash(hash) } #[cfg(test)] @@ -41,24 +40,19 @@ mod tests { use super::*; #[test] - fn test_get_user_operation_hash_v07() -> eyre::Result<()> { + fn test_get_user_operation_hash_v07() { let expected_hash = "0xa1ea19d934f05fc2d725f2be8452ad7e2f29d9747674045ea366a320b782411d"; let user_operation = UserOperationV07::mock(); - let entry_point = - "0x0000000071727De22E5E9d8BAf0edAc6f37da032".parse::
()?; + let entry_point = "0x0000000071727De22E5E9d8BAf0edAc6f37da032" + .parse::
() + .unwrap(); let chain_id = 11155111; let hash = get_user_operation_hash_v07( &user_operation, &entry_point, chain_id, - )?; - println!("hash: {:?}", hash); - eyre::ensure!( - format!("{}", hash.0) == expected_hash, - "hash should be {}", - expected_hash ); - Ok(()) + assert_eq!(hash.0.to_string(), expected_hash); } } diff --git a/crates/yttrium/src/user_operation/hash/pack_v07.rs b/crates/yttrium/src/user_operation/hash/pack_v07.rs index a77b674f..b758d24d 100644 --- a/crates/yttrium/src/user_operation/hash/pack_v07.rs +++ b/crates/yttrium/src/user_operation/hash/pack_v07.rs @@ -1,5 +1,5 @@ use crate::user_operation::UserOperationV07; -use alloy::primitives::Address; +use alloy::sol_types::SolValue; pub mod combine; pub mod hashed_call_data; @@ -8,55 +8,26 @@ pub mod hashed_paymaster_and_data; pub mod max_priority_fee_per_gas_and_max_fee_per_gas; pub mod verificaction_gas_limit_and_call_gas_limit; -pub fn pack_user_operation_v07( - user_operation: &UserOperationV07, -) -> eyre::Result> { - println!( - "pack_user_operation_v07 user_operation: {:?}", - user_operation.clone() - ); - +pub fn pack_user_operation_v07(user_operation: &UserOperationV07) -> Vec { let hashed_init_code = - hashed_init_code::get_hashed_init_code(user_operation)?; - println!("hashed_init_code: {:?}", hashed_init_code); + hashed_init_code::get_hashed_init_code(user_operation); let hashed_call_data = - hashed_call_data::get_hashed_call_data(user_operation)?; - println!("hashed_call_data: {:?}", hashed_call_data); + hashed_call_data::get_hashed_call_data(user_operation); let hashed_paymaster_and_data = hashed_paymaster_and_data::get_hashed_paymaster_and_data( user_operation, - )?; - println!("hashed_paymaster_and_data: {:?}", hashed_paymaster_and_data); - - use alloy::sol_types::SolValue; + ); let verificaction_gas_limit_and_call_gas_limit_item = - verificaction_gas_limit_and_call_gas_limit::get_verificaction_gas_limit_and_call_gas_limit(user_operation)?; - println!( - "verificaction_gas_limit_and_call_gas_limit_item: {:?}", - verificaction_gas_limit_and_call_gas_limit_item - ); + verificaction_gas_limit_and_call_gas_limit::get_verificaction_gas_limit_and_call_gas_limit(user_operation); let max_priority_fee_per_gas_and_max_fee_per_gas_item = - max_priority_fee_per_gas_and_max_fee_per_gas::get_max_priority_fee_per_gas_and_max_fee_per_gas(user_operation)?; - println!( - "max_priority_fee_per_gas_and_max_fee_per_gas_item: {:?}", - max_priority_fee_per_gas_and_max_fee_per_gas_item - ); + max_priority_fee_per_gas_and_max_fee_per_gas::get_max_priority_fee_per_gas_and_max_fee_per_gas(user_operation); - let items: ( - Address, - alloy::primitives::Uint<256, 4>, - alloy::primitives::FixedBytes<32>, - alloy::primitives::FixedBytes<32>, - alloy::primitives::FixedBytes<32>, - alloy::primitives::Uint<256, 4>, - alloy::primitives::FixedBytes<32>, - alloy::primitives::FixedBytes<32>, - ) = ( - user_operation.sender as Address, + let items = ( + user_operation.sender.to_address(), user_operation.nonce, hashed_init_code, hashed_call_data, @@ -66,10 +37,7 @@ pub fn pack_user_operation_v07( hashed_paymaster_and_data, ); - let encoded = items.abi_encode(); - println!("encoded: {:?}", encoded.clone()); - - Ok(encoded) + items.abi_encode() } #[cfg(test)] @@ -77,24 +45,17 @@ mod tests { use super::*; #[test] - fn test_pack_user_operation_v07() -> eyre::Result<()> { + fn test_pack_user_operation_v07() { let expected_packed_user_operation_hex = "0x000000000000000000000000a3abdc7f6334cd3ee466a115f30522377787c0240000000000000000000000000000000000000000000000000000000000000010c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a4700a8139e8d993db78f1d6b8682c7dcf9d4ef0b49b8bf883dc0a22a45b7aa7da2c00000000000000000000000000010b2500000000000000000000000000013880000000000000000000000000000000000000000000000000000000000000d9a900000000000000000000000043d4ca3500000000000000000000000417bbd4f1fc0dffa735c71f138a00eaaafa56834aebf784e3e446612810f3f325cfb8eda9"; let user_operation = UserOperationV07::mock(); - let packed_user_operation = pack_user_operation_v07(&user_operation)?; - println!("packed_user_operation: {:?}", packed_user_operation); + let packed_user_operation = pack_user_operation_v07(&user_operation); - let packed_user_operation_hex = - hex::encode(packed_user_operation.clone()); - println!("packed_user_operation_hex: {:?}", packed_user_operation_hex); + let packed_user_operation_hex = hex::encode(&packed_user_operation); - eyre::ensure!( - format!("0x{}", packed_user_operation_hex) - == expected_packed_user_operation_hex, - "packed_user_operation_hex should be {:?}", - expected_packed_user_operation_hex + assert_eq!( + format!("0x{}", packed_user_operation_hex), + expected_packed_user_operation_hex, ); - - Ok(()) } } diff --git a/crates/yttrium/src/user_operation/hash/pack_v07/combine.rs b/crates/yttrium/src/user_operation/hash/pack_v07/combine.rs index bb4cd1f4..9a186065 100644 --- a/crates/yttrium/src/user_operation/hash/pack_v07/combine.rs +++ b/crates/yttrium/src/user_operation/hash/pack_v07/combine.rs @@ -1,41 +1,25 @@ -use alloy::primitives::{Uint, B256}; -use std::str::FromStr; +use alloy::primitives::{B256, U256}; -pub fn combine_and_trim_first_16_bytes( - items: Vec>, -) -> eyre::Result { - let items_bytes_hex = items - .iter() - .map(|item| item.to_be_bytes_vec()[16..].to_vec()) - .map(hex::encode) - .collect::>(); - println!("items_bytes_hex: {:?}", items_bytes_hex); - - let combined = items_bytes_hex.join(""); - println!("combined: {:?}", combined); - - let result = B256::from_str(&combined)?; - - Ok(result) +pub fn combine_and_trim_first_16_bytes(item1: U256, item2: U256) -> B256 { + let mut vec = Vec::with_capacity(32); + vec.extend_from_slice(&item1.to_be_bytes_vec()[16..]); + vec.extend_from_slice(&item2.to_be_bytes_vec()[16..]); + B256::from_slice(&vec) } #[cfg(test)] mod tests { use super::*; + use alloy::primitives::b256; #[test] - fn test_combine_and_trim_first_16_bytes() -> eyre::Result<()> { - let expected_result = B256::from_str( - "0000000000000000000000000000000100000000000000000000000000000002", - )?; - let items = vec![Uint::<256, 4>::from(1), Uint::<256, 4>::from(2)]; - let result = combine_and_trim_first_16_bytes(items)?; - println!("result: {:?}", result); - eyre::ensure!( - result == expected_result, - "result should be {}", - expected_result + fn test_combine_and_trim_first_16_bytes() { + let expected_result = b256!( + "0000000000000000000000000000000100000000000000000000000000000002" ); - Ok(()) + let item1 = U256::from(1); + let item2 = U256::from(2); + let result = combine_and_trim_first_16_bytes(item1, item2); + assert_eq!(result, expected_result); } } diff --git a/crates/yttrium/src/user_operation/hash/pack_v07/hashed_call_data.rs b/crates/yttrium/src/user_operation/hash/pack_v07/hashed_call_data.rs index 4af26568..d05e5277 100644 --- a/crates/yttrium/src/user_operation/hash/pack_v07/hashed_call_data.rs +++ b/crates/yttrium/src/user_operation/hash/pack_v07/hashed_call_data.rs @@ -1,16 +1,8 @@ use crate::user_operation::UserOperationV07; use alloy::primitives::{keccak256, B256}; -pub fn get_hashed_call_data( - user_operation: &UserOperationV07, -) -> eyre::Result { - let hashed_call_data = { - let call_data = user_operation.clone().call_data; - keccak256(call_data) - }; - let hashed_call_data_hex = hex::encode(hashed_call_data); - println!("hashed_call_data_hex: {:?}", hashed_call_data_hex); - Ok(hashed_call_data) +pub fn get_hashed_call_data(user_operation: &UserOperationV07) -> B256 { + keccak256(&user_operation.call_data) } #[cfg(test)] @@ -18,17 +10,11 @@ mod tests { use super::*; #[test] - fn test_get_hashed_call_data() -> eyre::Result<()> { + fn test_get_hashed_call_data() { let expected_hashed_call_data_hex = "0x0a8139e8d993db78f1d6b8682c7dcf9d4ef0b49b8bf883dc0a22a45b7aa7da2c"; let user_operation = UserOperationV07::mock(); - let hashed_call_data = get_hashed_call_data(&user_operation)?; - println!("hashed_call_data: {:?}", hashed_call_data); - eyre::ensure!( - format!("{}", hashed_call_data) == expected_hashed_call_data_hex, - "hashed_call_data should be {}", - expected_hashed_call_data_hex - ); - Ok(()) + let hashed_call_data = get_hashed_call_data(&user_operation); + assert_eq!(hashed_call_data.to_string(), expected_hashed_call_data_hex,); } } diff --git a/crates/yttrium/src/user_operation/hash/pack_v07/hashed_init_code.rs b/crates/yttrium/src/user_operation/hash/pack_v07/hashed_init_code.rs index e5a400fb..18963b5f 100644 --- a/crates/yttrium/src/user_operation/hash/pack_v07/hashed_init_code.rs +++ b/crates/yttrium/src/user_operation/hash/pack_v07/hashed_init_code.rs @@ -1,9 +1,7 @@ use crate::user_operation::UserOperationV07; use alloy::primitives::{keccak256, B256}; -pub fn get_hashed_init_code( - user_operation: &UserOperationV07, -) -> eyre::Result { +pub fn get_hashed_init_code(user_operation: &UserOperationV07) -> B256 { let uo = user_operation.clone(); let value_vec = if let (Some(factory), Some(factory_data)) = (uo.factory, uo.factory_data.clone()) @@ -19,10 +17,7 @@ pub fn get_hashed_init_code( bytes_vec }; - let hashed_init_code = keccak256(value_vec); - println!("hashed_init_code: {:?}", hashed_init_code); - - Ok(hashed_init_code) + keccak256(value_vec) } #[cfg(test)] @@ -30,17 +25,11 @@ mod tests { use super::*; #[test] - fn test_get_hashed_init_code() -> eyre::Result<()> { + fn test_get_hashed_init_code() { let expected_hashed_init_code_hex = "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470"; let user_operation = UserOperationV07::mock(); - let hashed_init_code = get_hashed_init_code(&user_operation)?; - println!("hashed_init_code: {:?}", hashed_init_code); - eyre::ensure!( - format!("{}", hashed_init_code) == expected_hashed_init_code_hex, - "hashed_init_code should be {}", - expected_hashed_init_code_hex - ); - Ok(()) + let hashed_init_code = get_hashed_init_code(&user_operation); + assert_eq!(hashed_init_code.to_string(), expected_hashed_init_code_hex); } } diff --git a/crates/yttrium/src/user_operation/hash/pack_v07/hashed_paymaster_and_data.rs b/crates/yttrium/src/user_operation/hash/pack_v07/hashed_paymaster_and_data.rs index 76193b09..77b9e38f 100644 --- a/crates/yttrium/src/user_operation/hash/pack_v07/hashed_paymaster_and_data.rs +++ b/crates/yttrium/src/user_operation/hash/pack_v07/hashed_paymaster_and_data.rs @@ -1,70 +1,46 @@ use crate::user_operation::UserOperationV07; -use alloy::primitives::{keccak256, Bytes, Uint, B256, U256}; -use std::str::FromStr; - -fn trim_first_16_bytes_or_default(item: Option>) -> String { - let item = item.unwrap_or(U256::from(0)); - - let item_bytes_vec: Vec = item.to_be_bytes_vec()[16..].to_vec(); - - hex::encode(item_bytes_vec) -} - -fn get_data(user_operation: &UserOperationV07) -> eyre::Result { - let uo: UserOperationV07 = user_operation.clone(); - - let data = if let Some(paymaster) = uo.paymaster { - println!("paymaster: {:?}", paymaster); - - let paymaster_hex = format!("{}", paymaster); - - let paymaster_verification_gas_limit_hex = - trim_first_16_bytes_or_default(uo.paymaster_verification_gas_limit); - - let paymaster_post_op_gas_limit_hex = - trim_first_16_bytes_or_default(uo.paymaster_post_op_gas_limit); - - let paymaster_data = { - let paymaster_data = uo.paymaster_data.clone().unwrap_or_default(); - - let paymaster_data_hex = - format!("{:?}", paymaster_data)[2..].to_string(); - println!("paymaster_data_hex: {:?}", paymaster_data_hex); - - paymaster_data_hex - }; - - let combined = format!( - "{}{}{}{}", - paymaster_hex, - paymaster_verification_gas_limit_hex, - paymaster_post_op_gas_limit_hex, - paymaster_data - ); - println!("combined: {:?}", combined); - - combined +use alloy::primitives::{keccak256, Bytes, B256}; + +fn get_data(user_operation: &UserOperationV07) -> Bytes { + if let Some(paymaster) = user_operation.paymaster { + let address = paymaster.into_iter(); + + let paymaster_verification_gas_limit = user_operation + .paymaster_verification_gas_limit + .unwrap_or_default() + .to_be_bytes_vec() + .into_iter() + .skip(16); + + let paymaster_post_op_gas_limit = user_operation + .paymaster_post_op_gas_limit + .unwrap_or_default() + .to_be_bytes_vec() + .into_iter() + .skip(16); + + let paymaster_data = user_operation + .paymaster_data + .as_ref() + .unwrap_or_default() + .iter() + .copied(); + + address + .chain(paymaster_verification_gas_limit) + .chain(paymaster_post_op_gas_limit) + .chain(paymaster_data) + .collect() } else { - "".to_string() - }; - - let data = data.strip_prefix("0x").unwrap(); - - let bytes = Bytes::from_str(data)?; - - Ok(bytes) + Bytes::new() + } } pub fn get_hashed_paymaster_and_data( user_operation: &UserOperationV07, -) -> eyre::Result { - let data = get_data(user_operation)?; - println!("data: {:?}", data); - - let hashed = keccak256(data); - println!("hashed: {:?}", hashed); - - Ok(hashed) +) -> B256 { + let data = get_data(user_operation); + keccak256(data) } #[cfg(test)] @@ -72,18 +48,14 @@ mod tests { use super::*; #[test] - fn test_get_hashed_paymaster_and_data() -> eyre::Result<()> { + fn test_get_hashed_paymaster_and_data() { let expected_hashed_paymaster_and_data_hex = "0xfc0dffa735c71f138a00eaaafa56834aebf784e3e446612810f3f325cfb8eda9"; let user_operation = UserOperationV07::mock(); let hashed_paymaster_and_data = - get_hashed_paymaster_and_data(&user_operation)?; - println!("hashed_paymaster_and_data: {:?}", hashed_paymaster_and_data); - eyre::ensure!( - format!("{}", hashed_paymaster_and_data) - == expected_hashed_paymaster_and_data_hex, - "hashed_paymaster_and_data should be {}", - expected_hashed_paymaster_and_data_hex + get_hashed_paymaster_and_data(&user_operation); + assert_eq!( + hashed_paymaster_and_data.to_string(), + expected_hashed_paymaster_and_data_hex, ); - Ok(()) } } diff --git a/crates/yttrium/src/user_operation/hash/pack_v07/max_priority_fee_per_gas_and_max_fee_per_gas.rs b/crates/yttrium/src/user_operation/hash/pack_v07/max_priority_fee_per_gas_and_max_fee_per_gas.rs index af4aab90..ce475416 100644 --- a/crates/yttrium/src/user_operation/hash/pack_v07/max_priority_fee_per_gas_and_max_fee_per_gas.rs +++ b/crates/yttrium/src/user_operation/hash/pack_v07/max_priority_fee_per_gas_and_max_fee_per_gas.rs @@ -3,36 +3,29 @@ use alloy::primitives::B256; pub fn get_max_priority_fee_per_gas_and_max_fee_per_gas( user_operation: &UserOperationV07, -) -> eyre::Result { - let values = vec![ +) -> B256 { + super::combine::combine_and_trim_first_16_bytes( user_operation.max_priority_fee_per_gas, user_operation.max_fee_per_gas, - ]; - let combined = super::combine::combine_and_trim_first_16_bytes(values)?; - Ok(combined) + ) } #[cfg(test)] mod tests { use super::*; + use alloy::primitives::fixed_bytes; #[test] - fn test_get_max_priority_fee_per_gas_and_max_fee_per_gas( - ) -> eyre::Result<()> { - let expected_max_priority_fee_per_gas_and_max_fee_per_gas_hex = "0x00000000000000000000000043d4ca3500000000000000000000000417bbd4f1"; + fn test_get_max_priority_fee_per_gas_and_max_fee_per_gas() { + let expected_max_priority_fee_per_gas_and_max_fee_per_gas = fixed_bytes!( + "00000000000000000000000043d4ca3500000000000000000000000417bbd4f1" + ); let user_operation = UserOperationV07::mock(); let max_priority_fee_per_gas_and_max_fee_per_gas = - get_max_priority_fee_per_gas_and_max_fee_per_gas(&user_operation)?; - println!( - "max_priority_fee_per_gas_and_max_fee_per_gas: {:?}", - max_priority_fee_per_gas_and_max_fee_per_gas - ); - eyre::ensure!( - format!("{}", max_priority_fee_per_gas_and_max_fee_per_gas) - == expected_max_priority_fee_per_gas_and_max_fee_per_gas_hex, - "max_priority_fee_per_gas_and_max_fee_per_gas should be {}", - expected_max_priority_fee_per_gas_and_max_fee_per_gas_hex + get_max_priority_fee_per_gas_and_max_fee_per_gas(&user_operation); + assert_eq!( + max_priority_fee_per_gas_and_max_fee_per_gas, + expected_max_priority_fee_per_gas_and_max_fee_per_gas, ); - Ok(()) } } diff --git a/crates/yttrium/src/user_operation/hash/pack_v07/verificaction_gas_limit_and_call_gas_limit.rs b/crates/yttrium/src/user_operation/hash/pack_v07/verificaction_gas_limit_and_call_gas_limit.rs index 219f7069..2cc84f6c 100644 --- a/crates/yttrium/src/user_operation/hash/pack_v07/verificaction_gas_limit_and_call_gas_limit.rs +++ b/crates/yttrium/src/user_operation/hash/pack_v07/verificaction_gas_limit_and_call_gas_limit.rs @@ -4,37 +4,29 @@ use alloy::primitives::B256; pub fn get_verificaction_gas_limit_and_call_gas_limit( user_operation: &UserOperationV07, -) -> eyre::Result { - let values = vec![ +) -> B256 { + combine_and_trim_first_16_bytes( user_operation.verification_gas_limit, user_operation.call_gas_limit, - ]; - let combined = combine_and_trim_first_16_bytes(values)?; - Ok(combined) + ) } #[cfg(test)] mod tests { use super::*; + use alloy::primitives::fixed_bytes; #[test] - fn test_get_verificaction_gas_limit_and_call_gas_limit() -> eyre::Result<()> - { - let expected_verification_gas_limit_and_call_gas_limit_hex = - "0x00000000000000000000000000010b2500000000000000000000000000013880"; + fn test_get_verificaction_gas_limit_and_call_gas_limit() { + let expected_verification_gas_limit_and_call_gas_limit = fixed_bytes!( + "00000000000000000000000000010b2500000000000000000000000000013880" + ); let user_operation = UserOperationV07::mock(); let verification_gas_limit_and_call_gas_limit = - get_verificaction_gas_limit_and_call_gas_limit(&user_operation)?; - println!( - "verification_gas_limit_and_call_gas_limit: {:?}", - verification_gas_limit_and_call_gas_limit - ); - eyre::ensure!( - format!("{}", verification_gas_limit_and_call_gas_limit) - == expected_verification_gas_limit_and_call_gas_limit_hex, - "verification_gas_limit_and_call_gas_limit should be {}", - expected_verification_gas_limit_and_call_gas_limit_hex + get_verificaction_gas_limit_and_call_gas_limit(&user_operation); + assert_eq!( + verification_gas_limit_and_call_gas_limit, + expected_verification_gas_limit_and_call_gas_limit, ); - Ok(()) } }