├── .gitattributes ├── .github └── workflows │ └── test.yml ├── .gitignore ├── .gitmodules ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── bind.sh ├── contracts ├── MevWalletV1.sol ├── MevWalletV1Factory.sol ├── V0 │ ├── MevWalletV0.sol │ └── MevWalletV0Factory.sol └── utils │ ├── Drain.sol │ └── IERC20.sol ├── devenv.sh ├── foundry.toml ├── remappings.txt ├── script ├── DeployDrain.s.sol ├── DeployFactoryV1.s.sol ├── DeployImplV1.s.sol ├── DeployProxyV1.s.sol └── V0 │ ├── DeployFactoryV0.sol │ ├── DeployImplV0.sol │ └── DeployProxyV0.sol ├── src ├── bindings │ ├── deploy_drain.rs │ ├── deploy_factory_v0.rs │ ├── deploy_factory_v1.rs │ ├── deploy_impl_v0.rs │ ├── deploy_impl_v1.rs │ ├── deploy_proxy_v0.rs │ ├── deploy_proxy_v1.rs │ ├── drain.rs │ ├── i_mev_weth.rs │ ├── ierc20.rs │ ├── mev_wallet_v0.rs │ ├── mev_wallet_v0_factory.rs │ ├── mev_wallet_v1.rs │ ├── mev_wallet_v1_factory.rs │ ├── mevitize.rs │ ├── mod.rs │ └── mwb.rs ├── deploy.rs ├── lib.rs ├── macros.rs └── tx │ ├── builder.rs │ ├── dutch.rs │ ├── ext.rs │ ├── mod.rs │ └── types.rs ├── test.sh ├── test ├── MWB.sol └── MevWalletV1.t.sol └── tests └── integration.rs /.gitattributes: -------------------------------------------------------------------------------- 1 | /src/bindings/** binary 2 | /Cargo.toml binary 3 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: test 2 | 3 | on: workflow_dispatch 4 | 5 | env: 6 | FOUNDRY_PROFILE: ci 7 | 8 | jobs: 9 | check: 10 | strategy: 11 | fail-fast: true 12 | 13 | name: Foundry project 14 | runs-on: ubuntu-latest 15 | steps: 16 | - uses: actions/checkout@v3 17 | with: 18 | submodules: recursive 19 | 20 | - name: Install Foundry 21 | uses: foundry-rs/foundry-toolchain@v1 22 | with: 23 | version: nightly 24 | 25 | - name: Run Forge build 26 | run: | 27 | forge --version 28 | forge build --sizes 29 | id: build 30 | 31 | - name: Run Forge tests 32 | run: | 33 | forge test -vvv 34 | id: test 35 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiler files 2 | cache/ 3 | out/ 4 | 5 | # Ignores development broadcast logs 6 | /broadcast 7 | /broadcast/*/31337/ 8 | /broadcast/**/dry-run/ 9 | 10 | # Dotenv file 11 | .env 12 | 13 | # cargo 14 | **/target 15 | Cargo.lock 16 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "lib/forge-std"] 2 | path = lib/forge-std 3 | url = https://github.com/foundry-rs/forge-std 4 | branch = v1.2.0 5 | [submodule "lib/mev-weth"] 6 | path = lib/mev-weth 7 | url = https://github.com/prestwich/mev-weth -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "mev-wallet" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | async-trait = "0.1.62" 10 | ethers = { version = "2.0.0", features = ["eip712"] } 11 | futures-util = "0.3.26" 12 | once_cell = "1.17.0" 13 | serde = { version = "1.0.150", features = ["derive"] } 14 | thiserror = "1.0.37" 15 | 16 | [dev-dependencies] 17 | serde_json = "1.0.91" 18 | tokio = { version = "1.23.0", features = ["macros"] } 19 | tokio-test = "0.4.2" 20 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2022 2 | 3 | Permission is hereby granted, free of charge, to any 4 | person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the 6 | Software without restriction, including without 7 | limitation the rights to use, copy, modify, merge, 8 | publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software 10 | is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice 14 | shall be included in all copies or substantial portions 15 | of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 18 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 19 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 21 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 24 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | DEALINGS IN THE SOFTWARE. 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## MevWallet 2 | 3 | MevWallet is a smart contract wallet that allows the user to capture MEV from 4 | Searchers, or create MEV on purpose. 5 | 6 | This repo contains the solidity contracts in `contracts/`, deployment tooling 7 | in `script/`, and a Rust library for interacting with the contracts in `src/`. 8 | 9 | THIS IS UNAUDITED CODE. USE AT YOUR OWN RISK 10 | 11 | ### Wait what? 12 | 13 | MevWallet is a smart contract wallet that allows the user to capture MEV from 14 | Searchers, or create MEV on purpose. 15 | 16 | ### Ok yeah I heard you the first time, what does that mean? 17 | 18 | User transactions pay fees, and may generate MEV. Searchers take the MEV from 19 | the user, and pay it to block proposers. This means that users are effectively 20 | paying block creators twice. Once via MEV, and once via the regular transaction 21 | fee. 22 | 23 | MevWallet changes this relationship. Instead of paying block proposers via tx 24 | fees, users make transactions via the MevWallet. These transaction pay no tx 25 | fee. Instead, MevWallet transactions create MEV, and allow Searchers to pay tx 26 | fees on the user's behalf. This allows users to more effectively and efficiently 27 | price their transactions. 28 | 29 | It also allows one more (**really cool** thing). It lets the user give the 30 | Searcher _negative_ MEV. This means that the Searcher's bundle must _pay the 31 | user_ for the right to broadcast the transaction. If the user is creating a 32 | large amount of MEV, they can force the Searcher to give them a cut of that 33 | MEV. The user gets to use this to cover their tx fees (cool!) and potentially 34 | end up paying **less than 0** tx fees (coooler!). 35 | 36 | ### Addresses 37 | 38 | - MevWeth: `0x00000000008C43efC014746c230049e330039Cb3` 39 | - MevWalletV1 Implementation: `0x0000000000c08718718B974D644B098C19bd0064` 40 | - MevWalletV1 ProxyFactory: `0x9248B5e672e1880af34068C0FaE18D30c26D05Fb` 41 | 42 | Didn't bother to grind an address for the proxy factory :) 43 | 44 | ### How do I integrate it? 45 | 46 | This repo has a Rust library for working with MevTxns. I'm also publishing a 47 | typescript library using `ethers.js`. 48 | 49 | Use `MevTxBuilder` to build a txn: 50 | 51 | ```rust 52 | let mev_tx: MevTx = MevTxBuilder::new() 53 | .from(&wallet_contract) 54 | .to(weth_address) 55 | .call( 56 | // WOW! you can use any `abigen!` generated call here! 57 | // (you can also you `.data(some_bytes)` to set the raw calldata 58 | ierc20::TransferCall { 59 | recipient, 60 | amount, 61 | } 62 | ) 63 | .tip(1_000_000_000) 64 | .populate() 65 | .await? 66 | .build(); 67 | let signed_mev_tx = mev_tx.sign(wallet_address, signer).await?; 68 | 69 | // You can convert from an ether-rs tx too! 70 | // Middleware is required to resolve typed tx ENS name. This can often be a 71 | // dummy middleware. 72 | let typed_tx = weth.transfer(recipient, amount); 73 | let signed_mev_tx = MevTxBuilder::from_typed_tx(&wallet_contract, typed_tx) 74 | .await? 75 | .nonce(5) 76 | // adding a signer signs the mev tx when it's built :) 77 | .with_signer(signer) 78 | // Defaults to the signer's chain_id 79 | .with_chain_id(31337) 80 | .populate() 81 | .await? 82 | .build(wallet_address) 83 | .await?; 84 | ``` 85 | 86 | Searchers should can use any middleware to send a `MevTx`: 87 | 88 | ```rust 89 | let signed_mev_tx = serde_json::from_str(a_serialized_tx)?; 90 | 91 | // `into_call` converts into a normal `ContractCall` wrapping the MevTx 92 | // it sets value to the mev_tx_value, and 93 | let pending = signed_mev_tx 94 | .into_call(arc_middleware.clone()) 95 | // modify gas or w/e here :) 96 | // it is STRONGLY ADVISED that you use `.call()` to simulate 97 | // as we do not currently check the code at the wallet address 98 | .send() 99 | .await?; 100 | ``` 101 | 102 | You can also use this lib to deploy a new proxy contract: 103 | 104 | ```rust 105 | // set owner to msg.sender 106 | let my_wallet = MevWallet::new_proxy(middleware, salt).await?; 107 | 108 | // set owner to an arbitrary address 109 | let my_wallet = MevWallet::new_proxy(middleware, salt, owner_address).await?; 110 | ``` 111 | 112 | However, if doing it manually might be easier to use forge 113 | 114 | ```sh 115 | forge script --broadcast $FORGE_SIGNER_INFO DeployProxyV0 --sig "run(bytes32)" $SALT -vvvvvv 116 | ``` 117 | 118 | ### MevTx Layout 119 | 120 | MevTxns are very similar to normal txns, and are signed with EIP712. 121 | 122 | - `to` -- target address for the call. 123 | - `data` -- data for the call. 124 | - `value` -- value that should be passed in the call. 125 | - `delegate` -- `true` for delegatecall (but be careful!). Must be false if 126 | value is non-0 127 | - `tip` -- MEV created by the tx, in wei, can be negative 128 | - `maxBaseFee` -- call should not be executed above this basefee (may be `0`, 129 | for no max) 130 | - `deadline` -- call should not be executed after this timestamp (may be `0`, 131 | for no deadline) 132 | - `nonce` -- nonce for the MevTx, as set in `MevWallet` state 133 | 134 | ### Repo Setup 135 | 136 | `$ git submodule update --init --recursive` 137 | 138 | ### Running Tests 139 | 140 | Integrations tests are a bit funky right now. Run `./devenv.sh` to start an 141 | anvil instance. Then run `cargo test` in another terminal. Use ctrl+C to exist 142 | the anvil instance when necessary. 143 | 144 | ### Regenerating contract bindings. 145 | 146 | It is NOT RECOMMENDED that you make contract changes. If you must, make sure to 147 | run `./bind.sh` to generate the rust bindings. 148 | 149 | ### Deploying MevWallet 150 | 151 | If working on chain with a `MevWeth` deployment, use these scripts (including your RPC and signer details): 152 | 153 | ```sh 154 | # deploy the implementation to a new chain 155 | forge script DeployImplV0 156 | # deploy the proxy factory to a new chain 157 | forge script DeployFactoryV0 158 | # deploy a new proxy wallet 159 | forge script DeployProxyV0.sol --sig "run(bytes32)" $YOUR_SALT_BYTES32 160 | ``` 161 | 162 | If working on a fresh anvil instance or a chain without `MevWeth`, follow the 163 | [MevWeth deployment instructions.](https://github.com/blunt-instruments/MevWeth#i-want-mevweth-on-my-chain) 164 | Then deploy MevWallet as above :) 165 | -------------------------------------------------------------------------------- /bind.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | rm -rf out/bindings 4 | rm -rf src/bindings 5 | /Users/james/devel/foundry/target/debug/forge bind --module --via-ir 6 | cp -r ./out/bindings ./src/ -------------------------------------------------------------------------------- /contracts/MevWalletV1.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity ^0.8.17; 3 | 4 | import {Mevitize} from "mev-weth/Mevitize.sol"; 5 | 6 | contract MevWalletV1 is Mevitize { 7 | error ProvideValue(uint256); // 0x73883387 8 | error HighBaseFee(uint256); // 0x74878d58 9 | error WrongSigner(address); // 0x32c15fc2 10 | error Reverted(bytes); // 0xa8159920 11 | error NotBefore(uint64); // 0x08567e55 12 | error UsedNonce(uint256); // 0x6ac964b0 13 | error MissingNonce(uint256); // 0x299aa731 14 | 15 | error PermanentlyInvalid(); // 0xa04d981f 16 | 17 | // 0xbcf6a68a2f901be4a23a41b53acd7697893a7e34def4e28acba584da75283b67 18 | event Executed(uint256 indexed nonce); 19 | 20 | // 0x5679fb6ec38d3c67731b4def49181a8fbbb334cda5c263b0993e50cfe699d4e8 21 | bytes32 public constant TX_TYPEHASH = keccak256( 22 | "MevTx(address to,bytes data,int256 value,bool delegate,int256 tip,uint256 maxBaseFee,uint256 timing,uint256 nonce)" 23 | ); 24 | bytes32 public _DOMAIN_SEPARATOR; 25 | 26 | address public owner; 27 | uint256 public nonce; 28 | 29 | fallback() external payable {} 30 | receive() external payable {} 31 | 32 | constructor() { 33 | owner = address(0xff); // factor that, jerks 34 | } 35 | 36 | /** 37 | * @notice initializes the owner and domain separator 38 | */ 39 | function initialize(address newOwner) public { 40 | require(owner == address(0)); 41 | // Enforced because contracts cannot produce signatures 42 | uint256 s; 43 | assembly { 44 | s := extcodesize(newOwner) 45 | } 46 | require(s == 0, "No contract owner"); 47 | owner = newOwner; 48 | _DOMAIN_SEPARATOR = keccak256( 49 | abi.encode( 50 | keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"), 51 | keccak256("MevTx"), 52 | keccak256(bytes("1")), 53 | block.chainid, 54 | address(this) 55 | ) 56 | ); 57 | } 58 | 59 | /** 60 | * @notice onlyOwner does what it says on the tin 61 | */ 62 | modifier onlyOwner() { 63 | // we allow address(this) so that the wallet can be administered with 64 | // its own meta-tx 65 | require(msg.sender == owner || msg.sender == address(this)); 66 | _; 67 | } 68 | 69 | /** 70 | * @notice transferOwnership does what it says on the tin 71 | */ 72 | function transferOwnership(address newOwner) public onlyOwner { 73 | require(newOwner != address(0) && newOwner != address(this)); 74 | uint256 s; 75 | // Enforced because contracts cannot produce signatures 76 | assembly { 77 | s := extcodesize(newOwner) 78 | } 79 | require(s == 0, "No contract owner"); 80 | owner = newOwner; 81 | } 82 | 83 | /** 84 | * @notice checks the EIP-712 signsture 85 | */ 86 | function check712( 87 | address to, 88 | bytes memory data, 89 | int256 value, 90 | bool delegate, 91 | int256 tip, 92 | uint256 maxBaseFee, 93 | uint256 timing, 94 | uint256 n, 95 | uint8 v, 96 | bytes32 r, 97 | bytes32 s 98 | ) internal view { 99 | bytes32 hashStruct = 100 | keccak256(abi.encode(TX_TYPEHASH, to, keccak256(data), value, delegate, tip, maxBaseFee, timing, n)); 101 | bytes32 h = keccak256(abi.encodePacked("\x19\x01", _DOMAIN_SEPARATOR, hashStruct)); 102 | address signer = ecrecover(h, v, r, s); 103 | // signature must be valid 104 | if (signer == address(0)) revert PermanentlyInvalid(); 105 | // signature must be from owner 106 | if (signer != owner) revert WrongSigner(signer); 107 | } 108 | 109 | /** 110 | * @notice checks that the basefee is in user-acceptable range. 111 | */ 112 | function checkBaseFee(uint256 maxBaseFee) internal view { 113 | // if there's a limit on the basefee, it cannot be over that limit 114 | if (maxBaseFee != 0 && block.basefee > maxBaseFee) revert HighBaseFee(maxBaseFee); 115 | } 116 | 117 | /** 118 | * @notice checks that the block timestamp is in user-acceptable range. 119 | */ 120 | function checkTiming(uint256 timing) internal view { 121 | // Timing is encoded as `notBefore << 64 | notAfter` 122 | uint64 time = uint64(block.timestamp); 123 | uint64 notAfter = uint64(timing); 124 | uint64 notBefore = uint64(timing >> 64); 125 | // if notAfter is non-zero, timestamp cannot be after it 126 | if (notAfter != 0 && time > notAfter) { 127 | revert PermanentlyInvalid(); 128 | } 129 | // if notBefore is non-zero, timestamp cannot be before it 130 | if (notBefore != 0 && time < notBefore) { 131 | revert NotBefore(notBefore); 132 | } 133 | } 134 | 135 | /** 136 | * @notice checks that the value is as user specified. 137 | */ 138 | function checkValue(bool delegate, int256 value) internal view { 139 | // value cannot be negative 140 | if (value < 0) revert PermanentlyInvalid(); 141 | // delegate calls cannot have value 142 | if (delegate && value != 0) revert PermanentlyInvalid(); 143 | unchecked { 144 | // cast checked by previous if statement 145 | uint256 val = uint256(value); 146 | uint256 bal = address(this).balance; 147 | uint256 msgVal = msg.value; 148 | // becase bal cannot be less than msgval 149 | uint256 preBal = bal - msgVal; 150 | // if the value BEFORE getting the Searcher's input ETH was 151 | // sufficient, don't allow input ETH. This prevents the Searcher 152 | // from converting wallet MevWeth into ETH by providing extra value 153 | if (preBal >= val && msgVal != 0) revert ProvideValue(0); 154 | // if the value BEFORE getting the Searcher's input ETH was 155 | // insufficient, require the msg has the exact value necessary 156 | if (preBal < val) { 157 | // checked by the if statement 158 | uint256 deficit = val - preBal; 159 | if (msgVal != deficit) revert ProvideValue(deficit); 160 | } 161 | } 162 | } 163 | 164 | /** 165 | * @notice checks that the nonce is correct 166 | */ 167 | function checkNonce(uint256 n) internal view { 168 | uint256 _nonce = nonce; 169 | // Nonce cannot be 170 | if (n < _nonce) revert UsedNonce(_nonce); 171 | if (n > _nonce) revert MissingNonce(_nonce); 172 | // pass if equal 173 | } 174 | 175 | /** 176 | * @notice executes the meta-tx 177 | */ 178 | function execute(address to, bytes memory data, uint256 value, bool delegate) internal { 179 | bool success; 180 | // overwrite data because we don't need it anymore 181 | if (delegate) { 182 | (success, data) = to.delegatecall(data); 183 | } else { 184 | (success, data) = to.call{value: value}(data); 185 | } 186 | // okay this seems crazy but hear me out 187 | // MEV block builders already drop reverting txns. 188 | // This just makes it so they never get included at all 189 | // which is desirable for a metatx 190 | if (!success) { 191 | revert Reverted(data); 192 | } 193 | } 194 | 195 | /** 196 | * @notice execute a MEV-driven meta-transaction 197 | */ 198 | function mevTx( 199 | address to, 200 | bytes memory data, 201 | int256 value, 202 | bool delegate, 203 | int256 tip, 204 | uint256 maxBaseFee, 205 | uint256 timing, 206 | uint256 n, 207 | uint8 v, 208 | bytes32 r, 209 | bytes32 s 210 | ) external payable subsidize(tip + int256(msg.value)) { 211 | // check sig first, as this is most likely to produce reverts 212 | check712(to, data, value, delegate, tip, maxBaseFee, timing, n, v, r, s); 213 | 214 | // other condition of use checks 215 | if (to == address(0)) revert PermanentlyInvalid(); 216 | checkBaseFee(maxBaseFee); 217 | checkTiming(timing); 218 | checkValue(delegate, value); 219 | checkNonce(n); 220 | 221 | // re-entrancy protection 222 | nonce = type(uint256).max; 223 | 224 | // execute the tx 225 | execute(to, data, uint256(value), delegate); 226 | 227 | // emit executed, and incement nonce 228 | nonce = n + 1; 229 | emit Executed(n); 230 | } 231 | } 232 | -------------------------------------------------------------------------------- /contracts/MevWalletV1Factory.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity ^0.8.17; 3 | 4 | contract MevWalletV1Factory { 5 | error CreationFailed(); // 0xd786d393 6 | error InitFailed(bytes); // 0x225d0a58 7 | 8 | // 0x0070c3b37cbd33a8f91033a749a5e534a86edc8c2a4c6155b9c53e99ffd59437 9 | event Proxy(address); 10 | 11 | function createWallet(bytes32 salt, address owner) public returns (address) { 12 | // we commit to the owner so that deployment txns cannot be frontrun 13 | // (or rather, a frontrun will create _exactly_ the same state) 14 | salt = keccak256(abi.encode(salt, owner)); 15 | address p; 16 | assembly { 17 | let buf := mload(0x40) 18 | mstore(buf, 0x3d602880600a3d3981f3363d3d373d3d3d363d6ec08718718B974D644B098C19) 19 | mstore(add(buf, 0x20), 0xbd00645af43d82803e903d91602657fd5bf30000000000000000000000000000) 20 | 21 | p := create2(0, buf, 0x32, salt) 22 | } 23 | if (p == address(0)) revert CreationFailed(); 24 | 25 | bytes memory data = abi.encodeWithSignature("initialize(address)", [owner]); 26 | bool s; 27 | (s, data) = p.call(data); 28 | if (!s) revert InitFailed(data); 29 | emit Proxy(p); 30 | return p; 31 | } 32 | 33 | function createWallet(bytes32 salt) public returns (address) { 34 | return createWallet(salt, msg.sender); 35 | } 36 | } 37 | 38 | // 3d602d80600a3d3981f3363d3d373d3d3d363d 39 | // 63 c08718718B974D644B098C19 bd0064 40 | // 63 4096437C84E1B0927D5ED44F 45F6b3 41 | // 5af43d82803e903d91602b57fd5bf3 42 | -------------------------------------------------------------------------------- /contracts/V0/MevWalletV0.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity ^0.8.17; 3 | 4 | import {Mevitize} from "mev-weth/Mevitize.sol"; 5 | 6 | contract MevWalletV0 is Mevitize { 7 | error ProvideValue(uint256); // 0x73883387 8 | error HighBaseFee(uint256); // 0x74878d58 9 | error WrongSigner(address); // 0x32c15fc2 10 | error Reverted(bytes); // 0xa8159920 11 | error NotBefore(uint64); // 0x08567e55 12 | error UsedNonce(uint256); // 0x6ac964b0 13 | error MissingNonce(uint256); // 0x299aa731 14 | 15 | error PermanentlyInvalid(); // 0xa04d981f 16 | 17 | // 0xbcf6a68a2f901be4a23a41b53acd7697893a7e34def4e28acba584da75283b67 18 | event Executed(uint256 indexed nonce); 19 | 20 | // 0x5679fb6ec38d3c67731b4def49181a8fbbb334cda5c263b0993e50cfe699d4e8 21 | bytes32 public constant TX_TYPEHASH = keccak256( 22 | "MevTx(address to,bytes data,int256 value,bool delegate,int256 tip,uint256 maxBaseFee,uint256 timing,uint256 nonce)" 23 | ); 24 | bytes32 public _DOMAIN_SEPARATOR; 25 | 26 | address public owner; 27 | uint256 public nonce; 28 | 29 | fallback() external payable {} 30 | receive() external payable {} 31 | 32 | constructor() { 33 | owner = address(0xff); // factor that, jerks 34 | } 35 | 36 | /** 37 | * @notice initializes the owner and domain separator 38 | */ 39 | function initialize(address newOwner) public { 40 | require(owner == address(0)); 41 | // Enforced because contracts cannot produce signatures 42 | uint256 s; 43 | assembly { 44 | s := extcodesize(newOwner) 45 | } 46 | require(s == 0, "No contract owner"); 47 | owner = newOwner; 48 | _DOMAIN_SEPARATOR = keccak256( 49 | abi.encode( 50 | keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"), 51 | keccak256("MevTx"), 52 | keccak256(bytes("1")), 53 | block.chainid, 54 | address(this) 55 | ) 56 | ); 57 | } 58 | 59 | /** 60 | * @notice onlyOwner does what it says on the tin 61 | */ 62 | modifier onlyOwner() { 63 | // we allow address(this) so that the wallet can be administered with 64 | // its own meta-tx 65 | require(msg.sender == owner || msg.sender == address(this)); 66 | _; 67 | } 68 | 69 | /** 70 | * @notice transferOwnership does what it says on the tin 71 | */ 72 | function transferOwnership(address newOwner) public onlyOwner { 73 | require(newOwner != address(0) && newOwner != address(this)); 74 | uint256 s; 75 | // Enforced because contracts cannot produce signatures 76 | assembly { 77 | s := extcodesize(newOwner) 78 | } 79 | require(s == 0, "No contract owner"); 80 | owner = newOwner; 81 | } 82 | 83 | /** 84 | * @notice checks the EIP-712 signsture 85 | */ 86 | function check712( 87 | address to, 88 | bytes memory data, 89 | int256 value, 90 | bool delegate, 91 | int256 tip, 92 | uint256 maxBaseFee, 93 | uint256 timing, 94 | uint256 n, 95 | uint8 v, 96 | bytes32 r, 97 | bytes32 s 98 | ) internal view { 99 | bytes32 hashStruct = 100 | keccak256(abi.encode(TX_TYPEHASH, to, keccak256(data), value, delegate, tip, maxBaseFee, timing, n)); 101 | bytes32 h = keccak256(abi.encodePacked("\x19\x01", _DOMAIN_SEPARATOR, hashStruct)); 102 | address signer = ecrecover(h, v, r, s); 103 | // signature must be valid 104 | if (signer == address(0)) revert PermanentlyInvalid(); 105 | // signature must be from owner 106 | if (signer != owner) revert WrongSigner(signer); 107 | } 108 | 109 | /** 110 | * @notice checks that the basefee is in user-acceptable range. 111 | */ 112 | function checkBaseFee(uint256 maxBaseFee) internal view { 113 | // if there's a limit on the basefee, it cannot be over that limit 114 | if (maxBaseFee != 0 && block.basefee > maxBaseFee) revert HighBaseFee(maxBaseFee); 115 | } 116 | 117 | /** 118 | * @notice checks that the block timestamp is in user-acceptable range. 119 | */ 120 | function checkTiming(uint256 timing) internal view { 121 | // Timing is encoded as `notBefore << 64 | notAfter` 122 | uint64 time = uint64(block.timestamp); 123 | uint64 notAfter = uint64(timing); 124 | uint64 notBefore = uint64(timing >> 64); 125 | // if notAfter is non-zero, timestamp cannot be after it 126 | if (notAfter != 0 && time > notAfter) { 127 | revert PermanentlyInvalid(); 128 | } 129 | // if notBefore is non-zero, timestamp cannot be before it 130 | if (notBefore != 0 && time < notBefore) { 131 | revert NotBefore(notBefore); 132 | } 133 | } 134 | 135 | /** 136 | * @notice checks that the value is as user specified. 137 | */ 138 | function checkValue(bool delegate, int256 value) internal view { 139 | // value cannot be negative 140 | if (value < 0) revert PermanentlyInvalid(); 141 | // delegate calls cannot have value 142 | if (delegate && value != 0) revert PermanentlyInvalid(); 143 | // msg.value must be 0, or the committed amount 144 | if (value > 0 && msg.value != uint256(value)) revert ProvideValue(uint256(value)); 145 | } 146 | 147 | /** 148 | * @notice checks that the nonce is correct 149 | */ 150 | function checkNonce(uint256 n) internal view { 151 | uint256 _nonce = nonce; 152 | // Nonce cannot be 153 | if (n < _nonce) revert UsedNonce(_nonce); 154 | if (n > _nonce) revert MissingNonce(_nonce); 155 | // pass if equal 156 | } 157 | 158 | /** 159 | * @notice executes the meta-tx 160 | */ 161 | function execute(address to, bytes memory data, bool delegate) internal { 162 | bool success; 163 | // overwrite data because we don't need it anymore 164 | if (delegate) { 165 | (success, data) = to.delegatecall(data); 166 | } else { 167 | // safe to use msg.value as it has been checked in checkValue 168 | (success, data) = to.call{value: msg.value}(data); 169 | } 170 | // okay this seems crazy but hear me out 171 | // MEV block builders already drop reverting txns. 172 | // This just makes it so they never get included at all 173 | // which is desirable for a metatx 174 | if (!success) { 175 | revert Reverted(data); 176 | } 177 | } 178 | 179 | /** 180 | * @notice execute a MEV-driven meta-transaction 181 | */ 182 | function mevTx( 183 | address to, 184 | bytes memory data, 185 | int256 value, 186 | bool delegate, 187 | int256 tip, 188 | uint256 maxBaseFee, 189 | uint256 timing, 190 | uint256 n, 191 | uint8 v, 192 | bytes32 r, 193 | bytes32 s 194 | ) external payable subsidize(tip + value) { 195 | // check sig first, as this is most likely to produce reverts 196 | check712(to, data, value, delegate, tip, maxBaseFee, timing, n, v, r, s); 197 | 198 | // other condition of use checks 199 | if (to == address(0)) revert PermanentlyInvalid(); 200 | checkBaseFee(maxBaseFee); 201 | checkTiming(timing); 202 | checkValue(delegate, value); 203 | checkNonce(n); 204 | 205 | // re-entrancy protection 206 | nonce = type(uint256).max; 207 | 208 | // execute the tx 209 | execute(to, data, delegate); 210 | 211 | // emit executed, and incement nonce 212 | nonce = n + 1; 213 | emit Executed(n); 214 | } 215 | } 216 | -------------------------------------------------------------------------------- /contracts/V0/MevWalletV0Factory.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity ^0.8.17; 3 | 4 | contract MevWalletV0Factory { 5 | error CreationFailed(); // 0xd786d393 6 | error InitFailed(bytes); // 0x225d0a58 7 | 8 | // 0x0070c3b37cbd33a8f91033a749a5e534a86edc8c2a4c6155b9c53e99ffd59437 9 | event Proxy(address); 10 | 11 | function createWallet(bytes32 salt, address owner) public returns (address) { 12 | // we commit to the owner so that deployment txns cannot be frontrun 13 | // (or rather, a frontrun will create _exactly_ the same) state 14 | salt = keccak256(abi.encode(salt, owner)); 15 | address p; 16 | assembly { 17 | let buf := mload(0x40) 18 | mstore(buf, 0x3d602880600a3d3981f3363d3d373d3d3d363d6e8EaBBE9A46Fa87F0d1e41e62) 19 | mstore(add(buf, 0x20), 0xA96d505af43d82803e903d91602657fd5bf30000000000000000000000000000) 20 | 21 | p := create2(0, buf, 0x32, salt) 22 | } 23 | if (p == address(0)) revert CreationFailed(); 24 | 25 | bytes memory data = abi.encodeWithSignature("initialize(address)", [owner]); 26 | bool s; 27 | (s, data) = p.call(data); 28 | if (!s) revert InitFailed(data); 29 | emit Proxy(p); 30 | return p; 31 | } 32 | 33 | function createWallet(bytes32 salt) public returns (address) { 34 | return createWallet(salt, msg.sender); 35 | } 36 | } 37 | 38 | // 3d602d80600a3d3981f3363d3d373d3d3d363d 39 | // 6e 7Dcbd85Fc67915ad4bE7DAE2 66e268 40 | // 6e 8EaBBE9A46Fa87F0d1e41e62 A96d50 41 | // 5af43d82803e903d91602b57fd5bf3 42 | 43 | -------------------------------------------------------------------------------- /contracts/utils/Drain.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity ^0.8.17; 3 | 4 | import {Mevitize} from "mev-weth/Mevitize.sol"; 5 | import {IERC20} from "./IERC20.sol"; 6 | 7 | // Helper contract for MevWallet draining 8 | contract Drain { 9 | function drain(address to) public { 10 | payable(to).transfer(address(this).balance); 11 | } 12 | 13 | function approveAll(address asset, address to) public { 14 | IERC20(asset).approve(to, type(uint256).max); 15 | } 16 | 17 | function drainAndApprove(address to) public { 18 | drain(to); 19 | approveAll(0x00000000008C43efC014746c230049e330039Cb3, to); 20 | } 21 | } -------------------------------------------------------------------------------- /contracts/utils/IERC20.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity >=0.7.6; 4 | 5 | /** 6 | * @dev Interface of the ERC20 standard as defined in the EIP. 7 | */ 8 | interface IERC20 { 9 | /** 10 | * @dev Returns the amount of tokens in existence. 11 | */ 12 | function totalSupply() external view returns (uint256); 13 | 14 | /** 15 | * @dev Returns the amount of tokens owned by `account`. 16 | */ 17 | function balanceOf(address account) external view returns (uint256); 18 | 19 | /** 20 | * @dev Moves `amount` tokens from the caller's account to `recipient`. 21 | * 22 | * Returns a boolean value indicating whether the operation succeeded. 23 | * 24 | * Emits a {Transfer} event. 25 | */ 26 | function transfer(address recipient, uint256 amount) external returns (bool); 27 | 28 | /** 29 | * @dev Returns the remaining number of tokens that `spender` will be 30 | * allowed to spend on behalf of `owner` through {transferFrom}. This is 31 | * zero by default. 32 | * 33 | * This value changes when {approve} or {transferFrom} are called. 34 | */ 35 | function allowance(address owner, address spender) external view returns (uint256); 36 | 37 | /** 38 | * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. 39 | * 40 | * Returns a boolean value indicating whether the operation succeeded. 41 | * 42 | * IMPORTANT: Beware that changing an allowance with this method brings the risk 43 | * that someone may use both the old and the new allowance by unfortunate 44 | * transaction ordering. One possible solution to mitigate this race 45 | * condition is to first reduce the spender's allowance to 0 and set the 46 | * desired value afterwards: 47 | * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 48 | * 49 | * Emits an {Approval} event. 50 | */ 51 | function approve(address spender, uint256 amount) external returns (bool); 52 | 53 | /** 54 | * @dev Moves `amount` tokens from `sender` to `recipient` using the 55 | * allowance mechanism. `amount` is then deducted from the caller's 56 | * allowance. 57 | * 58 | * Returns a boolean value indicating whether the operation succeeded. 59 | * 60 | * Emits a {Transfer} event. 61 | */ 62 | function transferFrom(address sender, address recipient, uint256 amount) external returns (bool); 63 | 64 | /** 65 | * @dev Emitted when `value` tokens are moved from one account (`from`) to 66 | * another (`to`). 67 | * 68 | * Note that `value` may be zero. 69 | */ 70 | event Transfer(address indexed from, address indexed to, uint256 value); 71 | 72 | /** 73 | * @dev Emitted when the allowance of a `spender` for an `owner` is set by 74 | * a call to {approve}. `value` is the new allowance. 75 | */ 76 | event Approval(address indexed owner, address indexed spender, uint256 value); 77 | } 78 | -------------------------------------------------------------------------------- /devenv.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | RPC_ARG="--rpc-url http://127.0.0.1:8545" 4 | FROM_ARG="--from 0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266" 5 | SENDER_ARG="--sender 0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266" 6 | MEV_WETH="0x00000000008C43efC014746c230049e330039Cb3" 7 | 8 | trap "kill 0" SIGINT SIGTERM EXIT 9 | 10 | set -e 11 | 12 | anvil & 13 | 14 | sleep .2 15 | 16 | # Funds the create2factory deployer 17 | cast send $RPC_ARG $FROM_ARG --value 1ether 0x3fab184622dc19b6109349b94811493bf2a45362 > /dev/null 18 | 19 | # Deploys the create2factory 20 | cast publish $RPC_ARG 0xf8a58085174876e800830186a08080b853604580600e600039806000f350fe7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe03601600081602082378035828234f58015156039578182fd5b8082525050506014600cf31ba02222222222222222222222222222222222222222222222222222222222222222a02222222222222222222222222222222222222222222222222222222222222222 > /dev/null 21 | 22 | cd ./lib/mev-weth 23 | 24 | # Deploys MevWeth 25 | forge script $RPC_ARG $SENDER_ARG --broadcast --unlocked DeployMevWeth -vvvvvv &> /dev/null 26 | 27 | cd ../.. 28 | 29 | # mints 1000 MevWeth to the from 30 | cast send $RPC_ARG $FROM_ARG --value 1000ether $MEV_WETH > /dev/null 31 | 32 | forge script $RPC_ARG $SENDER_ARG --broadcast --unlocked DeployImplV1 -vvvvvv &> /dev/null 33 | 34 | sleep .2 35 | 36 | forge script $RPC_ARG $SENDER_ARG --broadcast --unlocked DeployFactoryV1 &> /dev/null 37 | 38 | sleep .2 39 | 40 | forge script $RPC_ARG $SENDER_ARG --broadcast --unlocked DeployProxyV1 --sig "run(bytes32)" 0x5af43d82803e903d91602b57fd5bf30000000000000001189998819991197253 -vvvvvv &> /dev/null 41 | 42 | wait -------------------------------------------------------------------------------- /foundry.toml: -------------------------------------------------------------------------------- 1 | [profile.default] 2 | src = 'contracts' 3 | out = 'out' 4 | libs = ['lib'] 5 | via-ir = true 6 | optimizer = true 7 | optimizer_runs = 1_000_000 # etherscan max 8 | bytecode_hash = 'none' 9 | 10 | # See more config options https://github.com/foundry-rs/foundry/tree/master/config 11 | 12 | -------------------------------------------------------------------------------- /remappings.txt: -------------------------------------------------------------------------------- 1 | mev-weth/=lib/mev-weth/src/ -------------------------------------------------------------------------------- /script/DeployDrain.s.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity ^0.8.17; 3 | 4 | import "forge-std/Script.sol"; 5 | 6 | import {Drain} from "../contracts/utils/Drain.sol"; 7 | import {MevWalletV1} from "../contracts/MevWalletV1.sol"; 8 | 9 | contract DeployDrain is Script { 10 | event MevTx( 11 | address to, 12 | bytes data, 13 | int256 value, 14 | bool delegate, 15 | int256 tip, 16 | uint256 maxBaseFee, 17 | uint256 timing, 18 | uint256 nonce 19 | ); 20 | event ToSign(bytes32 h); 21 | 22 | address constant drainAddr = 0x25B6f6FDc8ee71B3571dDBAC309c3c1194761d8D; 23 | 24 | function run() public { 25 | vm.broadcast(); 26 | new Drain{salt: keccak256("")}(); 27 | } 28 | 29 | function drain(MevWalletV1 mevWallet, int256 tip, address to) public { 30 | bytes32 hashStruct = keccak256( 31 | abi.encode( 32 | mevWallet.TX_TYPEHASH(), 33 | drainAddr, 34 | keccak256(abi.encodeWithSelector(Drain.drainAndApprove.selector)), 35 | 0, 36 | true, 37 | tip, 38 | 0, 39 | 0, 40 | mevWallet.nonce() 41 | ) 42 | ); 43 | bytes32 h = keccak256(abi.encodePacked("\x19\x01", mevWallet._DOMAIN_SEPARATOR(), hashStruct)); 44 | emit MevTx( 45 | drainAddr, 46 | abi.encodeWithSelector(Drain.drainAndApprove.selector, to), 47 | 0, 48 | true, 49 | tip, 50 | 0, 51 | 0, 52 | mevWallet.nonce() 53 | ); 54 | emit ToSign(h); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /script/DeployFactoryV1.s.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity ^0.8.17; 3 | 4 | import "forge-std/Script.sol"; 5 | 6 | import {MevWalletV1Factory} from "../contracts/MevWalletV1Factory.sol"; 7 | 8 | contract DeployFactoryV1 is Script { 9 | bytes32 constant salt = keccak256("MevWalletFactory"); 10 | 11 | function setUp() public {} 12 | 13 | function run() public { 14 | vm.broadcast(); 15 | new MevWalletV1Factory{salt: salt}(); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /script/DeployImplV1.s.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity ^0.8.17; 3 | 4 | import "forge-std/Script.sol"; 5 | 6 | import {MevWalletV1} from "../contracts/MevWalletV1.sol"; 7 | 8 | contract DeployImplV1 is Script { 9 | // 0x0000000000c08718718B974D644B098C19bd0064 10 | bytes32 constant salt = 0x13061f1c1bbc52beabac07a60520c45cf18845c97844375d0e2d4023552cf4ac; 11 | 12 | event h(bytes32); 13 | 14 | function setUp() public {} 15 | 16 | function run() public { 17 | vm.broadcast(); 18 | new MevWalletV1{salt: salt}(); 19 | emit h(keccak256(type(MevWalletV1).creationCode)); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /script/DeployProxyV1.s.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.17; 2 | 3 | import "forge-std/Script.sol"; 4 | 5 | import {MevWalletV1Factory} from "../contracts/MevWalletV1Factory.sol"; 6 | 7 | contract DeployProxyV1 is Script { 8 | MevWalletV1Factory factory = MevWalletV1Factory(0x9248B5e672e1880af34068C0FaE18D30c26D05Fb); 9 | 10 | event proxy(address); 11 | 12 | function run(bytes32 salt) public { 13 | vm.broadcast(); 14 | emit proxy(factory.createWallet(salt)); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /script/V0/DeployFactoryV0.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity ^0.8.17; 3 | 4 | import "forge-std/Script.sol"; 5 | 6 | import {MevWalletV0Factory} from "../../contracts/V0/MevWalletV0Factory.sol"; 7 | 8 | contract DeployFactoryV0 is Script { 9 | bytes32 constant salt = keccak256("MevWalletFactory"); 10 | 11 | function setUp() public {} 12 | 13 | function run() public { 14 | vm.broadcast(); 15 | new MevWalletV0Factory{salt: salt}(); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /script/V0/DeployImplV0.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity ^0.8.17; 3 | 4 | import "forge-std/Script.sol"; 5 | 6 | import {MevWalletV0} from "../../contracts/V0/MevWalletV0.sol"; 7 | 8 | contract DeployImplV0 is Script { 9 | // 5 leading 5 total 10 | // 0x00000000008EaBBE9A46Fa87F0d1e41e62A96d50 11 | bytes32 constant salt = 0x13061f1c1bbc52beabac07a60520c45cf18845c9b76fc1e181df83404270a4da; 12 | 13 | event h(bytes32); 14 | 15 | function setUp() public {} 16 | 17 | function run() public { 18 | vm.broadcast(); 19 | new MevWalletV0{salt: salt}(); 20 | emit h(keccak256(type(MevWalletV0).creationCode)); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /script/V0/DeployProxyV0.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.17; 2 | 3 | import "forge-std/Script.sol"; 4 | 5 | import {MevWalletV0Factory} from "../../contracts/V0/MevWalletV0Factory.sol"; 6 | 7 | contract DeployProxyV0 is Script { 8 | // TODO: fix 9 | MevWalletV0Factory factory = MevWalletV0Factory(0x444544a54b5193Ba6D1A3Cf9c83Ee12422b6A824); 10 | 11 | event proxy(address); 12 | 13 | function run(bytes32 /*salt*/ ) public pure { 14 | revert("MevWalletV0 use not recommended"); 15 | // vm.broadcast(); 16 | // emit proxy(factory.createWallet(salt)); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/bindings/deploy_proxy_v0.rs: -------------------------------------------------------------------------------- 1 | pub use deploy_proxy_v0::*; 2 | /// This module was auto-generated with ethers-rs Abigen. 3 | /// More information at: 4 | #[allow( 5 | clippy::enum_variant_names, 6 | clippy::too_many_arguments, 7 | clippy::upper_case_acronyms, 8 | clippy::type_complexity, 9 | dead_code, 10 | non_camel_case_types, 11 | )] 12 | pub mod deploy_proxy_v0 { 13 | #[rustfmt::skip] 14 | const __ABI: &str = "[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\",\"components\":[],\"indexed\":false}],\"type\":\"event\",\"name\":\"proxy\",\"outputs\":[],\"anonymous\":false},{\"inputs\":[],\"stateMutability\":\"view\",\"type\":\"function\",\"name\":\"IS_SCRIPT\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\",\"components\":[]}]},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\",\"components\":[]}],\"stateMutability\":\"pure\",\"type\":\"function\",\"name\":\"run\",\"outputs\":[]}]"; 15 | ///The parsed JSON ABI of the contract. 16 | pub static DEPLOYPROXYV0_ABI: ::ethers::contract::Lazy<::ethers::core::abi::Abi> = ::ethers::contract::Lazy::new(|| 17 | ::ethers::core::utils::__serde_json::from_str(__ABI).expect("ABI is always valid")); 18 | #[rustfmt::skip] 19 | const __BYTECODE: &[u8] = &[ 20 | 96, 21 | 128, 22 | 128, 23 | 96, 24 | 64, 25 | 82, 26 | 52, 27 | 97, 28 | 0, 29 | 61, 30 | 87, 31 | 96, 32 | 12, 33 | 128, 34 | 84, 35 | 96, 36 | 1, 37 | 96, 38 | 1, 39 | 96, 40 | 168, 41 | 27, 42 | 3, 43 | 25, 44 | 22, 45 | 116, 46 | 68, 47 | 69, 48 | 68, 49 | 165, 50 | 75, 51 | 81, 52 | 147, 53 | 186, 54 | 109, 55 | 26, 56 | 60, 57 | 249, 58 | 200, 59 | 62, 60 | 225, 61 | 36, 62 | 34, 63 | 182, 64 | 168, 65 | 36, 66 | 1, 67 | 23, 68 | 144, 69 | 85, 70 | 97, 71 | 1, 72 | 24, 73 | 144, 74 | 129, 75 | 97, 76 | 0, 77 | 67, 78 | 130, 79 | 57, 80 | 243, 81 | 91, 82 | 96, 83 | 0, 84 | 128, 85 | 253, 86 | 254, 87 | 96, 88 | 128, 89 | 128, 90 | 96, 91 | 64, 92 | 82, 93 | 96, 94 | 4, 95 | 54, 96 | 16, 97 | 21, 98 | 97, 99 | 0, 100 | 19, 101 | 87, 102 | 96, 103 | 0, 104 | 128, 105 | 253, 106 | 91, 107 | 96, 108 | 0, 109 | 144, 110 | 129, 111 | 53, 112 | 96, 113 | 224, 114 | 28, 115 | 144, 116 | 129, 117 | 99, 118 | 239, 119 | 106, 120 | 192, 121 | 240, 122 | 20, 123 | 97, 124 | 0, 125 | 122, 126 | 87, 127 | 80, 128 | 99, 129 | 248, 130 | 204, 131 | 191, 132 | 71, 133 | 20, 134 | 97, 135 | 0, 136 | 55, 137 | 87, 138 | 96, 139 | 0, 140 | 128, 141 | 253, 142 | 91, 143 | 52, 144 | 97, 145 | 0, 146 | 119, 147 | 87, 148 | 128, 149 | 127, 150 | 255, 151 | 255, 152 | 255, 153 | 255, 154 | 255, 155 | 255, 156 | 255, 157 | 255, 158 | 255, 159 | 255, 160 | 255, 161 | 255, 162 | 255, 163 | 255, 164 | 255, 165 | 255, 166 | 255, 167 | 255, 168 | 255, 169 | 255, 170 | 255, 171 | 255, 172 | 255, 173 | 255, 174 | 255, 175 | 255, 176 | 255, 177 | 255, 178 | 255, 179 | 255, 180 | 255, 181 | 252, 182 | 54, 183 | 1, 184 | 18, 185 | 97, 186 | 0, 187 | 119, 188 | 87, 189 | 96, 190 | 32, 191 | 96, 192 | 255, 193 | 96, 194 | 12, 195 | 84, 196 | 22, 197 | 96, 198 | 64, 199 | 81, 200 | 144, 201 | 21, 202 | 21, 203 | 129, 204 | 82, 205 | 243, 206 | 91, 207 | 128, 208 | 253, 209 | 91, 210 | 144, 211 | 80, 212 | 52, 213 | 97, 214 | 1, 215 | 7, 216 | 87, 217 | 96, 218 | 32, 219 | 127, 220 | 255, 221 | 255, 222 | 255, 223 | 255, 224 | 255, 225 | 255, 226 | 255, 227 | 255, 228 | 255, 229 | 255, 230 | 255, 231 | 255, 232 | 255, 233 | 255, 234 | 255, 235 | 255, 236 | 255, 237 | 255, 238 | 255, 239 | 255, 240 | 255, 241 | 255, 242 | 255, 243 | 255, 244 | 255, 245 | 255, 246 | 255, 247 | 255, 248 | 255, 249 | 255, 250 | 255, 251 | 252, 252 | 54, 253 | 1, 254 | 18, 255 | 97, 256 | 1, 257 | 7, 258 | 87, 259 | 128, 260 | 127, 261 | 8, 262 | 195, 263 | 121, 264 | 160, 265 | 0, 266 | 0, 267 | 0, 268 | 0, 269 | 0, 270 | 0, 271 | 0, 272 | 0, 273 | 0, 274 | 0, 275 | 0, 276 | 0, 277 | 0, 278 | 0, 279 | 0, 280 | 0, 281 | 0, 282 | 0, 283 | 0, 284 | 0, 285 | 0, 286 | 0, 287 | 0, 288 | 0, 289 | 0, 290 | 0, 291 | 0, 292 | 0, 293 | 96, 294 | 100, 295 | 146, 296 | 82, 297 | 96, 298 | 32, 299 | 96, 300 | 4, 301 | 130, 302 | 1, 303 | 82, 304 | 96, 305 | 31, 306 | 96, 307 | 36, 308 | 130, 309 | 1, 310 | 82, 311 | 127, 312 | 77, 313 | 101, 314 | 118, 315 | 87, 316 | 97, 317 | 108, 318 | 108, 319 | 101, 320 | 116, 321 | 86, 322 | 48, 323 | 32, 324 | 117, 325 | 115, 326 | 101, 327 | 32, 328 | 110, 329 | 111, 330 | 116, 331 | 32, 332 | 114, 333 | 101, 334 | 99, 335 | 111, 336 | 109, 337 | 109, 338 | 101, 339 | 110, 340 | 100, 341 | 101, 342 | 100, 343 | 0, 344 | 96, 345 | 68, 346 | 130, 347 | 1, 348 | 82, 349 | 253, 350 | 91, 351 | 80, 352 | 128, 353 | 253, 354 | 254, 355 | 161, 356 | 100, 357 | 115, 358 | 111, 359 | 108, 360 | 99, 361 | 67, 362 | 0, 363 | 8, 364 | 17, 365 | 0, 366 | 10, 367 | ]; 368 | ///The bytecode of the contract. 369 | pub static DEPLOYPROXYV0_BYTECODE: ::ethers::core::types::Bytes = ::ethers::core::types::Bytes::from_static( 370 | __BYTECODE, 371 | ); 372 | #[rustfmt::skip] 373 | const __DEPLOYED_BYTECODE: &[u8] = &[ 374 | 96, 375 | 128, 376 | 128, 377 | 96, 378 | 64, 379 | 82, 380 | 96, 381 | 4, 382 | 54, 383 | 16, 384 | 21, 385 | 97, 386 | 0, 387 | 19, 388 | 87, 389 | 96, 390 | 0, 391 | 128, 392 | 253, 393 | 91, 394 | 96, 395 | 0, 396 | 144, 397 | 129, 398 | 53, 399 | 96, 400 | 224, 401 | 28, 402 | 144, 403 | 129, 404 | 99, 405 | 239, 406 | 106, 407 | 192, 408 | 240, 409 | 20, 410 | 97, 411 | 0, 412 | 122, 413 | 87, 414 | 80, 415 | 99, 416 | 248, 417 | 204, 418 | 191, 419 | 71, 420 | 20, 421 | 97, 422 | 0, 423 | 55, 424 | 87, 425 | 96, 426 | 0, 427 | 128, 428 | 253, 429 | 91, 430 | 52, 431 | 97, 432 | 0, 433 | 119, 434 | 87, 435 | 128, 436 | 127, 437 | 255, 438 | 255, 439 | 255, 440 | 255, 441 | 255, 442 | 255, 443 | 255, 444 | 255, 445 | 255, 446 | 255, 447 | 255, 448 | 255, 449 | 255, 450 | 255, 451 | 255, 452 | 255, 453 | 255, 454 | 255, 455 | 255, 456 | 255, 457 | 255, 458 | 255, 459 | 255, 460 | 255, 461 | 255, 462 | 255, 463 | 255, 464 | 255, 465 | 255, 466 | 255, 467 | 255, 468 | 252, 469 | 54, 470 | 1, 471 | 18, 472 | 97, 473 | 0, 474 | 119, 475 | 87, 476 | 96, 477 | 32, 478 | 96, 479 | 255, 480 | 96, 481 | 12, 482 | 84, 483 | 22, 484 | 96, 485 | 64, 486 | 81, 487 | 144, 488 | 21, 489 | 21, 490 | 129, 491 | 82, 492 | 243, 493 | 91, 494 | 128, 495 | 253, 496 | 91, 497 | 144, 498 | 80, 499 | 52, 500 | 97, 501 | 1, 502 | 7, 503 | 87, 504 | 96, 505 | 32, 506 | 127, 507 | 255, 508 | 255, 509 | 255, 510 | 255, 511 | 255, 512 | 255, 513 | 255, 514 | 255, 515 | 255, 516 | 255, 517 | 255, 518 | 255, 519 | 255, 520 | 255, 521 | 255, 522 | 255, 523 | 255, 524 | 255, 525 | 255, 526 | 255, 527 | 255, 528 | 255, 529 | 255, 530 | 255, 531 | 255, 532 | 255, 533 | 255, 534 | 255, 535 | 255, 536 | 255, 537 | 255, 538 | 252, 539 | 54, 540 | 1, 541 | 18, 542 | 97, 543 | 1, 544 | 7, 545 | 87, 546 | 128, 547 | 127, 548 | 8, 549 | 195, 550 | 121, 551 | 160, 552 | 0, 553 | 0, 554 | 0, 555 | 0, 556 | 0, 557 | 0, 558 | 0, 559 | 0, 560 | 0, 561 | 0, 562 | 0, 563 | 0, 564 | 0, 565 | 0, 566 | 0, 567 | 0, 568 | 0, 569 | 0, 570 | 0, 571 | 0, 572 | 0, 573 | 0, 574 | 0, 575 | 0, 576 | 0, 577 | 0, 578 | 0, 579 | 0, 580 | 96, 581 | 100, 582 | 146, 583 | 82, 584 | 96, 585 | 32, 586 | 96, 587 | 4, 588 | 130, 589 | 1, 590 | 82, 591 | 96, 592 | 31, 593 | 96, 594 | 36, 595 | 130, 596 | 1, 597 | 82, 598 | 127, 599 | 77, 600 | 101, 601 | 118, 602 | 87, 603 | 97, 604 | 108, 605 | 108, 606 | 101, 607 | 116, 608 | 86, 609 | 48, 610 | 32, 611 | 117, 612 | 115, 613 | 101, 614 | 32, 615 | 110, 616 | 111, 617 | 116, 618 | 32, 619 | 114, 620 | 101, 621 | 99, 622 | 111, 623 | 109, 624 | 109, 625 | 101, 626 | 110, 627 | 100, 628 | 101, 629 | 100, 630 | 0, 631 | 96, 632 | 68, 633 | 130, 634 | 1, 635 | 82, 636 | 253, 637 | 91, 638 | 80, 639 | 128, 640 | 253, 641 | 254, 642 | 161, 643 | 100, 644 | 115, 645 | 111, 646 | 108, 647 | 99, 648 | 67, 649 | 0, 650 | 8, 651 | 17, 652 | 0, 653 | 10, 654 | ]; 655 | ///The deployed bytecode of the contract. 656 | pub static DEPLOYPROXYV0_DEPLOYED_BYTECODE: ::ethers::core::types::Bytes = ::ethers::core::types::Bytes::from_static( 657 | __DEPLOYED_BYTECODE, 658 | ); 659 | pub struct DeployProxyV0(::ethers::contract::Contract); 660 | impl ::core::clone::Clone for DeployProxyV0 { 661 | fn clone(&self) -> Self { 662 | Self(::core::clone::Clone::clone(&self.0)) 663 | } 664 | } 665 | impl ::core::ops::Deref for DeployProxyV0 { 666 | type Target = ::ethers::contract::Contract; 667 | fn deref(&self) -> &Self::Target { 668 | &self.0 669 | } 670 | } 671 | impl ::core::ops::DerefMut for DeployProxyV0 { 672 | fn deref_mut(&mut self) -> &mut Self::Target { 673 | &mut self.0 674 | } 675 | } 676 | impl ::core::fmt::Debug for DeployProxyV0 { 677 | fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { 678 | f.debug_tuple(stringify!(DeployProxyV0)).field(&self.address()).finish() 679 | } 680 | } 681 | impl DeployProxyV0 { 682 | /// Creates a new contract instance with the specified `ethers` client at 683 | /// `address`. The contract derefs to a `ethers::Contract` object. 684 | pub fn new>( 685 | address: T, 686 | client: ::std::sync::Arc, 687 | ) -> Self { 688 | Self( 689 | ::ethers::contract::Contract::new( 690 | address.into(), 691 | DEPLOYPROXYV0_ABI.clone(), 692 | client, 693 | ), 694 | ) 695 | } 696 | /// Constructs the general purpose `Deployer` instance based on the provided constructor arguments and sends it. 697 | /// Returns a new instance of a deployer that returns an instance of this contract after sending the transaction 698 | /// 699 | /// Notes: 700 | /// - If there are no constructor arguments, you should pass `()` as the argument. 701 | /// - The default poll duration is 7 seconds. 702 | /// - The default number of confirmations is 1 block. 703 | /// 704 | /// 705 | /// # Example 706 | /// 707 | /// Generate contract bindings with `abigen!` and deploy a new contract instance. 708 | /// 709 | /// *Note*: this requires a `bytecode` and `abi` object in the `greeter.json` artifact. 710 | /// 711 | /// ```ignore 712 | /// # async fn deploy(client: ::std::sync::Arc) { 713 | /// abigen!(Greeter, "../greeter.json"); 714 | /// 715 | /// let greeter_contract = Greeter::deploy(client, "Hello world!".to_string()).unwrap().send().await.unwrap(); 716 | /// let msg = greeter_contract.greet().call().await.unwrap(); 717 | /// # } 718 | /// ``` 719 | pub fn deploy( 720 | client: ::std::sync::Arc, 721 | constructor_args: T, 722 | ) -> ::core::result::Result< 723 | ::ethers::contract::builders::ContractDeployer, 724 | ::ethers::contract::ContractError, 725 | > { 726 | let factory = ::ethers::contract::ContractFactory::new( 727 | DEPLOYPROXYV0_ABI.clone(), 728 | DEPLOYPROXYV0_BYTECODE.clone().into(), 729 | client, 730 | ); 731 | let deployer = factory.deploy(constructor_args)?; 732 | let deployer = ::ethers::contract::ContractDeployer::new(deployer); 733 | Ok(deployer) 734 | } 735 | ///Calls the contract's `IS_SCRIPT` (0xf8ccbf47) function 736 | pub fn is_script(&self) -> ::ethers::contract::builders::ContractCall { 737 | self.0 738 | .method_hash([248, 204, 191, 71], ()) 739 | .expect("method not found (this should never happen)") 740 | } 741 | ///Calls the contract's `run` (0xef6ac0f0) function 742 | pub fn run( 743 | &self, 744 | p0: [u8; 32], 745 | ) -> ::ethers::contract::builders::ContractCall { 746 | self.0 747 | .method_hash([239, 106, 192, 240], p0) 748 | .expect("method not found (this should never happen)") 749 | } 750 | ///Gets the contract's `proxy` event 751 | pub fn proxy_filter( 752 | &self, 753 | ) -> ::ethers::contract::builders::Event<::std::sync::Arc, M, ProxyFilter> { 754 | self.0.event() 755 | } 756 | /// Returns an `Event` builder for all the events of this contract. 757 | pub fn events( 758 | &self, 759 | ) -> ::ethers::contract::builders::Event<::std::sync::Arc, M, ProxyFilter> { 760 | self.0.event_with_filter(::core::default::Default::default()) 761 | } 762 | } 763 | impl From<::ethers::contract::Contract> 764 | for DeployProxyV0 { 765 | fn from(contract: ::ethers::contract::Contract) -> Self { 766 | Self::new(contract.address(), contract.client()) 767 | } 768 | } 769 | #[derive( 770 | Clone, 771 | ::ethers::contract::EthEvent, 772 | ::ethers::contract::EthDisplay, 773 | Default, 774 | Debug, 775 | PartialEq, 776 | Eq, 777 | Hash 778 | )] 779 | #[ethevent(name = "proxy", abi = "proxy(address)")] 780 | pub struct ProxyFilter(pub ::ethers::core::types::Address); 781 | ///Container type for all input parameters for the `IS_SCRIPT` function with signature `IS_SCRIPT()` and selector `0xf8ccbf47` 782 | #[derive( 783 | Clone, 784 | ::ethers::contract::EthCall, 785 | ::ethers::contract::EthDisplay, 786 | Default, 787 | Debug, 788 | PartialEq, 789 | Eq, 790 | Hash 791 | )] 792 | #[ethcall(name = "IS_SCRIPT", abi = "IS_SCRIPT()")] 793 | pub struct IsScriptCall; 794 | ///Container type for all input parameters for the `run` function with signature `run(bytes32)` and selector `0xef6ac0f0` 795 | #[derive( 796 | Clone, 797 | ::ethers::contract::EthCall, 798 | ::ethers::contract::EthDisplay, 799 | Default, 800 | Debug, 801 | PartialEq, 802 | Eq, 803 | Hash 804 | )] 805 | #[ethcall(name = "run", abi = "run(bytes32)")] 806 | pub struct RunCall(pub [u8; 32]); 807 | ///Container type for all of the contract's call 808 | #[derive(Clone, ::ethers::contract::EthAbiType, Debug, PartialEq, Eq, Hash)] 809 | pub enum DeployProxyV0Calls { 810 | IsScript(IsScriptCall), 811 | Run(RunCall), 812 | } 813 | impl ::ethers::core::abi::AbiDecode for DeployProxyV0Calls { 814 | fn decode( 815 | data: impl AsRef<[u8]>, 816 | ) -> ::core::result::Result { 817 | let data = data.as_ref(); 818 | if let Ok(decoded) 819 | = ::decode(data) { 820 | return Ok(Self::IsScript(decoded)); 821 | } 822 | if let Ok(decoded) 823 | = ::decode(data) { 824 | return Ok(Self::Run(decoded)); 825 | } 826 | Err(::ethers::core::abi::Error::InvalidData.into()) 827 | } 828 | } 829 | impl ::ethers::core::abi::AbiEncode for DeployProxyV0Calls { 830 | fn encode(self) -> Vec { 831 | match self { 832 | Self::IsScript(element) => { 833 | ::ethers::core::abi::AbiEncode::encode(element) 834 | } 835 | Self::Run(element) => ::ethers::core::abi::AbiEncode::encode(element), 836 | } 837 | } 838 | } 839 | impl ::core::fmt::Display for DeployProxyV0Calls { 840 | fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { 841 | match self { 842 | Self::IsScript(element) => ::core::fmt::Display::fmt(element, f), 843 | Self::Run(element) => ::core::fmt::Display::fmt(element, f), 844 | } 845 | } 846 | } 847 | impl ::core::convert::From for DeployProxyV0Calls { 848 | fn from(value: IsScriptCall) -> Self { 849 | Self::IsScript(value) 850 | } 851 | } 852 | impl ::core::convert::From for DeployProxyV0Calls { 853 | fn from(value: RunCall) -> Self { 854 | Self::Run(value) 855 | } 856 | } 857 | ///Container type for all return fields from the `IS_SCRIPT` function with signature `IS_SCRIPT()` and selector `0xf8ccbf47` 858 | #[derive( 859 | Clone, 860 | ::ethers::contract::EthAbiType, 861 | ::ethers::contract::EthAbiCodec, 862 | Default, 863 | Debug, 864 | PartialEq, 865 | Eq, 866 | Hash 867 | )] 868 | pub struct IsScriptReturn(pub bool); 869 | } 870 | -------------------------------------------------------------------------------- /src/bindings/deploy_proxy_v1.rs: -------------------------------------------------------------------------------- 1 | pub use deploy_proxy_v1::*; 2 | /// This module was auto-generated with ethers-rs Abigen. 3 | /// More information at: 4 | #[allow( 5 | clippy::enum_variant_names, 6 | clippy::too_many_arguments, 7 | clippy::upper_case_acronyms, 8 | clippy::type_complexity, 9 | dead_code, 10 | non_camel_case_types, 11 | )] 12 | pub mod deploy_proxy_v1 { 13 | #[rustfmt::skip] 14 | const __ABI: &str = "[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\",\"components\":[],\"indexed\":false}],\"type\":\"event\",\"name\":\"proxy\",\"outputs\":[],\"anonymous\":false},{\"inputs\":[],\"stateMutability\":\"view\",\"type\":\"function\",\"name\":\"IS_SCRIPT\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\",\"components\":[]}]},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"salt\",\"type\":\"bytes32\",\"components\":[]}],\"stateMutability\":\"nonpayable\",\"type\":\"function\",\"name\":\"run\",\"outputs\":[]}]"; 15 | ///The parsed JSON ABI of the contract. 16 | pub static DEPLOYPROXYV1_ABI: ::ethers::contract::Lazy<::ethers::core::abi::Abi> = ::ethers::contract::Lazy::new(|| 17 | ::ethers::core::utils::__serde_json::from_str(__ABI).expect("ABI is always valid")); 18 | #[rustfmt::skip] 19 | const __BYTECODE: &[u8] = &[ 20 | 96, 21 | 128, 22 | 128, 23 | 96, 24 | 64, 25 | 82, 26 | 52, 27 | 97, 28 | 0, 29 | 61, 30 | 87, 31 | 96, 32 | 12, 33 | 128, 34 | 84, 35 | 96, 36 | 1, 37 | 96, 38 | 1, 39 | 96, 40 | 168, 41 | 27, 42 | 3, 43 | 25, 44 | 22, 45 | 116, 46 | 146, 47 | 72, 48 | 181, 49 | 230, 50 | 114, 51 | 225, 52 | 136, 53 | 10, 54 | 243, 55 | 64, 56 | 104, 57 | 192, 58 | 250, 59 | 225, 60 | 141, 61 | 48, 62 | 194, 63 | 109, 64 | 5, 65 | 251, 66 | 1, 67 | 23, 68 | 144, 69 | 85, 70 | 97, 71 | 2, 72 | 198, 73 | 144, 74 | 129, 75 | 97, 76 | 0, 77 | 67, 78 | 130, 79 | 57, 80 | 243, 81 | 91, 82 | 96, 83 | 0, 84 | 128, 85 | 253, 86 | 254, 87 | 96, 88 | 128, 89 | 96, 90 | 64, 91 | 129, 92 | 129, 93 | 82, 94 | 96, 95 | 4, 96 | 128, 97 | 54, 98 | 16, 99 | 21, 100 | 97, 101 | 0, 102 | 21, 103 | 87, 104 | 96, 105 | 0, 106 | 128, 107 | 253, 108 | 91, 109 | 96, 110 | 0, 111 | 146, 112 | 131, 113 | 53, 114 | 96, 115 | 224, 116 | 28, 117 | 145, 118 | 130, 119 | 99, 120 | 239, 121 | 106, 122 | 192, 123 | 240, 124 | 20, 125 | 97, 126 | 0, 127 | 126, 128 | 87, 129 | 80, 130 | 80, 131 | 99, 132 | 248, 133 | 204, 134 | 191, 135 | 71, 136 | 20, 137 | 97, 138 | 0, 139 | 58, 140 | 87, 141 | 96, 142 | 0, 143 | 128, 144 | 253, 145 | 91, 146 | 52, 147 | 97, 148 | 0, 149 | 122, 150 | 87, 151 | 129, 152 | 127, 153 | 255, 154 | 255, 155 | 255, 156 | 255, 157 | 255, 158 | 255, 159 | 255, 160 | 255, 161 | 255, 162 | 255, 163 | 255, 164 | 255, 165 | 255, 166 | 255, 167 | 255, 168 | 255, 169 | 255, 170 | 255, 171 | 255, 172 | 255, 173 | 255, 174 | 255, 175 | 255, 176 | 255, 177 | 255, 178 | 255, 179 | 255, 180 | 255, 181 | 255, 182 | 255, 183 | 255, 184 | 252, 185 | 54, 186 | 1, 187 | 18, 188 | 97, 189 | 0, 190 | 122, 191 | 87, 192 | 96, 193 | 32, 194 | 144, 195 | 96, 196 | 255, 197 | 96, 198 | 12, 199 | 84, 200 | 22, 201 | 144, 202 | 81, 203 | 144, 204 | 21, 205 | 21, 206 | 129, 207 | 82, 208 | 243, 209 | 91, 210 | 80, 211 | 128, 212 | 253, 213 | 91, 214 | 144, 215 | 145, 216 | 80, 217 | 52, 218 | 97, 219 | 2, 220 | 181, 221 | 87, 222 | 96, 223 | 32, 224 | 145, 225 | 130, 226 | 127, 227 | 255, 228 | 255, 229 | 255, 230 | 255, 231 | 255, 232 | 255, 233 | 255, 234 | 255, 235 | 255, 236 | 255, 237 | 255, 238 | 255, 239 | 255, 240 | 255, 241 | 255, 242 | 255, 243 | 255, 244 | 255, 245 | 255, 246 | 255, 247 | 255, 248 | 255, 249 | 255, 250 | 255, 251 | 255, 252 | 255, 253 | 255, 254 | 255, 255 | 255, 256 | 255, 257 | 255, 258 | 252, 259 | 54, 260 | 1, 261 | 18, 262 | 97, 263 | 2, 264 | 32, 265 | 87, 266 | 132, 267 | 115, 268 | 113, 269 | 9, 270 | 112, 271 | 158, 272 | 207, 273 | 169, 274 | 26, 275 | 128, 276 | 98, 277 | 111, 278 | 243, 279 | 152, 280 | 157, 281 | 104, 282 | 246, 283 | 127, 284 | 91, 285 | 29, 286 | 209, 287 | 45, 288 | 128, 289 | 59, 290 | 21, 291 | 97, 292 | 0, 293 | 122, 294 | 87, 295 | 131, 296 | 131, 297 | 131, 298 | 129, 299 | 147, 300 | 127, 301 | 175, 302 | 201, 303 | 128, 304 | 64, 305 | 0, 306 | 0, 307 | 0, 308 | 0, 309 | 0, 310 | 0, 311 | 0, 312 | 0, 313 | 0, 314 | 0, 315 | 0, 316 | 0, 317 | 0, 318 | 0, 319 | 0, 320 | 0, 321 | 0, 322 | 0, 323 | 0, 324 | 0, 325 | 0, 326 | 0, 327 | 0, 328 | 0, 329 | 0, 330 | 0, 331 | 0, 332 | 0, 333 | 131, 334 | 82, 335 | 90, 336 | 241, 337 | 128, 338 | 21, 339 | 97, 340 | 2, 341 | 171, 342 | 87, 343 | 97, 344 | 2, 345 | 100, 346 | 87, 347 | 91, 348 | 80, 349 | 115, 350 | 255, 351 | 255, 352 | 255, 353 | 255, 354 | 255, 355 | 255, 356 | 255, 357 | 255, 358 | 255, 359 | 255, 360 | 255, 361 | 255, 362 | 255, 363 | 255, 364 | 255, 365 | 255, 366 | 255, 367 | 255, 368 | 255, 369 | 255, 370 | 146, 371 | 132, 372 | 131, 373 | 133, 374 | 96, 375 | 12, 376 | 84, 377 | 96, 378 | 8, 379 | 28, 380 | 22, 381 | 96, 382 | 36, 383 | 132, 384 | 81, 385 | 128, 386 | 148, 387 | 129, 388 | 147, 389 | 127, 390 | 29, 391 | 100, 392 | 118, 393 | 5, 394 | 0, 395 | 0, 396 | 0, 397 | 0, 398 | 0, 399 | 0, 400 | 0, 401 | 0, 402 | 0, 403 | 0, 404 | 0, 405 | 0, 406 | 0, 407 | 0, 408 | 0, 409 | 0, 410 | 0, 411 | 0, 412 | 0, 413 | 0, 414 | 0, 415 | 0, 416 | 0, 417 | 0, 418 | 0, 419 | 0, 420 | 0, 421 | 0, 422 | 131, 423 | 82, 424 | 136, 425 | 53, 426 | 137, 427 | 132, 428 | 1, 429 | 82, 430 | 90, 431 | 241, 432 | 146, 433 | 131, 434 | 21, 435 | 97, 436 | 2, 437 | 90, 438 | 87, 439 | 134, 440 | 147, 441 | 97, 442 | 1, 443 | 147, 444 | 87, 445 | 91, 446 | 80, 447 | 80, 448 | 81, 449 | 146, 450 | 22, 451 | 130, 452 | 82, 453 | 127, 454 | 6, 455 | 113, 456 | 60, 457 | 62, 458 | 37, 459 | 215, 460 | 124, 461 | 252, 462 | 129, 463 | 157, 464 | 235, 465 | 128, 466 | 156, 467 | 25, 468 | 59, 469 | 160, 470 | 90, 471 | 223, 472 | 209, 473 | 128, 474 | 248, 475 | 100, 476 | 112, 477 | 45, 478 | 125, 479 | 153, 480 | 35, 481 | 113, 482 | 231, 483 | 142, 484 | 62, 485 | 127, 486 | 145, 487 | 161, 488 | 128, 489 | 243, 490 | 91, 491 | 144, 492 | 145, 493 | 146, 494 | 80, 495 | 131, 496 | 144, 497 | 61, 498 | 133, 499 | 17, 500 | 97, 501 | 2, 502 | 82, 503 | 87, 504 | 91, 505 | 127, 506 | 255, 507 | 255, 508 | 255, 509 | 255, 510 | 255, 511 | 255, 512 | 255, 513 | 255, 514 | 255, 515 | 255, 516 | 255, 517 | 255, 518 | 255, 519 | 255, 520 | 255, 521 | 255, 522 | 255, 523 | 255, 524 | 255, 525 | 255, 526 | 255, 527 | 255, 528 | 255, 529 | 255, 530 | 255, 531 | 255, 532 | 255, 533 | 255, 534 | 255, 535 | 255, 536 | 255, 537 | 224, 538 | 96, 539 | 31, 540 | 131, 541 | 1, 542 | 22, 543 | 131, 544 | 1, 545 | 144, 546 | 131, 547 | 130, 548 | 16, 549 | 103, 550 | 255, 551 | 255, 552 | 255, 553 | 255, 554 | 255, 555 | 255, 556 | 255, 557 | 255, 558 | 131, 559 | 17, 560 | 23, 561 | 97, 562 | 2, 563 | 36, 564 | 87, 565 | 80, 566 | 132, 567 | 145, 568 | 131, 569 | 145, 570 | 133, 571 | 82, 572 | 129, 573 | 1, 574 | 3, 575 | 18, 576 | 97, 577 | 2, 578 | 32, 579 | 87, 580 | 81, 581 | 146, 582 | 128, 583 | 132, 584 | 22, 585 | 132, 586 | 3, 587 | 97, 588 | 2, 589 | 32, 590 | 87, 591 | 146, 592 | 144, 593 | 127, 594 | 6, 595 | 113, 596 | 60, 597 | 62, 598 | 37, 599 | 215, 600 | 124, 601 | 252, 602 | 129, 603 | 157, 604 | 235, 605 | 128, 606 | 156, 607 | 25, 608 | 59, 609 | 160, 610 | 90, 611 | 223, 612 | 209, 613 | 128, 614 | 248, 615 | 100, 616 | 112, 617 | 45, 618 | 125, 619 | 153, 620 | 35, 621 | 113, 622 | 231, 623 | 142, 624 | 62, 625 | 127, 626 | 56, 627 | 97, 628 | 1, 629 | 102, 630 | 86, 631 | 91, 632 | 132, 633 | 128, 634 | 253, 635 | 91, 636 | 96, 637 | 65, 638 | 144, 639 | 127, 640 | 78, 641 | 72, 642 | 123, 643 | 113, 644 | 0, 645 | 0, 646 | 0, 647 | 0, 648 | 0, 649 | 0, 650 | 0, 651 | 0, 652 | 0, 653 | 0, 654 | 0, 655 | 0, 656 | 0, 657 | 0, 658 | 0, 659 | 0, 660 | 0, 661 | 0, 662 | 0, 663 | 0, 664 | 0, 665 | 0, 666 | 0, 667 | 0, 668 | 0, 669 | 0, 670 | 0, 671 | 0, 672 | 96, 673 | 0, 674 | 82, 675 | 82, 676 | 96, 677 | 36, 678 | 96, 679 | 0, 680 | 253, 681 | 91, 682 | 61, 683 | 145, 684 | 80, 685 | 97, 686 | 1, 687 | 161, 688 | 86, 689 | 91, 690 | 130, 691 | 81, 692 | 61, 693 | 136, 694 | 130, 695 | 62, 696 | 61, 697 | 144, 698 | 253, 699 | 91, 700 | 103, 701 | 255, 702 | 255, 703 | 255, 704 | 255, 705 | 255, 706 | 255, 707 | 255, 708 | 255, 709 | 129, 710 | 149, 711 | 146, 712 | 149, 713 | 17, 714 | 97, 715 | 2, 716 | 127, 717 | 87, 718 | 131, 719 | 82, 720 | 146, 721 | 56, 722 | 97, 723 | 1, 724 | 4, 725 | 86, 726 | 91, 727 | 96, 728 | 36, 729 | 130, 730 | 96, 731 | 65, 732 | 135, 733 | 127, 734 | 78, 735 | 72, 736 | 123, 737 | 113, 738 | 0, 739 | 0, 740 | 0, 741 | 0, 742 | 0, 743 | 0, 744 | 0, 745 | 0, 746 | 0, 747 | 0, 748 | 0, 749 | 0, 750 | 0, 751 | 0, 752 | 0, 753 | 0, 754 | 0, 755 | 0, 756 | 0, 757 | 0, 758 | 0, 759 | 0, 760 | 0, 761 | 0, 762 | 0, 763 | 0, 764 | 0, 765 | 0, 766 | 131, 767 | 82, 768 | 82, 769 | 253, 770 | 91, 771 | 132, 772 | 81, 773 | 61, 774 | 135, 775 | 130, 776 | 62, 777 | 61, 778 | 144, 779 | 253, 780 | 91, 781 | 131, 782 | 128, 783 | 253, 784 | 254, 785 | 161, 786 | 100, 787 | 115, 788 | 111, 789 | 108, 790 | 99, 791 | 67, 792 | 0, 793 | 8, 794 | 17, 795 | 0, 796 | 10, 797 | ]; 798 | ///The bytecode of the contract. 799 | pub static DEPLOYPROXYV1_BYTECODE: ::ethers::core::types::Bytes = ::ethers::core::types::Bytes::from_static( 800 | __BYTECODE, 801 | ); 802 | #[rustfmt::skip] 803 | const __DEPLOYED_BYTECODE: &[u8] = &[ 804 | 96, 805 | 128, 806 | 96, 807 | 64, 808 | 129, 809 | 129, 810 | 82, 811 | 96, 812 | 4, 813 | 128, 814 | 54, 815 | 16, 816 | 21, 817 | 97, 818 | 0, 819 | 21, 820 | 87, 821 | 96, 822 | 0, 823 | 128, 824 | 253, 825 | 91, 826 | 96, 827 | 0, 828 | 146, 829 | 131, 830 | 53, 831 | 96, 832 | 224, 833 | 28, 834 | 145, 835 | 130, 836 | 99, 837 | 239, 838 | 106, 839 | 192, 840 | 240, 841 | 20, 842 | 97, 843 | 0, 844 | 126, 845 | 87, 846 | 80, 847 | 80, 848 | 99, 849 | 248, 850 | 204, 851 | 191, 852 | 71, 853 | 20, 854 | 97, 855 | 0, 856 | 58, 857 | 87, 858 | 96, 859 | 0, 860 | 128, 861 | 253, 862 | 91, 863 | 52, 864 | 97, 865 | 0, 866 | 122, 867 | 87, 868 | 129, 869 | 127, 870 | 255, 871 | 255, 872 | 255, 873 | 255, 874 | 255, 875 | 255, 876 | 255, 877 | 255, 878 | 255, 879 | 255, 880 | 255, 881 | 255, 882 | 255, 883 | 255, 884 | 255, 885 | 255, 886 | 255, 887 | 255, 888 | 255, 889 | 255, 890 | 255, 891 | 255, 892 | 255, 893 | 255, 894 | 255, 895 | 255, 896 | 255, 897 | 255, 898 | 255, 899 | 255, 900 | 255, 901 | 252, 902 | 54, 903 | 1, 904 | 18, 905 | 97, 906 | 0, 907 | 122, 908 | 87, 909 | 96, 910 | 32, 911 | 144, 912 | 96, 913 | 255, 914 | 96, 915 | 12, 916 | 84, 917 | 22, 918 | 144, 919 | 81, 920 | 144, 921 | 21, 922 | 21, 923 | 129, 924 | 82, 925 | 243, 926 | 91, 927 | 80, 928 | 128, 929 | 253, 930 | 91, 931 | 144, 932 | 145, 933 | 80, 934 | 52, 935 | 97, 936 | 2, 937 | 181, 938 | 87, 939 | 96, 940 | 32, 941 | 145, 942 | 130, 943 | 127, 944 | 255, 945 | 255, 946 | 255, 947 | 255, 948 | 255, 949 | 255, 950 | 255, 951 | 255, 952 | 255, 953 | 255, 954 | 255, 955 | 255, 956 | 255, 957 | 255, 958 | 255, 959 | 255, 960 | 255, 961 | 255, 962 | 255, 963 | 255, 964 | 255, 965 | 255, 966 | 255, 967 | 255, 968 | 255, 969 | 255, 970 | 255, 971 | 255, 972 | 255, 973 | 255, 974 | 255, 975 | 252, 976 | 54, 977 | 1, 978 | 18, 979 | 97, 980 | 2, 981 | 32, 982 | 87, 983 | 132, 984 | 115, 985 | 113, 986 | 9, 987 | 112, 988 | 158, 989 | 207, 990 | 169, 991 | 26, 992 | 128, 993 | 98, 994 | 111, 995 | 243, 996 | 152, 997 | 157, 998 | 104, 999 | 246, 1000 | 127, 1001 | 91, 1002 | 29, 1003 | 209, 1004 | 45, 1005 | 128, 1006 | 59, 1007 | 21, 1008 | 97, 1009 | 0, 1010 | 122, 1011 | 87, 1012 | 131, 1013 | 131, 1014 | 131, 1015 | 129, 1016 | 147, 1017 | 127, 1018 | 175, 1019 | 201, 1020 | 128, 1021 | 64, 1022 | 0, 1023 | 0, 1024 | 0, 1025 | 0, 1026 | 0, 1027 | 0, 1028 | 0, 1029 | 0, 1030 | 0, 1031 | 0, 1032 | 0, 1033 | 0, 1034 | 0, 1035 | 0, 1036 | 0, 1037 | 0, 1038 | 0, 1039 | 0, 1040 | 0, 1041 | 0, 1042 | 0, 1043 | 0, 1044 | 0, 1045 | 0, 1046 | 0, 1047 | 0, 1048 | 0, 1049 | 0, 1050 | 131, 1051 | 82, 1052 | 90, 1053 | 241, 1054 | 128, 1055 | 21, 1056 | 97, 1057 | 2, 1058 | 171, 1059 | 87, 1060 | 97, 1061 | 2, 1062 | 100, 1063 | 87, 1064 | 91, 1065 | 80, 1066 | 115, 1067 | 255, 1068 | 255, 1069 | 255, 1070 | 255, 1071 | 255, 1072 | 255, 1073 | 255, 1074 | 255, 1075 | 255, 1076 | 255, 1077 | 255, 1078 | 255, 1079 | 255, 1080 | 255, 1081 | 255, 1082 | 255, 1083 | 255, 1084 | 255, 1085 | 255, 1086 | 255, 1087 | 146, 1088 | 132, 1089 | 131, 1090 | 133, 1091 | 96, 1092 | 12, 1093 | 84, 1094 | 96, 1095 | 8, 1096 | 28, 1097 | 22, 1098 | 96, 1099 | 36, 1100 | 132, 1101 | 81, 1102 | 128, 1103 | 148, 1104 | 129, 1105 | 147, 1106 | 127, 1107 | 29, 1108 | 100, 1109 | 118, 1110 | 5, 1111 | 0, 1112 | 0, 1113 | 0, 1114 | 0, 1115 | 0, 1116 | 0, 1117 | 0, 1118 | 0, 1119 | 0, 1120 | 0, 1121 | 0, 1122 | 0, 1123 | 0, 1124 | 0, 1125 | 0, 1126 | 0, 1127 | 0, 1128 | 0, 1129 | 0, 1130 | 0, 1131 | 0, 1132 | 0, 1133 | 0, 1134 | 0, 1135 | 0, 1136 | 0, 1137 | 0, 1138 | 0, 1139 | 131, 1140 | 82, 1141 | 136, 1142 | 53, 1143 | 137, 1144 | 132, 1145 | 1, 1146 | 82, 1147 | 90, 1148 | 241, 1149 | 146, 1150 | 131, 1151 | 21, 1152 | 97, 1153 | 2, 1154 | 90, 1155 | 87, 1156 | 134, 1157 | 147, 1158 | 97, 1159 | 1, 1160 | 147, 1161 | 87, 1162 | 91, 1163 | 80, 1164 | 80, 1165 | 81, 1166 | 146, 1167 | 22, 1168 | 130, 1169 | 82, 1170 | 127, 1171 | 6, 1172 | 113, 1173 | 60, 1174 | 62, 1175 | 37, 1176 | 215, 1177 | 124, 1178 | 252, 1179 | 129, 1180 | 157, 1181 | 235, 1182 | 128, 1183 | 156, 1184 | 25, 1185 | 59, 1186 | 160, 1187 | 90, 1188 | 223, 1189 | 209, 1190 | 128, 1191 | 248, 1192 | 100, 1193 | 112, 1194 | 45, 1195 | 125, 1196 | 153, 1197 | 35, 1198 | 113, 1199 | 231, 1200 | 142, 1201 | 62, 1202 | 127, 1203 | 145, 1204 | 161, 1205 | 128, 1206 | 243, 1207 | 91, 1208 | 144, 1209 | 145, 1210 | 146, 1211 | 80, 1212 | 131, 1213 | 144, 1214 | 61, 1215 | 133, 1216 | 17, 1217 | 97, 1218 | 2, 1219 | 82, 1220 | 87, 1221 | 91, 1222 | 127, 1223 | 255, 1224 | 255, 1225 | 255, 1226 | 255, 1227 | 255, 1228 | 255, 1229 | 255, 1230 | 255, 1231 | 255, 1232 | 255, 1233 | 255, 1234 | 255, 1235 | 255, 1236 | 255, 1237 | 255, 1238 | 255, 1239 | 255, 1240 | 255, 1241 | 255, 1242 | 255, 1243 | 255, 1244 | 255, 1245 | 255, 1246 | 255, 1247 | 255, 1248 | 255, 1249 | 255, 1250 | 255, 1251 | 255, 1252 | 255, 1253 | 255, 1254 | 224, 1255 | 96, 1256 | 31, 1257 | 131, 1258 | 1, 1259 | 22, 1260 | 131, 1261 | 1, 1262 | 144, 1263 | 131, 1264 | 130, 1265 | 16, 1266 | 103, 1267 | 255, 1268 | 255, 1269 | 255, 1270 | 255, 1271 | 255, 1272 | 255, 1273 | 255, 1274 | 255, 1275 | 131, 1276 | 17, 1277 | 23, 1278 | 97, 1279 | 2, 1280 | 36, 1281 | 87, 1282 | 80, 1283 | 132, 1284 | 145, 1285 | 131, 1286 | 145, 1287 | 133, 1288 | 82, 1289 | 129, 1290 | 1, 1291 | 3, 1292 | 18, 1293 | 97, 1294 | 2, 1295 | 32, 1296 | 87, 1297 | 81, 1298 | 146, 1299 | 128, 1300 | 132, 1301 | 22, 1302 | 132, 1303 | 3, 1304 | 97, 1305 | 2, 1306 | 32, 1307 | 87, 1308 | 146, 1309 | 144, 1310 | 127, 1311 | 6, 1312 | 113, 1313 | 60, 1314 | 62, 1315 | 37, 1316 | 215, 1317 | 124, 1318 | 252, 1319 | 129, 1320 | 157, 1321 | 235, 1322 | 128, 1323 | 156, 1324 | 25, 1325 | 59, 1326 | 160, 1327 | 90, 1328 | 223, 1329 | 209, 1330 | 128, 1331 | 248, 1332 | 100, 1333 | 112, 1334 | 45, 1335 | 125, 1336 | 153, 1337 | 35, 1338 | 113, 1339 | 231, 1340 | 142, 1341 | 62, 1342 | 127, 1343 | 56, 1344 | 97, 1345 | 1, 1346 | 102, 1347 | 86, 1348 | 91, 1349 | 132, 1350 | 128, 1351 | 253, 1352 | 91, 1353 | 96, 1354 | 65, 1355 | 144, 1356 | 127, 1357 | 78, 1358 | 72, 1359 | 123, 1360 | 113, 1361 | 0, 1362 | 0, 1363 | 0, 1364 | 0, 1365 | 0, 1366 | 0, 1367 | 0, 1368 | 0, 1369 | 0, 1370 | 0, 1371 | 0, 1372 | 0, 1373 | 0, 1374 | 0, 1375 | 0, 1376 | 0, 1377 | 0, 1378 | 0, 1379 | 0, 1380 | 0, 1381 | 0, 1382 | 0, 1383 | 0, 1384 | 0, 1385 | 0, 1386 | 0, 1387 | 0, 1388 | 0, 1389 | 96, 1390 | 0, 1391 | 82, 1392 | 82, 1393 | 96, 1394 | 36, 1395 | 96, 1396 | 0, 1397 | 253, 1398 | 91, 1399 | 61, 1400 | 145, 1401 | 80, 1402 | 97, 1403 | 1, 1404 | 161, 1405 | 86, 1406 | 91, 1407 | 130, 1408 | 81, 1409 | 61, 1410 | 136, 1411 | 130, 1412 | 62, 1413 | 61, 1414 | 144, 1415 | 253, 1416 | 91, 1417 | 103, 1418 | 255, 1419 | 255, 1420 | 255, 1421 | 255, 1422 | 255, 1423 | 255, 1424 | 255, 1425 | 255, 1426 | 129, 1427 | 149, 1428 | 146, 1429 | 149, 1430 | 17, 1431 | 97, 1432 | 2, 1433 | 127, 1434 | 87, 1435 | 131, 1436 | 82, 1437 | 146, 1438 | 56, 1439 | 97, 1440 | 1, 1441 | 4, 1442 | 86, 1443 | 91, 1444 | 96, 1445 | 36, 1446 | 130, 1447 | 96, 1448 | 65, 1449 | 135, 1450 | 127, 1451 | 78, 1452 | 72, 1453 | 123, 1454 | 113, 1455 | 0, 1456 | 0, 1457 | 0, 1458 | 0, 1459 | 0, 1460 | 0, 1461 | 0, 1462 | 0, 1463 | 0, 1464 | 0, 1465 | 0, 1466 | 0, 1467 | 0, 1468 | 0, 1469 | 0, 1470 | 0, 1471 | 0, 1472 | 0, 1473 | 0, 1474 | 0, 1475 | 0, 1476 | 0, 1477 | 0, 1478 | 0, 1479 | 0, 1480 | 0, 1481 | 0, 1482 | 0, 1483 | 131, 1484 | 82, 1485 | 82, 1486 | 253, 1487 | 91, 1488 | 132, 1489 | 81, 1490 | 61, 1491 | 135, 1492 | 130, 1493 | 62, 1494 | 61, 1495 | 144, 1496 | 253, 1497 | 91, 1498 | 131, 1499 | 128, 1500 | 253, 1501 | 254, 1502 | 161, 1503 | 100, 1504 | 115, 1505 | 111, 1506 | 108, 1507 | 99, 1508 | 67, 1509 | 0, 1510 | 8, 1511 | 17, 1512 | 0, 1513 | 10, 1514 | ]; 1515 | ///The deployed bytecode of the contract. 1516 | pub static DEPLOYPROXYV1_DEPLOYED_BYTECODE: ::ethers::core::types::Bytes = ::ethers::core::types::Bytes::from_static( 1517 | __DEPLOYED_BYTECODE, 1518 | ); 1519 | pub struct DeployProxyV1(::ethers::contract::Contract); 1520 | impl ::core::clone::Clone for DeployProxyV1 { 1521 | fn clone(&self) -> Self { 1522 | Self(::core::clone::Clone::clone(&self.0)) 1523 | } 1524 | } 1525 | impl ::core::ops::Deref for DeployProxyV1 { 1526 | type Target = ::ethers::contract::Contract; 1527 | fn deref(&self) -> &Self::Target { 1528 | &self.0 1529 | } 1530 | } 1531 | impl ::core::ops::DerefMut for DeployProxyV1 { 1532 | fn deref_mut(&mut self) -> &mut Self::Target { 1533 | &mut self.0 1534 | } 1535 | } 1536 | impl ::core::fmt::Debug for DeployProxyV1 { 1537 | fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { 1538 | f.debug_tuple(stringify!(DeployProxyV1)).field(&self.address()).finish() 1539 | } 1540 | } 1541 | impl DeployProxyV1 { 1542 | /// Creates a new contract instance with the specified `ethers` client at 1543 | /// `address`. The contract derefs to a `ethers::Contract` object. 1544 | pub fn new>( 1545 | address: T, 1546 | client: ::std::sync::Arc, 1547 | ) -> Self { 1548 | Self( 1549 | ::ethers::contract::Contract::new( 1550 | address.into(), 1551 | DEPLOYPROXYV1_ABI.clone(), 1552 | client, 1553 | ), 1554 | ) 1555 | } 1556 | /// Constructs the general purpose `Deployer` instance based on the provided constructor arguments and sends it. 1557 | /// Returns a new instance of a deployer that returns an instance of this contract after sending the transaction 1558 | /// 1559 | /// Notes: 1560 | /// - If there are no constructor arguments, you should pass `()` as the argument. 1561 | /// - The default poll duration is 7 seconds. 1562 | /// - The default number of confirmations is 1 block. 1563 | /// 1564 | /// 1565 | /// # Example 1566 | /// 1567 | /// Generate contract bindings with `abigen!` and deploy a new contract instance. 1568 | /// 1569 | /// *Note*: this requires a `bytecode` and `abi` object in the `greeter.json` artifact. 1570 | /// 1571 | /// ```ignore 1572 | /// # async fn deploy(client: ::std::sync::Arc) { 1573 | /// abigen!(Greeter, "../greeter.json"); 1574 | /// 1575 | /// let greeter_contract = Greeter::deploy(client, "Hello world!".to_string()).unwrap().send().await.unwrap(); 1576 | /// let msg = greeter_contract.greet().call().await.unwrap(); 1577 | /// # } 1578 | /// ``` 1579 | pub fn deploy( 1580 | client: ::std::sync::Arc, 1581 | constructor_args: T, 1582 | ) -> ::core::result::Result< 1583 | ::ethers::contract::builders::ContractDeployer, 1584 | ::ethers::contract::ContractError, 1585 | > { 1586 | let factory = ::ethers::contract::ContractFactory::new( 1587 | DEPLOYPROXYV1_ABI.clone(), 1588 | DEPLOYPROXYV1_BYTECODE.clone().into(), 1589 | client, 1590 | ); 1591 | let deployer = factory.deploy(constructor_args)?; 1592 | let deployer = ::ethers::contract::ContractDeployer::new(deployer); 1593 | Ok(deployer) 1594 | } 1595 | ///Calls the contract's `IS_SCRIPT` (0xf8ccbf47) function 1596 | pub fn is_script(&self) -> ::ethers::contract::builders::ContractCall { 1597 | self.0 1598 | .method_hash([248, 204, 191, 71], ()) 1599 | .expect("method not found (this should never happen)") 1600 | } 1601 | ///Calls the contract's `run` (0xef6ac0f0) function 1602 | pub fn run( 1603 | &self, 1604 | salt: [u8; 32], 1605 | ) -> ::ethers::contract::builders::ContractCall { 1606 | self.0 1607 | .method_hash([239, 106, 192, 240], salt) 1608 | .expect("method not found (this should never happen)") 1609 | } 1610 | ///Gets the contract's `proxy` event 1611 | pub fn proxy_filter( 1612 | &self, 1613 | ) -> ::ethers::contract::builders::Event<::std::sync::Arc, M, ProxyFilter> { 1614 | self.0.event() 1615 | } 1616 | /// Returns an `Event` builder for all the events of this contract. 1617 | pub fn events( 1618 | &self, 1619 | ) -> ::ethers::contract::builders::Event<::std::sync::Arc, M, ProxyFilter> { 1620 | self.0.event_with_filter(::core::default::Default::default()) 1621 | } 1622 | } 1623 | impl From<::ethers::contract::Contract> 1624 | for DeployProxyV1 { 1625 | fn from(contract: ::ethers::contract::Contract) -> Self { 1626 | Self::new(contract.address(), contract.client()) 1627 | } 1628 | } 1629 | #[derive( 1630 | Clone, 1631 | ::ethers::contract::EthEvent, 1632 | ::ethers::contract::EthDisplay, 1633 | Default, 1634 | Debug, 1635 | PartialEq, 1636 | Eq, 1637 | Hash 1638 | )] 1639 | #[ethevent(name = "proxy", abi = "proxy(address)")] 1640 | pub struct ProxyFilter(pub ::ethers::core::types::Address); 1641 | ///Container type for all input parameters for the `IS_SCRIPT` function with signature `IS_SCRIPT()` and selector `0xf8ccbf47` 1642 | #[derive( 1643 | Clone, 1644 | ::ethers::contract::EthCall, 1645 | ::ethers::contract::EthDisplay, 1646 | Default, 1647 | Debug, 1648 | PartialEq, 1649 | Eq, 1650 | Hash 1651 | )] 1652 | #[ethcall(name = "IS_SCRIPT", abi = "IS_SCRIPT()")] 1653 | pub struct IsScriptCall; 1654 | ///Container type for all input parameters for the `run` function with signature `run(bytes32)` and selector `0xef6ac0f0` 1655 | #[derive( 1656 | Clone, 1657 | ::ethers::contract::EthCall, 1658 | ::ethers::contract::EthDisplay, 1659 | Default, 1660 | Debug, 1661 | PartialEq, 1662 | Eq, 1663 | Hash 1664 | )] 1665 | #[ethcall(name = "run", abi = "run(bytes32)")] 1666 | pub struct RunCall { 1667 | pub salt: [u8; 32], 1668 | } 1669 | ///Container type for all of the contract's call 1670 | #[derive(Clone, ::ethers::contract::EthAbiType, Debug, PartialEq, Eq, Hash)] 1671 | pub enum DeployProxyV1Calls { 1672 | IsScript(IsScriptCall), 1673 | Run(RunCall), 1674 | } 1675 | impl ::ethers::core::abi::AbiDecode for DeployProxyV1Calls { 1676 | fn decode( 1677 | data: impl AsRef<[u8]>, 1678 | ) -> ::core::result::Result { 1679 | let data = data.as_ref(); 1680 | if let Ok(decoded) 1681 | = ::decode(data) { 1682 | return Ok(Self::IsScript(decoded)); 1683 | } 1684 | if let Ok(decoded) 1685 | = ::decode(data) { 1686 | return Ok(Self::Run(decoded)); 1687 | } 1688 | Err(::ethers::core::abi::Error::InvalidData.into()) 1689 | } 1690 | } 1691 | impl ::ethers::core::abi::AbiEncode for DeployProxyV1Calls { 1692 | fn encode(self) -> Vec { 1693 | match self { 1694 | Self::IsScript(element) => { 1695 | ::ethers::core::abi::AbiEncode::encode(element) 1696 | } 1697 | Self::Run(element) => ::ethers::core::abi::AbiEncode::encode(element), 1698 | } 1699 | } 1700 | } 1701 | impl ::core::fmt::Display for DeployProxyV1Calls { 1702 | fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { 1703 | match self { 1704 | Self::IsScript(element) => ::core::fmt::Display::fmt(element, f), 1705 | Self::Run(element) => ::core::fmt::Display::fmt(element, f), 1706 | } 1707 | } 1708 | } 1709 | impl ::core::convert::From for DeployProxyV1Calls { 1710 | fn from(value: IsScriptCall) -> Self { 1711 | Self::IsScript(value) 1712 | } 1713 | } 1714 | impl ::core::convert::From for DeployProxyV1Calls { 1715 | fn from(value: RunCall) -> Self { 1716 | Self::Run(value) 1717 | } 1718 | } 1719 | ///Container type for all return fields from the `IS_SCRIPT` function with signature `IS_SCRIPT()` and selector `0xf8ccbf47` 1720 | #[derive( 1721 | Clone, 1722 | ::ethers::contract::EthAbiType, 1723 | ::ethers::contract::EthAbiCodec, 1724 | Default, 1725 | Debug, 1726 | PartialEq, 1727 | Eq, 1728 | Hash 1729 | )] 1730 | pub struct IsScriptReturn(pub bool); 1731 | } 1732 | -------------------------------------------------------------------------------- /src/bindings/i_mev_weth.rs: -------------------------------------------------------------------------------- 1 | pub use i_mev_weth::*; 2 | /// This module was auto-generated with ethers-rs Abigen. 3 | /// More information at: 4 | #[allow( 5 | clippy::enum_variant_names, 6 | clippy::too_many_arguments, 7 | clippy::upper_case_acronyms, 8 | clippy::type_complexity, 9 | dead_code, 10 | non_camel_case_types, 11 | )] 12 | pub mod i_mev_weth { 13 | #[rustfmt::skip] 14 | const __ABI: &str = "[{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\",\"components\":[]}],\"stateMutability\":\"nonpayable\",\"type\":\"function\",\"name\":\"addMev\",\"outputs\":[]},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\",\"components\":[]},{\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\",\"components\":[]}],\"stateMutability\":\"nonpayable\",\"type\":\"function\",\"name\":\"addMev\",\"outputs\":[]},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\",\"components\":[]}],\"stateMutability\":\"nonpayable\",\"type\":\"function\",\"name\":\"getMev\",\"outputs\":[]},{\"inputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\",\"name\":\"getMev\",\"outputs\":[]},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\",\"components\":[]},{\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\",\"components\":[]}],\"stateMutability\":\"nonpayable\",\"type\":\"function\",\"name\":\"getMev\",\"outputs\":[]},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\",\"components\":[]}],\"stateMutability\":\"nonpayable\",\"type\":\"function\",\"name\":\"getMev\",\"outputs\":[]},{\"inputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\",\"name\":\"mev\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\",\"components\":[]}]}]"; 15 | ///The parsed JSON ABI of the contract. 16 | pub static IMEVWETH_ABI: ::ethers::contract::Lazy<::ethers::core::abi::Abi> = ::ethers::contract::Lazy::new(|| 17 | ::ethers::core::utils::__serde_json::from_str(__ABI).expect("ABI is always valid")); 18 | pub struct IMevWeth(::ethers::contract::Contract); 19 | impl ::core::clone::Clone for IMevWeth { 20 | fn clone(&self) -> Self { 21 | Self(::core::clone::Clone::clone(&self.0)) 22 | } 23 | } 24 | impl ::core::ops::Deref for IMevWeth { 25 | type Target = ::ethers::contract::Contract; 26 | fn deref(&self) -> &Self::Target { 27 | &self.0 28 | } 29 | } 30 | impl ::core::ops::DerefMut for IMevWeth { 31 | fn deref_mut(&mut self) -> &mut Self::Target { 32 | &mut self.0 33 | } 34 | } 35 | impl ::core::fmt::Debug for IMevWeth { 36 | fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { 37 | f.debug_tuple(stringify!(IMevWeth)).field(&self.address()).finish() 38 | } 39 | } 40 | impl IMevWeth { 41 | /// Creates a new contract instance with the specified `ethers` client at 42 | /// `address`. The contract derefs to a `ethers::Contract` object. 43 | pub fn new>( 44 | address: T, 45 | client: ::std::sync::Arc, 46 | ) -> Self { 47 | Self( 48 | ::ethers::contract::Contract::new( 49 | address.into(), 50 | IMEVWETH_ABI.clone(), 51 | client, 52 | ), 53 | ) 54 | } 55 | ///Calls the contract's `addMev` (0x5992bfdd) function 56 | pub fn add_mev( 57 | &self, 58 | value: ::ethers::core::types::U256, 59 | ) -> ::ethers::contract::builders::ContractCall { 60 | self.0 61 | .method_hash([89, 146, 191, 221], value) 62 | .expect("method not found (this should never happen)") 63 | } 64 | ///Calls the contract's `addMev` (0xf801cf60) function 65 | pub fn add_mev_with_from( 66 | &self, 67 | from: ::ethers::core::types::Address, 68 | value: ::ethers::core::types::U256, 69 | ) -> ::ethers::contract::builders::ContractCall { 70 | self.0 71 | .method_hash([248, 1, 207, 96], (from, value)) 72 | .expect("method not found (this should never happen)") 73 | } 74 | ///Calls the contract's `getMev` (0x848316fb) function 75 | pub fn get_mev_1( 76 | &self, 77 | to: ::ethers::core::types::Address, 78 | ) -> ::ethers::contract::builders::ContractCall { 79 | self.0 80 | .method_hash([132, 131, 22, 251], to) 81 | .expect("method not found (this should never happen)") 82 | } 83 | ///Calls the contract's `getMev` (0x99c24631) function 84 | pub fn get_mev_0(&self) -> ::ethers::contract::builders::ContractCall { 85 | self.0 86 | .method_hash([153, 194, 70, 49], ()) 87 | .expect("method not found (this should never happen)") 88 | } 89 | ///Calls the contract's `getMev` (0xea3e3e3e) function 90 | pub fn get_mev_3( 91 | &self, 92 | to: ::ethers::core::types::Address, 93 | value: ::ethers::core::types::U256, 94 | ) -> ::ethers::contract::builders::ContractCall { 95 | self.0 96 | .method_hash([234, 62, 62, 62], (to, value)) 97 | .expect("method not found (this should never happen)") 98 | } 99 | ///Calls the contract's `getMev` (0xf8e5c102) function 100 | pub fn get_mev_2( 101 | &self, 102 | value: ::ethers::core::types::U256, 103 | ) -> ::ethers::contract::builders::ContractCall { 104 | self.0 105 | .method_hash([248, 229, 193, 2], value) 106 | .expect("method not found (this should never happen)") 107 | } 108 | ///Calls the contract's `mev` (0x13c9b33a) function 109 | pub fn mev( 110 | &self, 111 | ) -> ::ethers::contract::builders::ContractCall { 112 | self.0 113 | .method_hash([19, 201, 179, 58], ()) 114 | .expect("method not found (this should never happen)") 115 | } 116 | } 117 | impl From<::ethers::contract::Contract> 118 | for IMevWeth { 119 | fn from(contract: ::ethers::contract::Contract) -> Self { 120 | Self::new(contract.address(), contract.client()) 121 | } 122 | } 123 | ///Container type for all input parameters for the `addMev` function with signature `addMev(uint256)` and selector `0x5992bfdd` 124 | #[derive( 125 | Clone, 126 | ::ethers::contract::EthCall, 127 | ::ethers::contract::EthDisplay, 128 | Default, 129 | Debug, 130 | PartialEq, 131 | Eq, 132 | Hash 133 | )] 134 | #[ethcall(name = "addMev", abi = "addMev(uint256)")] 135 | pub struct AddMevCall { 136 | pub value: ::ethers::core::types::U256, 137 | } 138 | ///Container type for all input parameters for the `addMev` function with signature `addMev(address,uint256)` and selector `0xf801cf60` 139 | #[derive( 140 | Clone, 141 | ::ethers::contract::EthCall, 142 | ::ethers::contract::EthDisplay, 143 | Default, 144 | Debug, 145 | PartialEq, 146 | Eq, 147 | Hash 148 | )] 149 | #[ethcall(name = "addMev", abi = "addMev(address,uint256)")] 150 | pub struct AddMevWithFromCall { 151 | pub from: ::ethers::core::types::Address, 152 | pub value: ::ethers::core::types::U256, 153 | } 154 | ///Container type for all input parameters for the `getMev` function with signature `getMev(address)` and selector `0x848316fb` 155 | #[derive( 156 | Clone, 157 | ::ethers::contract::EthCall, 158 | ::ethers::contract::EthDisplay, 159 | Default, 160 | Debug, 161 | PartialEq, 162 | Eq, 163 | Hash 164 | )] 165 | #[ethcall(name = "getMev", abi = "getMev(address)")] 166 | pub struct GetMev1Call { 167 | pub to: ::ethers::core::types::Address, 168 | } 169 | ///Container type for all input parameters for the `getMev` function with signature `getMev()` and selector `0x99c24631` 170 | #[derive( 171 | Clone, 172 | ::ethers::contract::EthCall, 173 | ::ethers::contract::EthDisplay, 174 | Default, 175 | Debug, 176 | PartialEq, 177 | Eq, 178 | Hash 179 | )] 180 | #[ethcall(name = "getMev", abi = "getMev()")] 181 | pub struct GetMev0Call; 182 | ///Container type for all input parameters for the `getMev` function with signature `getMev(address,uint256)` and selector `0xea3e3e3e` 183 | #[derive( 184 | Clone, 185 | ::ethers::contract::EthCall, 186 | ::ethers::contract::EthDisplay, 187 | Default, 188 | Debug, 189 | PartialEq, 190 | Eq, 191 | Hash 192 | )] 193 | #[ethcall(name = "getMev", abi = "getMev(address,uint256)")] 194 | pub struct GetMev3Call { 195 | pub to: ::ethers::core::types::Address, 196 | pub value: ::ethers::core::types::U256, 197 | } 198 | ///Container type for all input parameters for the `getMev` function with signature `getMev(uint256)` and selector `0xf8e5c102` 199 | #[derive( 200 | Clone, 201 | ::ethers::contract::EthCall, 202 | ::ethers::contract::EthDisplay, 203 | Default, 204 | Debug, 205 | PartialEq, 206 | Eq, 207 | Hash 208 | )] 209 | #[ethcall(name = "getMev", abi = "getMev(uint256)")] 210 | pub struct GetMev2Call { 211 | pub value: ::ethers::core::types::U256, 212 | } 213 | ///Container type for all input parameters for the `mev` function with signature `mev()` and selector `0x13c9b33a` 214 | #[derive( 215 | Clone, 216 | ::ethers::contract::EthCall, 217 | ::ethers::contract::EthDisplay, 218 | Default, 219 | Debug, 220 | PartialEq, 221 | Eq, 222 | Hash 223 | )] 224 | #[ethcall(name = "mev", abi = "mev()")] 225 | pub struct MevCall; 226 | ///Container type for all of the contract's call 227 | #[derive(Clone, ::ethers::contract::EthAbiType, Debug, PartialEq, Eq, Hash)] 228 | pub enum IMevWethCalls { 229 | AddMev(AddMevCall), 230 | AddMevWithFrom(AddMevWithFromCall), 231 | GetMev1(GetMev1Call), 232 | GetMev0(GetMev0Call), 233 | GetMev3(GetMev3Call), 234 | GetMev2(GetMev2Call), 235 | Mev(MevCall), 236 | } 237 | impl ::ethers::core::abi::AbiDecode for IMevWethCalls { 238 | fn decode( 239 | data: impl AsRef<[u8]>, 240 | ) -> ::core::result::Result { 241 | let data = data.as_ref(); 242 | if let Ok(decoded) 243 | = ::decode(data) { 244 | return Ok(Self::AddMev(decoded)); 245 | } 246 | if let Ok(decoded) 247 | = ::decode(data) { 248 | return Ok(Self::AddMevWithFrom(decoded)); 249 | } 250 | if let Ok(decoded) 251 | = ::decode(data) { 252 | return Ok(Self::GetMev1(decoded)); 253 | } 254 | if let Ok(decoded) 255 | = ::decode(data) { 256 | return Ok(Self::GetMev0(decoded)); 257 | } 258 | if let Ok(decoded) 259 | = ::decode(data) { 260 | return Ok(Self::GetMev3(decoded)); 261 | } 262 | if let Ok(decoded) 263 | = ::decode(data) { 264 | return Ok(Self::GetMev2(decoded)); 265 | } 266 | if let Ok(decoded) 267 | = ::decode(data) { 268 | return Ok(Self::Mev(decoded)); 269 | } 270 | Err(::ethers::core::abi::Error::InvalidData.into()) 271 | } 272 | } 273 | impl ::ethers::core::abi::AbiEncode for IMevWethCalls { 274 | fn encode(self) -> Vec { 275 | match self { 276 | Self::AddMev(element) => ::ethers::core::abi::AbiEncode::encode(element), 277 | Self::AddMevWithFrom(element) => { 278 | ::ethers::core::abi::AbiEncode::encode(element) 279 | } 280 | Self::GetMev1(element) => ::ethers::core::abi::AbiEncode::encode(element), 281 | Self::GetMev0(element) => ::ethers::core::abi::AbiEncode::encode(element), 282 | Self::GetMev3(element) => ::ethers::core::abi::AbiEncode::encode(element), 283 | Self::GetMev2(element) => ::ethers::core::abi::AbiEncode::encode(element), 284 | Self::Mev(element) => ::ethers::core::abi::AbiEncode::encode(element), 285 | } 286 | } 287 | } 288 | impl ::core::fmt::Display for IMevWethCalls { 289 | fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { 290 | match self { 291 | Self::AddMev(element) => ::core::fmt::Display::fmt(element, f), 292 | Self::AddMevWithFrom(element) => ::core::fmt::Display::fmt(element, f), 293 | Self::GetMev1(element) => ::core::fmt::Display::fmt(element, f), 294 | Self::GetMev0(element) => ::core::fmt::Display::fmt(element, f), 295 | Self::GetMev3(element) => ::core::fmt::Display::fmt(element, f), 296 | Self::GetMev2(element) => ::core::fmt::Display::fmt(element, f), 297 | Self::Mev(element) => ::core::fmt::Display::fmt(element, f), 298 | } 299 | } 300 | } 301 | impl ::core::convert::From for IMevWethCalls { 302 | fn from(value: AddMevCall) -> Self { 303 | Self::AddMev(value) 304 | } 305 | } 306 | impl ::core::convert::From for IMevWethCalls { 307 | fn from(value: AddMevWithFromCall) -> Self { 308 | Self::AddMevWithFrom(value) 309 | } 310 | } 311 | impl ::core::convert::From for IMevWethCalls { 312 | fn from(value: GetMev1Call) -> Self { 313 | Self::GetMev1(value) 314 | } 315 | } 316 | impl ::core::convert::From for IMevWethCalls { 317 | fn from(value: GetMev0Call) -> Self { 318 | Self::GetMev0(value) 319 | } 320 | } 321 | impl ::core::convert::From for IMevWethCalls { 322 | fn from(value: GetMev3Call) -> Self { 323 | Self::GetMev3(value) 324 | } 325 | } 326 | impl ::core::convert::From for IMevWethCalls { 327 | fn from(value: GetMev2Call) -> Self { 328 | Self::GetMev2(value) 329 | } 330 | } 331 | impl ::core::convert::From for IMevWethCalls { 332 | fn from(value: MevCall) -> Self { 333 | Self::Mev(value) 334 | } 335 | } 336 | ///Container type for all return fields from the `mev` function with signature `mev()` and selector `0x13c9b33a` 337 | #[derive( 338 | Clone, 339 | ::ethers::contract::EthAbiType, 340 | ::ethers::contract::EthAbiCodec, 341 | Default, 342 | Debug, 343 | PartialEq, 344 | Eq, 345 | Hash 346 | )] 347 | pub struct MevReturn(pub ::ethers::core::types::U256); 348 | } 349 | -------------------------------------------------------------------------------- /src/bindings/ierc20.rs: -------------------------------------------------------------------------------- 1 | pub use ierc20::*; 2 | /// This module was auto-generated with ethers-rs Abigen. 3 | /// More information at: 4 | #[allow( 5 | clippy::enum_variant_names, 6 | clippy::too_many_arguments, 7 | clippy::upper_case_acronyms, 8 | clippy::type_complexity, 9 | dead_code, 10 | non_camel_case_types, 11 | )] 12 | pub mod ierc20 { 13 | #[rustfmt::skip] 14 | const __ABI: &str = "[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\",\"components\":[],\"indexed\":true},{\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\",\"components\":[],\"indexed\":true},{\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\",\"components\":[],\"indexed\":false}],\"type\":\"event\",\"name\":\"Approval\",\"outputs\":[],\"anonymous\":false},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\",\"components\":[],\"indexed\":true},{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\",\"components\":[],\"indexed\":true},{\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\",\"components\":[],\"indexed\":false}],\"type\":\"event\",\"name\":\"Transfer\",\"outputs\":[],\"anonymous\":false},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\",\"components\":[]},{\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\",\"components\":[]}],\"stateMutability\":\"view\",\"type\":\"function\",\"name\":\"allowance\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\",\"components\":[]}]},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\",\"components\":[]},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\",\"components\":[]}],\"stateMutability\":\"nonpayable\",\"type\":\"function\",\"name\":\"approve\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\",\"components\":[]}]},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\",\"components\":[]}],\"stateMutability\":\"view\",\"type\":\"function\",\"name\":\"balanceOf\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\",\"components\":[]}]},{\"inputs\":[],\"stateMutability\":\"view\",\"type\":\"function\",\"name\":\"totalSupply\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\",\"components\":[]}]},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\",\"components\":[]},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\",\"components\":[]}],\"stateMutability\":\"nonpayable\",\"type\":\"function\",\"name\":\"transfer\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\",\"components\":[]}]},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\",\"components\":[]},{\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\",\"components\":[]},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\",\"components\":[]}],\"stateMutability\":\"nonpayable\",\"type\":\"function\",\"name\":\"transferFrom\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\",\"components\":[]}]}]"; 15 | ///The parsed JSON ABI of the contract. 16 | pub static IERC20_ABI: ::ethers::contract::Lazy<::ethers::core::abi::Abi> = ::ethers::contract::Lazy::new(|| 17 | ::ethers::core::utils::__serde_json::from_str(__ABI).expect("ABI is always valid")); 18 | pub struct IERC20(::ethers::contract::Contract); 19 | impl ::core::clone::Clone for IERC20 { 20 | fn clone(&self) -> Self { 21 | Self(::core::clone::Clone::clone(&self.0)) 22 | } 23 | } 24 | impl ::core::ops::Deref for IERC20 { 25 | type Target = ::ethers::contract::Contract; 26 | fn deref(&self) -> &Self::Target { 27 | &self.0 28 | } 29 | } 30 | impl ::core::ops::DerefMut for IERC20 { 31 | fn deref_mut(&mut self) -> &mut Self::Target { 32 | &mut self.0 33 | } 34 | } 35 | impl ::core::fmt::Debug for IERC20 { 36 | fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { 37 | f.debug_tuple(stringify!(IERC20)).field(&self.address()).finish() 38 | } 39 | } 40 | impl IERC20 { 41 | /// Creates a new contract instance with the specified `ethers` client at 42 | /// `address`. The contract derefs to a `ethers::Contract` object. 43 | pub fn new>( 44 | address: T, 45 | client: ::std::sync::Arc, 46 | ) -> Self { 47 | Self( 48 | ::ethers::contract::Contract::new( 49 | address.into(), 50 | IERC20_ABI.clone(), 51 | client, 52 | ), 53 | ) 54 | } 55 | ///Calls the contract's `allowance` (0xdd62ed3e) function 56 | pub fn allowance( 57 | &self, 58 | owner: ::ethers::core::types::Address, 59 | spender: ::ethers::core::types::Address, 60 | ) -> ::ethers::contract::builders::ContractCall { 61 | self.0 62 | .method_hash([221, 98, 237, 62], (owner, spender)) 63 | .expect("method not found (this should never happen)") 64 | } 65 | ///Calls the contract's `approve` (0x095ea7b3) function 66 | pub fn approve( 67 | &self, 68 | spender: ::ethers::core::types::Address, 69 | amount: ::ethers::core::types::U256, 70 | ) -> ::ethers::contract::builders::ContractCall { 71 | self.0 72 | .method_hash([9, 94, 167, 179], (spender, amount)) 73 | .expect("method not found (this should never happen)") 74 | } 75 | ///Calls the contract's `balanceOf` (0x70a08231) function 76 | pub fn balance_of( 77 | &self, 78 | account: ::ethers::core::types::Address, 79 | ) -> ::ethers::contract::builders::ContractCall { 80 | self.0 81 | .method_hash([112, 160, 130, 49], account) 82 | .expect("method not found (this should never happen)") 83 | } 84 | ///Calls the contract's `totalSupply` (0x18160ddd) function 85 | pub fn total_supply( 86 | &self, 87 | ) -> ::ethers::contract::builders::ContractCall { 88 | self.0 89 | .method_hash([24, 22, 13, 221], ()) 90 | .expect("method not found (this should never happen)") 91 | } 92 | ///Calls the contract's `transfer` (0xa9059cbb) function 93 | pub fn transfer( 94 | &self, 95 | recipient: ::ethers::core::types::Address, 96 | amount: ::ethers::core::types::U256, 97 | ) -> ::ethers::contract::builders::ContractCall { 98 | self.0 99 | .method_hash([169, 5, 156, 187], (recipient, amount)) 100 | .expect("method not found (this should never happen)") 101 | } 102 | ///Calls the contract's `transferFrom` (0x23b872dd) function 103 | pub fn transfer_from( 104 | &self, 105 | sender: ::ethers::core::types::Address, 106 | recipient: ::ethers::core::types::Address, 107 | amount: ::ethers::core::types::U256, 108 | ) -> ::ethers::contract::builders::ContractCall { 109 | self.0 110 | .method_hash([35, 184, 114, 221], (sender, recipient, amount)) 111 | .expect("method not found (this should never happen)") 112 | } 113 | ///Gets the contract's `Approval` event 114 | pub fn approval_filter( 115 | &self, 116 | ) -> ::ethers::contract::builders::Event< 117 | ::std::sync::Arc, 118 | M, 119 | ApprovalFilter, 120 | > { 121 | self.0.event() 122 | } 123 | ///Gets the contract's `Transfer` event 124 | pub fn transfer_filter( 125 | &self, 126 | ) -> ::ethers::contract::builders::Event< 127 | ::std::sync::Arc, 128 | M, 129 | TransferFilter, 130 | > { 131 | self.0.event() 132 | } 133 | /// Returns an `Event` builder for all the events of this contract. 134 | pub fn events( 135 | &self, 136 | ) -> ::ethers::contract::builders::Event<::std::sync::Arc, M, IERC20Events> { 137 | self.0.event_with_filter(::core::default::Default::default()) 138 | } 139 | } 140 | impl From<::ethers::contract::Contract> 141 | for IERC20 { 142 | fn from(contract: ::ethers::contract::Contract) -> Self { 143 | Self::new(contract.address(), contract.client()) 144 | } 145 | } 146 | #[derive( 147 | Clone, 148 | ::ethers::contract::EthEvent, 149 | ::ethers::contract::EthDisplay, 150 | Default, 151 | Debug, 152 | PartialEq, 153 | Eq, 154 | Hash 155 | )] 156 | #[ethevent(name = "Approval", abi = "Approval(address,address,uint256)")] 157 | pub struct ApprovalFilter { 158 | #[ethevent(indexed)] 159 | pub owner: ::ethers::core::types::Address, 160 | #[ethevent(indexed)] 161 | pub spender: ::ethers::core::types::Address, 162 | pub value: ::ethers::core::types::U256, 163 | } 164 | #[derive( 165 | Clone, 166 | ::ethers::contract::EthEvent, 167 | ::ethers::contract::EthDisplay, 168 | Default, 169 | Debug, 170 | PartialEq, 171 | Eq, 172 | Hash 173 | )] 174 | #[ethevent(name = "Transfer", abi = "Transfer(address,address,uint256)")] 175 | pub struct TransferFilter { 176 | #[ethevent(indexed)] 177 | pub from: ::ethers::core::types::Address, 178 | #[ethevent(indexed)] 179 | pub to: ::ethers::core::types::Address, 180 | pub value: ::ethers::core::types::U256, 181 | } 182 | ///Container type for all of the contract's events 183 | #[derive(Clone, ::ethers::contract::EthAbiType, Debug, PartialEq, Eq, Hash)] 184 | pub enum IERC20Events { 185 | ApprovalFilter(ApprovalFilter), 186 | TransferFilter(TransferFilter), 187 | } 188 | impl ::ethers::contract::EthLogDecode for IERC20Events { 189 | fn decode_log( 190 | log: &::ethers::core::abi::RawLog, 191 | ) -> ::core::result::Result { 192 | if let Ok(decoded) = ApprovalFilter::decode_log(log) { 193 | return Ok(IERC20Events::ApprovalFilter(decoded)); 194 | } 195 | if let Ok(decoded) = TransferFilter::decode_log(log) { 196 | return Ok(IERC20Events::TransferFilter(decoded)); 197 | } 198 | Err(::ethers::core::abi::Error::InvalidData) 199 | } 200 | } 201 | impl ::core::fmt::Display for IERC20Events { 202 | fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { 203 | match self { 204 | Self::ApprovalFilter(element) => ::core::fmt::Display::fmt(element, f), 205 | Self::TransferFilter(element) => ::core::fmt::Display::fmt(element, f), 206 | } 207 | } 208 | } 209 | impl ::core::convert::From for IERC20Events { 210 | fn from(value: ApprovalFilter) -> Self { 211 | Self::ApprovalFilter(value) 212 | } 213 | } 214 | impl ::core::convert::From for IERC20Events { 215 | fn from(value: TransferFilter) -> Self { 216 | Self::TransferFilter(value) 217 | } 218 | } 219 | ///Container type for all input parameters for the `allowance` function with signature `allowance(address,address)` and selector `0xdd62ed3e` 220 | #[derive( 221 | Clone, 222 | ::ethers::contract::EthCall, 223 | ::ethers::contract::EthDisplay, 224 | Default, 225 | Debug, 226 | PartialEq, 227 | Eq, 228 | Hash 229 | )] 230 | #[ethcall(name = "allowance", abi = "allowance(address,address)")] 231 | pub struct AllowanceCall { 232 | pub owner: ::ethers::core::types::Address, 233 | pub spender: ::ethers::core::types::Address, 234 | } 235 | ///Container type for all input parameters for the `approve` function with signature `approve(address,uint256)` and selector `0x095ea7b3` 236 | #[derive( 237 | Clone, 238 | ::ethers::contract::EthCall, 239 | ::ethers::contract::EthDisplay, 240 | Default, 241 | Debug, 242 | PartialEq, 243 | Eq, 244 | Hash 245 | )] 246 | #[ethcall(name = "approve", abi = "approve(address,uint256)")] 247 | pub struct ApproveCall { 248 | pub spender: ::ethers::core::types::Address, 249 | pub amount: ::ethers::core::types::U256, 250 | } 251 | ///Container type for all input parameters for the `balanceOf` function with signature `balanceOf(address)` and selector `0x70a08231` 252 | #[derive( 253 | Clone, 254 | ::ethers::contract::EthCall, 255 | ::ethers::contract::EthDisplay, 256 | Default, 257 | Debug, 258 | PartialEq, 259 | Eq, 260 | Hash 261 | )] 262 | #[ethcall(name = "balanceOf", abi = "balanceOf(address)")] 263 | pub struct BalanceOfCall { 264 | pub account: ::ethers::core::types::Address, 265 | } 266 | ///Container type for all input parameters for the `totalSupply` function with signature `totalSupply()` and selector `0x18160ddd` 267 | #[derive( 268 | Clone, 269 | ::ethers::contract::EthCall, 270 | ::ethers::contract::EthDisplay, 271 | Default, 272 | Debug, 273 | PartialEq, 274 | Eq, 275 | Hash 276 | )] 277 | #[ethcall(name = "totalSupply", abi = "totalSupply()")] 278 | pub struct TotalSupplyCall; 279 | ///Container type for all input parameters for the `transfer` function with signature `transfer(address,uint256)` and selector `0xa9059cbb` 280 | #[derive( 281 | Clone, 282 | ::ethers::contract::EthCall, 283 | ::ethers::contract::EthDisplay, 284 | Default, 285 | Debug, 286 | PartialEq, 287 | Eq, 288 | Hash 289 | )] 290 | #[ethcall(name = "transfer", abi = "transfer(address,uint256)")] 291 | pub struct TransferCall { 292 | pub recipient: ::ethers::core::types::Address, 293 | pub amount: ::ethers::core::types::U256, 294 | } 295 | ///Container type for all input parameters for the `transferFrom` function with signature `transferFrom(address,address,uint256)` and selector `0x23b872dd` 296 | #[derive( 297 | Clone, 298 | ::ethers::contract::EthCall, 299 | ::ethers::contract::EthDisplay, 300 | Default, 301 | Debug, 302 | PartialEq, 303 | Eq, 304 | Hash 305 | )] 306 | #[ethcall(name = "transferFrom", abi = "transferFrom(address,address,uint256)")] 307 | pub struct TransferFromCall { 308 | pub sender: ::ethers::core::types::Address, 309 | pub recipient: ::ethers::core::types::Address, 310 | pub amount: ::ethers::core::types::U256, 311 | } 312 | ///Container type for all of the contract's call 313 | #[derive(Clone, ::ethers::contract::EthAbiType, Debug, PartialEq, Eq, Hash)] 314 | pub enum IERC20Calls { 315 | Allowance(AllowanceCall), 316 | Approve(ApproveCall), 317 | BalanceOf(BalanceOfCall), 318 | TotalSupply(TotalSupplyCall), 319 | Transfer(TransferCall), 320 | TransferFrom(TransferFromCall), 321 | } 322 | impl ::ethers::core::abi::AbiDecode for IERC20Calls { 323 | fn decode( 324 | data: impl AsRef<[u8]>, 325 | ) -> ::core::result::Result { 326 | let data = data.as_ref(); 327 | if let Ok(decoded) 328 | = ::decode(data) { 329 | return Ok(Self::Allowance(decoded)); 330 | } 331 | if let Ok(decoded) 332 | = ::decode(data) { 333 | return Ok(Self::Approve(decoded)); 334 | } 335 | if let Ok(decoded) 336 | = ::decode(data) { 337 | return Ok(Self::BalanceOf(decoded)); 338 | } 339 | if let Ok(decoded) 340 | = ::decode(data) { 341 | return Ok(Self::TotalSupply(decoded)); 342 | } 343 | if let Ok(decoded) 344 | = ::decode(data) { 345 | return Ok(Self::Transfer(decoded)); 346 | } 347 | if let Ok(decoded) 348 | = ::decode(data) { 349 | return Ok(Self::TransferFrom(decoded)); 350 | } 351 | Err(::ethers::core::abi::Error::InvalidData.into()) 352 | } 353 | } 354 | impl ::ethers::core::abi::AbiEncode for IERC20Calls { 355 | fn encode(self) -> Vec { 356 | match self { 357 | Self::Allowance(element) => { 358 | ::ethers::core::abi::AbiEncode::encode(element) 359 | } 360 | Self::Approve(element) => ::ethers::core::abi::AbiEncode::encode(element), 361 | Self::BalanceOf(element) => { 362 | ::ethers::core::abi::AbiEncode::encode(element) 363 | } 364 | Self::TotalSupply(element) => { 365 | ::ethers::core::abi::AbiEncode::encode(element) 366 | } 367 | Self::Transfer(element) => { 368 | ::ethers::core::abi::AbiEncode::encode(element) 369 | } 370 | Self::TransferFrom(element) => { 371 | ::ethers::core::abi::AbiEncode::encode(element) 372 | } 373 | } 374 | } 375 | } 376 | impl ::core::fmt::Display for IERC20Calls { 377 | fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { 378 | match self { 379 | Self::Allowance(element) => ::core::fmt::Display::fmt(element, f), 380 | Self::Approve(element) => ::core::fmt::Display::fmt(element, f), 381 | Self::BalanceOf(element) => ::core::fmt::Display::fmt(element, f), 382 | Self::TotalSupply(element) => ::core::fmt::Display::fmt(element, f), 383 | Self::Transfer(element) => ::core::fmt::Display::fmt(element, f), 384 | Self::TransferFrom(element) => ::core::fmt::Display::fmt(element, f), 385 | } 386 | } 387 | } 388 | impl ::core::convert::From for IERC20Calls { 389 | fn from(value: AllowanceCall) -> Self { 390 | Self::Allowance(value) 391 | } 392 | } 393 | impl ::core::convert::From for IERC20Calls { 394 | fn from(value: ApproveCall) -> Self { 395 | Self::Approve(value) 396 | } 397 | } 398 | impl ::core::convert::From for IERC20Calls { 399 | fn from(value: BalanceOfCall) -> Self { 400 | Self::BalanceOf(value) 401 | } 402 | } 403 | impl ::core::convert::From for IERC20Calls { 404 | fn from(value: TotalSupplyCall) -> Self { 405 | Self::TotalSupply(value) 406 | } 407 | } 408 | impl ::core::convert::From for IERC20Calls { 409 | fn from(value: TransferCall) -> Self { 410 | Self::Transfer(value) 411 | } 412 | } 413 | impl ::core::convert::From for IERC20Calls { 414 | fn from(value: TransferFromCall) -> Self { 415 | Self::TransferFrom(value) 416 | } 417 | } 418 | ///Container type for all return fields from the `allowance` function with signature `allowance(address,address)` and selector `0xdd62ed3e` 419 | #[derive( 420 | Clone, 421 | ::ethers::contract::EthAbiType, 422 | ::ethers::contract::EthAbiCodec, 423 | Default, 424 | Debug, 425 | PartialEq, 426 | Eq, 427 | Hash 428 | )] 429 | pub struct AllowanceReturn(pub ::ethers::core::types::U256); 430 | ///Container type for all return fields from the `approve` function with signature `approve(address,uint256)` and selector `0x095ea7b3` 431 | #[derive( 432 | Clone, 433 | ::ethers::contract::EthAbiType, 434 | ::ethers::contract::EthAbiCodec, 435 | Default, 436 | Debug, 437 | PartialEq, 438 | Eq, 439 | Hash 440 | )] 441 | pub struct ApproveReturn(pub bool); 442 | ///Container type for all return fields from the `balanceOf` function with signature `balanceOf(address)` and selector `0x70a08231` 443 | #[derive( 444 | Clone, 445 | ::ethers::contract::EthAbiType, 446 | ::ethers::contract::EthAbiCodec, 447 | Default, 448 | Debug, 449 | PartialEq, 450 | Eq, 451 | Hash 452 | )] 453 | pub struct BalanceOfReturn(pub ::ethers::core::types::U256); 454 | ///Container type for all return fields from the `totalSupply` function with signature `totalSupply()` and selector `0x18160ddd` 455 | #[derive( 456 | Clone, 457 | ::ethers::contract::EthAbiType, 458 | ::ethers::contract::EthAbiCodec, 459 | Default, 460 | Debug, 461 | PartialEq, 462 | Eq, 463 | Hash 464 | )] 465 | pub struct TotalSupplyReturn(pub ::ethers::core::types::U256); 466 | ///Container type for all return fields from the `transfer` function with signature `transfer(address,uint256)` and selector `0xa9059cbb` 467 | #[derive( 468 | Clone, 469 | ::ethers::contract::EthAbiType, 470 | ::ethers::contract::EthAbiCodec, 471 | Default, 472 | Debug, 473 | PartialEq, 474 | Eq, 475 | Hash 476 | )] 477 | pub struct TransferReturn(pub bool); 478 | ///Container type for all return fields from the `transferFrom` function with signature `transferFrom(address,address,uint256)` and selector `0x23b872dd` 479 | #[derive( 480 | Clone, 481 | ::ethers::contract::EthAbiType, 482 | ::ethers::contract::EthAbiCodec, 483 | Default, 484 | Debug, 485 | PartialEq, 486 | Eq, 487 | Hash 488 | )] 489 | pub struct TransferFromReturn(pub bool); 490 | } 491 | -------------------------------------------------------------------------------- /src/bindings/mevitize.rs: -------------------------------------------------------------------------------- 1 | pub use mevitize::*; 2 | /// This module was auto-generated with ethers-rs Abigen. 3 | /// More information at: 4 | #[allow( 5 | clippy::enum_variant_names, 6 | clippy::too_many_arguments, 7 | clippy::upper_case_acronyms, 8 | clippy::type_complexity, 9 | dead_code, 10 | non_camel_case_types, 11 | )] 12 | pub mod mevitize { 13 | #[rustfmt::skip] 14 | const __ABI: &str = "[{\"inputs\":[],\"type\":\"error\",\"name\":\"ExactBaseFee\",\"outputs\":[]}]"; 15 | ///The parsed JSON ABI of the contract. 16 | pub static MEVITIZE_ABI: ::ethers::contract::Lazy<::ethers::core::abi::Abi> = ::ethers::contract::Lazy::new(|| 17 | ::ethers::core::utils::__serde_json::from_str(__ABI).expect("ABI is always valid")); 18 | #[rustfmt::skip] 19 | const __BYTECODE: &[u8] = &[ 20 | 96, 21 | 128, 22 | 128, 23 | 96, 24 | 64, 25 | 82, 26 | 52, 27 | 96, 28 | 19, 29 | 87, 30 | 96, 31 | 17, 32 | 144, 33 | 129, 34 | 96, 35 | 25, 36 | 130, 37 | 57, 38 | 243, 39 | 91, 40 | 96, 41 | 0, 42 | 128, 43 | 253, 44 | 254, 45 | 96, 46 | 0, 47 | 128, 48 | 253, 49 | 254, 50 | 161, 51 | 100, 52 | 115, 53 | 111, 54 | 108, 55 | 99, 56 | 67, 57 | 0, 58 | 8, 59 | 17, 60 | 0, 61 | 10, 62 | ]; 63 | ///The bytecode of the contract. 64 | pub static MEVITIZE_BYTECODE: ::ethers::core::types::Bytes = ::ethers::core::types::Bytes::from_static( 65 | __BYTECODE, 66 | ); 67 | #[rustfmt::skip] 68 | const __DEPLOYED_BYTECODE: &[u8] = &[ 69 | 96, 70 | 0, 71 | 128, 72 | 253, 73 | 254, 74 | 161, 75 | 100, 76 | 115, 77 | 111, 78 | 108, 79 | 99, 80 | 67, 81 | 0, 82 | 8, 83 | 17, 84 | 0, 85 | 10, 86 | ]; 87 | ///The deployed bytecode of the contract. 88 | pub static MEVITIZE_DEPLOYED_BYTECODE: ::ethers::core::types::Bytes = ::ethers::core::types::Bytes::from_static( 89 | __DEPLOYED_BYTECODE, 90 | ); 91 | pub struct Mevitize(::ethers::contract::Contract); 92 | impl ::core::clone::Clone for Mevitize { 93 | fn clone(&self) -> Self { 94 | Self(::core::clone::Clone::clone(&self.0)) 95 | } 96 | } 97 | impl ::core::ops::Deref for Mevitize { 98 | type Target = ::ethers::contract::Contract; 99 | fn deref(&self) -> &Self::Target { 100 | &self.0 101 | } 102 | } 103 | impl ::core::ops::DerefMut for Mevitize { 104 | fn deref_mut(&mut self) -> &mut Self::Target { 105 | &mut self.0 106 | } 107 | } 108 | impl ::core::fmt::Debug for Mevitize { 109 | fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { 110 | f.debug_tuple(stringify!(Mevitize)).field(&self.address()).finish() 111 | } 112 | } 113 | impl Mevitize { 114 | /// Creates a new contract instance with the specified `ethers` client at 115 | /// `address`. The contract derefs to a `ethers::Contract` object. 116 | pub fn new>( 117 | address: T, 118 | client: ::std::sync::Arc, 119 | ) -> Self { 120 | Self( 121 | ::ethers::contract::Contract::new( 122 | address.into(), 123 | MEVITIZE_ABI.clone(), 124 | client, 125 | ), 126 | ) 127 | } 128 | /// Constructs the general purpose `Deployer` instance based on the provided constructor arguments and sends it. 129 | /// Returns a new instance of a deployer that returns an instance of this contract after sending the transaction 130 | /// 131 | /// Notes: 132 | /// - If there are no constructor arguments, you should pass `()` as the argument. 133 | /// - The default poll duration is 7 seconds. 134 | /// - The default number of confirmations is 1 block. 135 | /// 136 | /// 137 | /// # Example 138 | /// 139 | /// Generate contract bindings with `abigen!` and deploy a new contract instance. 140 | /// 141 | /// *Note*: this requires a `bytecode` and `abi` object in the `greeter.json` artifact. 142 | /// 143 | /// ```ignore 144 | /// # async fn deploy(client: ::std::sync::Arc) { 145 | /// abigen!(Greeter, "../greeter.json"); 146 | /// 147 | /// let greeter_contract = Greeter::deploy(client, "Hello world!".to_string()).unwrap().send().await.unwrap(); 148 | /// let msg = greeter_contract.greet().call().await.unwrap(); 149 | /// # } 150 | /// ``` 151 | pub fn deploy( 152 | client: ::std::sync::Arc, 153 | constructor_args: T, 154 | ) -> ::core::result::Result< 155 | ::ethers::contract::builders::ContractDeployer, 156 | ::ethers::contract::ContractError, 157 | > { 158 | let factory = ::ethers::contract::ContractFactory::new( 159 | MEVITIZE_ABI.clone(), 160 | MEVITIZE_BYTECODE.clone().into(), 161 | client, 162 | ); 163 | let deployer = factory.deploy(constructor_args)?; 164 | let deployer = ::ethers::contract::ContractDeployer::new(deployer); 165 | Ok(deployer) 166 | } 167 | } 168 | impl From<::ethers::contract::Contract> 169 | for Mevitize { 170 | fn from(contract: ::ethers::contract::Contract) -> Self { 171 | Self::new(contract.address(), contract.client()) 172 | } 173 | } 174 | ///Custom Error type `ExactBaseFee` with signature `ExactBaseFee()` and selector `0x2daf442d` 175 | #[derive( 176 | Clone, 177 | ::ethers::contract::EthError, 178 | ::ethers::contract::EthDisplay, 179 | Default, 180 | Debug, 181 | PartialEq, 182 | Eq, 183 | Hash 184 | )] 185 | #[etherror(name = "ExactBaseFee", abi = "ExactBaseFee()")] 186 | pub struct ExactBaseFee; 187 | } 188 | -------------------------------------------------------------------------------- /src/bindings/mod.rs: -------------------------------------------------------------------------------- 1 | #![allow(clippy::all)] 2 | //! This module contains abigen! generated bindings for solidity contracts. 3 | //! This is autogenerated code. 4 | //! Do not manually edit these files. 5 | //! These files may be overwritten by the codegen system at any time. 6 | pub mod deploy_drain; 7 | pub mod deploy_factory_v0; 8 | pub mod deploy_factory_v1; 9 | pub mod deploy_impl_v0; 10 | pub mod deploy_impl_v1; 11 | pub mod deploy_proxy_v0; 12 | pub mod deploy_proxy_v1; 13 | pub mod drain; 14 | pub mod i_mev_weth; 15 | pub mod ierc20; 16 | pub mod mev_wallet_v0; 17 | pub mod mev_wallet_v0_factory; 18 | pub mod mev_wallet_v1; 19 | pub mod mev_wallet_v1_factory; 20 | pub mod mevitize; 21 | pub mod mwb; 22 | -------------------------------------------------------------------------------- /src/deploy.rs: -------------------------------------------------------------------------------- 1 | use ethers::{prelude::builders::ContractCall, providers::Middleware, types::Address}; 2 | use std::sync::Arc; 3 | 4 | use crate::{bindings::mev_wallet_v0_factory::MevWalletV0Factory, MEV_WALLET_PROXY_FACTORY_ADDR}; 5 | 6 | /// Create a new proxy with a specific salt 7 | pub fn deploy_proxy(client: Arc, salt: impl Into<[u8; 32]>) -> ContractCall 8 | where 9 | M: Middleware, 10 | { 11 | let factory = MevWalletV0Factory::new(MEV_WALLET_PROXY_FACTORY_ADDR, client); 12 | 13 | factory.create_wallet(salt.into()) 14 | } 15 | 16 | /// Create a new proxy with a specific salt and owner 17 | pub fn deploy_proxy_with_owner( 18 | client: Arc, 19 | salt: impl Into<[u8; 32]>, 20 | owner: impl Into
, 21 | ) -> ContractCall 22 | where 23 | M: Middleware, 24 | { 25 | let factory = MevWalletV0Factory::new(MEV_WALLET_PROXY_FACTORY_ADDR, client); 26 | 27 | factory.create_wallet_with_owner(salt.into(), owner.into()) 28 | } 29 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #![warn(missing_docs, unreachable_pub)] 2 | #![deny(unused_must_use)] 3 | //! SDK for working with MEV-driven Meta-txns 4 | 5 | /// Contract Bindings 6 | #[allow(missing_docs)] 7 | pub mod bindings; 8 | pub use bindings::mev_wallet_v1::MevWalletV1; 9 | 10 | /// Mev Transaction creation and broadcast utils 11 | pub mod tx; 12 | pub use crate::tx::{MevTx, MevTxBuilder, SignedMevTx}; 13 | 14 | /// crate internal macros 15 | mod macros; 16 | 17 | /// Tooling for deploying new MevWallet proxies 18 | pub mod deploy; 19 | 20 | use ethers::types::H160; 21 | 22 | /// Mev transaction tx typehash: 23 | /// `fd4c9c9ceea85482c3671626f7b1c65bc2230325b86dbcb0f971327c3062c26a` 24 | /// 25 | /// `keccak256( 26 | /// "MevTx(address to,bytes data,int256 value,bool delegate,int256 tip,uint256 maxBaseFee,uint256 timing,uint256 nonce)" 27 | /// )` 28 | pub const TX_TYPEHASH: [u8; 32] = [ 29 | 253, 76, 156, 156, 238, 168, 84, 130, 195, 103, 22, 38, 247, 177, 198, 91, 194, 35, 3, 37, 184, 30 | 109, 188, 176, 249, 113, 50, 124, 48, 98, 194, 106, 31 | ]; 32 | 33 | /// MevWeth address: `0x00000000008C43efC014746c230049e330039Cb3` 34 | pub const MEV_WETH_ADDR: H160 = H160([ 35 | 0, 0, 0, 0, 0, 140, 67, 239, 192, 20, 116, 108, 35, 0, 73, 227, 48, 3, 156, 179, 36 | ]); 37 | 38 | /// MevWalletProxyFactory address: `0x9248B5e672e1880af34068C0FaE18D30c26D05Fb` 39 | pub const MEV_WALLET_PROXY_FACTORY_ADDR: H160 = H160([ 40 | 146, 72, 181, 230, 114, 225, 136, 10, 243, 64, 104, 192, 250, 225, 141, 48, 194, 109, 5, 251, 41 | ]); 42 | 43 | #[cfg(test)] 44 | mod test { 45 | use super::*; 46 | use ethers::utils::hex; 47 | 48 | #[test] 49 | fn test_const_wallets() { 50 | assert_eq!( 51 | MEV_WALLET_PROXY_FACTORY_ADDR, 52 | "0x9248B5e672e1880af34068C0FaE18D30c26D05Fb" 53 | .parse() 54 | .unwrap() 55 | ); 56 | assert_eq!( 57 | MEV_WETH_ADDR, 58 | "0x00000000008C43efC014746c230049e330039Cb3" 59 | .parse() 60 | .unwrap() 61 | ); 62 | } 63 | 64 | #[test] 65 | fn it_calculates_the_typehash() { 66 | assert_eq!( 67 | TX_TYPEHASH, 68 | ethers::utils::keccak256( 69 | "MevTx(address to,bytes data,int256 value,bool delegate,int256 tip,uint256 maxBaseFee,uint256 timing,uint256 nonce)".as_bytes(), 70 | ) 71 | ); 72 | assert_eq!( 73 | TX_TYPEHASH.to_vec(), 74 | hex::decode("fd4c9c9ceea85482c3671626f7b1c65bc2230325b86dbcb0f971327c3062c26a") 75 | .unwrap() 76 | ); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/macros.rs: -------------------------------------------------------------------------------- 1 | /// Delegates the common builder calls to an internal builder named `builder` 2 | #[macro_export] 3 | macro_rules! delegate_builder { 4 | ( 5 | $name:ident $(< 6 | // match one or more lifetimes separated by a comma 7 | $( $generic:tt ),+ 8 | >)? 9 | ) => { 10 | impl $(< $($generic),+ >)? $name $(< $($generic),+ >)? { 11 | 12 | /// Return the list of mandatory keys that are not yet set. 13 | pub fn missing_keys(&self) -> Vec<&'static str> { 14 | self.builder.missing_keys() 15 | } 16 | 17 | 18 | /// Set the chain_id explicitly. If this is not called, it will be signed 19 | /// with the signer's current chain ID. 20 | pub fn chain_id(mut self, chain_id: u64) -> Self { 21 | self.builder = self.builder.chain_id(chain_id); 22 | self 23 | } 24 | 25 | /// Set `to` 26 | pub fn to(mut self, to: ethers::types::Address) -> Self { 27 | self.builder = self.builder.to(to); 28 | self 29 | } 30 | 31 | /// Set `data` 32 | pub fn data(mut self, data: impl Into<::ethers::types::Bytes>) -> Self { 33 | self.builder = self.builder.data(data); 34 | self 35 | } 36 | 37 | /// Set `value` 38 | pub fn value(mut self, value: impl Into<::ethers::types::I256>) -> Self { 39 | self.builder = self.builder.value(value); 40 | self 41 | } 42 | 43 | /// Set `delegate` 44 | pub fn delegate(mut self, delegate: bool) -> Self { 45 | self.builder = self.builder.delegate(delegate); 46 | self 47 | } 48 | 49 | /// Set `tip` 50 | pub fn tip(mut self, tip: impl Into<::ethers::types::I256>) -> Self { 51 | self.builder = self.builder.tip(tip); 52 | self 53 | } 54 | 55 | /// Set `max_base_fee` 56 | pub fn max_base_fee(mut self, max_base_fee: impl Into) -> Self { 57 | self.builder = self.builder.max_base_fee(max_base_fee); 58 | self 59 | } 60 | 61 | /// Set `not_before` 62 | pub fn not_before(mut self, not_before: impl Into) -> Self { 63 | self.builder = self.builder.not_before(not_before); 64 | self 65 | } 66 | 67 | /// Set `deadline` 68 | pub fn deadline(mut self, deadline: impl Into) -> Self { 69 | self.builder = self.builder.deadline(deadline); 70 | self 71 | } 72 | 73 | /// Set `nonce` 74 | pub fn nonce(mut self, nonce: impl Into) -> Self { 75 | self.builder = self.builder.nonce(nonce); 76 | self 77 | } 78 | } 79 | 80 | } 81 | } 82 | 83 | /// Implements async populate calls on builders that accept a builder with a 84 | /// wallet 85 | #[macro_export] 86 | macro_rules! delegate_builder_populate { 87 | ( 88 | $name:ident $(< 89 | // match one or more lifetimes separated by a comma 90 | $( $lt:lifetime, )* 91 | MevWalletV1, 92 | $( $generic:tt ),* 93 | >)? 94 | ) => { 95 | impl $(< $($lt,)* M, $($generic),+ >)? $name $(< $($lt,)* $crate::MevWalletV1, $($generic),+ >)? 96 | where 97 | M: Middleware + 'static 98 | { 99 | /// Populate the nonce by querying the contract. Note that multiple calls 100 | /// will return the same nonce until the chain confirms a transaction. If 101 | /// building multiple offline txns, consider [`populate_nonce_with_offset`] 102 | pub async fn populate_nonce(mut self) -> Result<$name $(< $($lt,)* $crate::MevWalletV1, $($generic),+ >)?, BuilderError> 103 | 104 | // pub async fn populate_nonce(mut self) -> Result 105 | where 106 | M: Middleware + 'static, 107 | { 108 | self.builder = self.builder.populate_nonce().await?; 109 | Ok(self) 110 | } 111 | 112 | /// Populate the nonce by querying the contract, then add `offset` to the 113 | /// result. Useful for making sequences of transactions. 114 | pub async fn populate_nonce_with_offset(mut self, offset: u64) -> Result<$name $(< $($lt,)* $crate::MevWalletV1, $($generic),+ >)?, BuilderError> 115 | where 116 | M: Middleware + 'static, 117 | { 118 | self.builder = self.builder.populate_nonce_with_offset(offset).await?; 119 | Ok(self) 120 | } 121 | 122 | /// Populate the max base fee using the chain's EIP1559 estimator 123 | pub async fn populate_max_base_fee(mut self) -> Result<$name $(< $($lt,)* $crate::MevWalletV1, $($generic),+ >)?, BuilderError> 124 | where 125 | M: Middleware + 'static, 126 | { 127 | self.builder = self.builder.populate_max_base_fee().await?; 128 | Ok(self) 129 | } 130 | 131 | /// Populate the transaction using the contract. Fills gas, nonce, and 132 | /// max_base_fee, if they are not already filled. 133 | pub async fn populate(mut self) -> Result<$name $(< $($lt,)* $crate::MevWalletV1, $($generic),+ >)?, BuilderError> 134 | where 135 | M: Middleware + 'static, 136 | { 137 | self.builder = self.builder.populate().await?; 138 | Ok(self) 139 | } 140 | } 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /src/tx/builder.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | 3 | use ethers::{ 4 | prelude::{Address, Bytes, EthCall, U256}, 5 | providers::Middleware, 6 | signers::Signer, 7 | types::{transaction::eip2718::TypedTransaction, Eip1559TransactionRequest, I256}, 8 | }; 9 | 10 | use crate::{delegate_builder, delegate_builder_populate, MevTx, MevWalletV1, SignedMevTx}; 11 | 12 | /// Error type for [`MevTxBuilder`] 13 | #[derive(Debug, thiserror::Error)] 14 | pub enum BuilderError { 15 | /// Missing keys 16 | #[error("Missing Keys: {0:?}")] 17 | MissingKeys(Vec<&'static str>), 18 | /// From wallet is not set. 19 | #[error("Missing Contract. Set `wallet()` on the builder")] 20 | MissingContract, 21 | /// Custom error (e.g. signer) 22 | #[error("(0)")] 23 | Custom(String), 24 | } 25 | 26 | /// The `MevTxBuilder` builds MevTxns. 27 | pub type MevTxBuilder = MevTxBuilderInternal<()>; 28 | 29 | /// The `SignedMevTxBuilder` builds MevTxns. 30 | pub type SignedMevTxBuilder<'a, S> = SignedMevTxBuilderInternal<'a, (), S>; 31 | 32 | /// A Builder for `MevTx` 33 | #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] 34 | pub struct MevTxBuilderInternal { 35 | #[serde(skip_deserializing, skip_serializing)] 36 | contract: Option, 37 | #[serde(default)] 38 | wallet: Option
, 39 | #[serde(default)] 40 | chain_id: Option, 41 | #[serde(default)] 42 | to: Option
, 43 | #[serde(default)] 44 | data: Option, 45 | #[serde(default)] 46 | value: Option, 47 | #[serde(default)] 48 | delegate: Option, 49 | #[serde(default)] 50 | tip: Option, 51 | #[serde(default)] 52 | max_base_fee: Option, 53 | #[serde(default)] 54 | deadline: Option, 55 | #[serde(default)] 56 | not_before: Option, 57 | #[serde(default)] 58 | nonce: Option, 59 | } 60 | 61 | #[allow(clippy::derivable_impls)] 62 | impl Default for MevTxBuilder { 63 | fn default() -> Self { 64 | Self { 65 | chain_id: Default::default(), 66 | contract: Default::default(), 67 | wallet: Default::default(), 68 | to: Default::default(), 69 | data: Default::default(), 70 | value: Default::default(), 71 | delegate: Default::default(), 72 | tip: Default::default(), 73 | max_base_fee: Default::default(), 74 | deadline: Default::default(), 75 | not_before: Default::default(), 76 | nonce: Default::default(), 77 | } 78 | } 79 | } 80 | 81 | impl From<&MevTxBuilderInternal> for TypedTransaction { 82 | fn from(val: &MevTxBuilderInternal) -> Self { 83 | TypedTransaction::Eip1559(Eip1559TransactionRequest { 84 | from: None, 85 | to: val.to.map(Into::into), 86 | value: val.value.map(I256::into_raw), 87 | data: val.data.clone(), 88 | nonce: None, 89 | access_list: Default::default(), 90 | max_priority_fee_per_gas: None, 91 | max_fee_per_gas: val.max_base_fee, 92 | chain_id: None, 93 | gas: None, 94 | }) 95 | } 96 | } 97 | 98 | impl MevTxBuilderInternal<()> { 99 | /// Instantiate an empty builder 100 | pub fn new() -> Self { 101 | Default::default() 102 | } 103 | } 104 | 105 | impl MevTxBuilderInternal { 106 | /// Add a wallet address 107 | pub fn wallet_address(mut self, address: Address) -> Self { 108 | self.wallet = Some(address); 109 | self 110 | } 111 | 112 | /// Instantiate a [`MevWalletV1`] with the provided `Middleware`. Must already 113 | /// have an address 114 | pub fn with_mware(self, mware: Arc) -> Result>, Self> 115 | where 116 | M: Middleware + 'static, 117 | { 118 | if let Some(address) = self.wallet { 119 | Ok(self.address_with_mware(address, mware)) 120 | } else { 121 | Err(self) 122 | } 123 | } 124 | 125 | /// Add an address and instatiate a [`MevWalletV1`] with the provided middleware 126 | pub fn address_with_mware( 127 | self, 128 | address: Address, 129 | mware: Arc, 130 | ) -> MevTxBuilderInternal> 131 | where 132 | M: Middleware + 'static, 133 | { 134 | let contract = MevWalletV1::new(address, mware); 135 | self.wallet(&contract) 136 | } 137 | 138 | /// Set the MevWallet from which this tx will be sent 139 | pub fn wallet(self, contract: &MevWalletV1) -> MevTxBuilderInternal> 140 | where 141 | M: Middleware + 'static, 142 | { 143 | MevTxBuilderInternal { 144 | chain_id: self.chain_id, 145 | contract: Some(contract.clone()), 146 | wallet: Some(contract.address()), 147 | to: self.to, 148 | data: self.data, 149 | value: self.value, 150 | delegate: self.delegate, 151 | tip: self.tip, 152 | max_base_fee: self.max_base_fee, 153 | deadline: self.deadline, 154 | not_before: self.not_before, 155 | nonce: self.nonce, 156 | } 157 | } 158 | 159 | /// Set `to` 160 | pub fn to(mut self, to: Address) -> Self { 161 | self.to = Some(to); 162 | self 163 | } 164 | 165 | /// Set `data` 166 | pub fn data(mut self, data: impl Into) -> Self { 167 | self.data = Some(data.into()); 168 | self 169 | } 170 | 171 | /// Set `data` to any contract call 172 | pub fn call(mut self, call: E) -> Self 173 | where 174 | E: EthCall, 175 | { 176 | self.data = Some(call.encode().into()); 177 | self 178 | } 179 | 180 | /// Set `value` 181 | pub fn value(mut self, value: impl Into) -> Self { 182 | self.value = Some(value.into()); 183 | self 184 | } 185 | 186 | /// Set `delegate` 187 | pub fn delegate(mut self, delegate: bool) -> Self { 188 | self.delegate = Some(delegate); 189 | self 190 | } 191 | 192 | /// Set `tip` 193 | pub fn tip(mut self, tip: impl Into) -> Self { 194 | self.tip = Some(tip.into()); 195 | self 196 | } 197 | 198 | /// Set `max_base_fee` 199 | pub fn max_base_fee(mut self, max_base_fee: impl Into) -> Self { 200 | self.max_base_fee = Some(max_base_fee.into()); 201 | self 202 | } 203 | 204 | /// Set `not_before` 205 | pub fn not_before(mut self, not_before: impl Into) -> Self { 206 | self.not_before = Some(not_before.into().low_u64().into()); 207 | self 208 | } 209 | 210 | /// Set `deadline` 211 | pub fn deadline(mut self, deadline: impl Into) -> Self { 212 | self.deadline = Some(deadline.into().low_u64().into()); 213 | self 214 | } 215 | 216 | /// Set `nonce` 217 | pub fn nonce(mut self, nonce: impl Into) -> Self { 218 | self.nonce = Some(nonce.into()); 219 | self 220 | } 221 | 222 | /// Sign this [`MevTx`] with the specified signer. Converts into a 223 | /// [`SignedMevTxBuilder`], which has an `async fn build` 224 | pub fn with_signer(self, signer: &S) -> SignedMevTxBuilderInternal { 225 | SignedMevTxBuilderInternal { 226 | builder: self, 227 | signer, 228 | } 229 | } 230 | 231 | /// Set the chain_id explicitly. If this is not called, it will be signed 232 | /// with the signer's current chain ID. 233 | pub fn chain_id(mut self, chain_id: u64) -> Self { 234 | self.chain_id = Some(chain_id); 235 | self 236 | } 237 | 238 | /// Return the list of mandatory keys that are not yet set. 239 | pub fn missing_keys(&self) -> Vec<&'static str> { 240 | let mut v = vec![]; 241 | if self.wallet.is_none() { 242 | v.push("wallet"); 243 | } 244 | if self.to.is_none() { 245 | v.push("to"); 246 | } 247 | if self.tip.is_none() { 248 | v.push("tip"); 249 | } 250 | if self.nonce.is_none() { 251 | v.push("nonce"); 252 | } 253 | v 254 | } 255 | } 256 | 257 | impl MevTxBuilderInternal> 258 | where 259 | M: Middleware + 'static, 260 | { 261 | /// Create a [`MevTxBuilder`] from an ethers `TypedTransaction`. This 262 | /// convenience method allows users to convert regular contract 263 | /// interactions into MEV txns easily. 264 | pub async fn from_typed_tx( 265 | contract: &MevWalletV1, 266 | typed_tx: ðers::types::transaction::eip2718::TypedTransaction, 267 | ) -> Result 268 | where 269 | M: Middleware, 270 | { 271 | let mut builder = MevTxBuilder::default().wallet(contract); 272 | 273 | match typed_tx.to() { 274 | Some(ethers::types::NameOrAddress::Address(addr)) => builder = builder.to(*addr), 275 | Some(ethers::types::NameOrAddress::Name(name)) => { 276 | let addr = contract 277 | .client() 278 | .resolve_name(name) 279 | .await 280 | .map_err(|e| BuilderError::Custom(format!("{e}")))?; 281 | builder = builder.to(addr) 282 | } 283 | _ => {} 284 | } 285 | if let Some(data) = typed_tx.data() { 286 | builder = builder.data(data.clone()); 287 | } 288 | if let Some(value) = typed_tx.value() { 289 | builder = builder.value(ethers::types::I256::from_raw(*value)); 290 | } 291 | if let Some(t) = typed_tx.as_eip1559_ref() { 292 | if let (Some(max_fee), Some(max_priority)) = 293 | (t.max_fee_per_gas, t.max_priority_fee_per_gas) 294 | { 295 | builder = builder.max_base_fee(max_fee - max_priority); 296 | } 297 | } 298 | Ok(builder) 299 | } 300 | 301 | /// Populate the nonce by querying the contract. Note that multiple calls 302 | /// will return the same nonce until the chain confirms a transaction. If 303 | /// building multiple offline txns, consider [`populate_nonce_with_offset`] 304 | pub async fn populate_nonce(mut self) -> Result 305 | where 306 | M: Middleware + 'static, 307 | { 308 | let contract = self 309 | .contract 310 | .as_ref() 311 | .ok_or(BuilderError::MissingContract)?; 312 | if self.nonce.is_none() { 313 | self.nonce = Some( 314 | contract 315 | .nonce() 316 | .await 317 | .map_err(|e| BuilderError::Custom(format!("{e}")))?, 318 | ); 319 | } 320 | Ok(self) 321 | } 322 | 323 | /// Populate the nonce by querying the contract, then add `offset` to the 324 | /// result. Useful for making sequences of transactions. 325 | pub async fn populate_nonce_with_offset(mut self, offset: u64) -> Result 326 | where 327 | M: Middleware + 'static, 328 | { 329 | self = self.populate_nonce().await?; 330 | self.nonce.map(|n| n + offset); 331 | Ok(self) 332 | } 333 | 334 | /// Populate the max base fee using the chain's EIP1559 estimator 335 | pub async fn populate_max_base_fee(mut self) -> Result 336 | where 337 | M: Middleware + 'static, 338 | { 339 | let provider = self 340 | .contract 341 | .as_ref() 342 | .ok_or(BuilderError::MissingContract)? 343 | .client(); 344 | 345 | if self.max_base_fee.is_none() { 346 | let (max_fee_per_gas, _) = provider 347 | .estimate_eip1559_fees(None) 348 | .await 349 | .map_err(|e| BuilderError::Custom(format!("{e}")))?; 350 | self.max_base_fee = Some(max_fee_per_gas); 351 | } 352 | 353 | Ok(self) 354 | } 355 | 356 | /// Populate the transaction using the contract. Fills nonce and 357 | /// max_base_fee, if they are not already filled. 358 | pub async fn populate(self) -> Result 359 | where 360 | M: Middleware + 'static, 361 | { 362 | self.populate_nonce().await?.populate_max_base_fee().await 363 | } 364 | 365 | /// Build 366 | pub fn build(self) -> Result { 367 | let missing = self.missing_keys(); 368 | if !missing.is_empty() { 369 | return Err(BuilderError::MissingKeys(missing)); 370 | } 371 | 372 | let timing = self.not_before.unwrap_or_default() << 64 | self.deadline.unwrap_or_default(); 373 | 374 | Ok(MevTx { 375 | chain_id: self.chain_id.unwrap_or(1), 376 | wallet: self.wallet.expect("checked by missing_keys"), 377 | to: self.to.expect("checked by missing_keys"), 378 | data: self.data.unwrap_or_default(), 379 | value: self.value.unwrap_or_default(), 380 | delegate: self.delegate.unwrap_or_default(), 381 | tip: self.tip.expect("checked by missing_keys"), 382 | max_base_fee: self.max_base_fee.unwrap_or_default(), 383 | timing, 384 | nonce: self.nonce.expect("checked by missing_keys"), 385 | }) 386 | } 387 | } 388 | 389 | /// A [`SignedMevTxBuilder`] builds [`SignedMevTx`]. It is created from a 390 | /// builder with a signer 391 | #[derive(Debug, serde::Serialize)] 392 | 393 | pub struct SignedMevTxBuilderInternal<'a, M, S> { 394 | builder: MevTxBuilderInternal, 395 | signer: &'a S, 396 | } 397 | 398 | impl<'a, M, S> Clone for SignedMevTxBuilderInternal<'a, M, S> 399 | where 400 | M: Clone, 401 | { 402 | fn clone(&self) -> Self { 403 | Self { 404 | builder: self.builder.clone(), 405 | signer: self.signer, 406 | } 407 | } 408 | } 409 | 410 | delegate_builder!(SignedMevTxBuilderInternal<'a, M, S>); 411 | delegate_builder_populate!(SignedMevTxBuilderInternal<'a, MevWalletV1, S>); 412 | 413 | impl<'a, M, S: Signer> SignedMevTxBuilderInternal<'a, M, S> 414 | where 415 | S: Signer, 416 | { 417 | /// Set the MevWallet from which this tx will be sent 418 | pub fn wallet( 419 | self, 420 | contract: &MevWalletV1, 421 | ) -> SignedMevTxBuilderInternal<'a, MevWalletV1, S> 422 | where 423 | N: Middleware + 'static, 424 | { 425 | SignedMevTxBuilderInternal { 426 | builder: self.builder.wallet(contract), 427 | signer: self.signer, 428 | } 429 | } 430 | 431 | /// Replace the signer 432 | pub fn with_signer(self, signer: &T) -> SignedMevTxBuilderInternal { 433 | self.builder.with_signer(signer) 434 | } 435 | } 436 | 437 | impl<'a, M, S> SignedMevTxBuilderInternal<'a, MevWalletV1, S> 438 | where 439 | M: Middleware + 'static, 440 | S: Signer, 441 | { 442 | /// Build and sign the transaction 443 | pub async fn build(self) -> Result { 444 | let tx = self.builder.build()?; 445 | 446 | tx.sign(self.signer) 447 | .await 448 | .map_err(|e| format!("{e}")) 449 | .map_err(BuilderError::Custom) 450 | } 451 | } 452 | -------------------------------------------------------------------------------- /src/tx/dutch.rs: -------------------------------------------------------------------------------- 1 | use ethers::{ 2 | providers::Middleware, 3 | signers::Signer, 4 | types::{I256, U256}, 5 | }; 6 | 7 | use crate::{delegate_builder, delegate_builder_populate, MevTx, MevWalletV1, SignedMevTx}; 8 | 9 | use super::{BuilderError, MevTxBuilderInternal, SignedMevTxBuilderInternal}; 10 | 11 | /// Policy for escalating 12 | pub trait EscalationPolicy: IntoIterator {} 13 | 14 | impl EscalationPolicy for T where T: IntoIterator {} 15 | 16 | /// A tx builder that produces an ascending-tip MEV gas auction 17 | pub struct DutchBuilder { 18 | /// Internal builder 19 | builder: MevTxBuilderInternal, 20 | /// Fee escalation policy 21 | policy: T, 22 | } 23 | 24 | impl DutchBuilder { 25 | /// Add a signer to the builder 26 | pub fn with_signer(self, signer: &S) -> SignedDutchBuilder { 27 | SignedDutchBuilder { 28 | builder: self.builder.with_signer(signer), 29 | policy: self.policy, 30 | } 31 | } 32 | } 33 | 34 | impl DutchBuilder, T> 35 | where 36 | T: EscalationPolicy, 37 | M: Middleware + 'static, 38 | { 39 | /// Build an array of transactions adjusting the tip according to the escalation policy 40 | pub fn build(self) -> Result, BuilderError> { 41 | let mut txns = vec![]; 42 | for (not_before, new_tip) in self.policy.into_iter() { 43 | let tx = self 44 | .builder 45 | .clone() 46 | .tip(new_tip) 47 | .not_before(not_before) 48 | .build()?; 49 | txns.push(tx); 50 | } 51 | Ok(txns) 52 | } 53 | } 54 | 55 | delegate_builder!(DutchBuilder); 56 | delegate_builder_populate!(DutchBuilder, T>); 57 | 58 | /// A Reverse-Dutch MEV auction transaction builder 59 | pub struct SignedDutchBuilder<'a, M, T, S> { 60 | /// Internal builder 61 | builder: SignedMevTxBuilderInternal<'a, M, S>, 62 | /// Fee escalation policy 63 | policy: T, 64 | } 65 | 66 | impl<'a, M, T, S> SignedDutchBuilder<'a, MevWalletV1, T, S> 67 | where 68 | T: EscalationPolicy, 69 | M: Middleware + 'static + Clone, 70 | S: Signer, 71 | { 72 | /// Build the dutch auction and sign the txns 73 | pub async fn build(self) -> Result, BuilderError> { 74 | let mut txns = vec![]; 75 | for (not_before, new_tip) in self.policy.into_iter() { 76 | let tx = self 77 | .builder 78 | .clone() 79 | .tip(new_tip) 80 | .not_before(not_before) 81 | .build() 82 | .await?; 83 | txns.push(tx); 84 | } 85 | Ok(txns) 86 | } 87 | } 88 | 89 | delegate_builder!(SignedDutchBuilder<'a, M, T, S>); 90 | delegate_builder_populate!(SignedDutchBuilder<'a, MevWalletV1, T, S>); 91 | -------------------------------------------------------------------------------- /src/tx/ext.rs: -------------------------------------------------------------------------------- 1 | use async_trait::async_trait; 2 | 3 | use ethers::{ 4 | prelude::{ContractCall, ContractError}, 5 | providers::{Middleware, PendingTransaction, ProviderError}, 6 | signers::Signer, 7 | types::{Address, U256}, 8 | }; 9 | use std::sync::Arc; 10 | 11 | use crate::{ 12 | bindings::{ 13 | ierc20::IERC20, 14 | mev_wallet_v1::{MevWalletV1Errors, ProvideValue}, 15 | }, 16 | deploy::{deploy_proxy, deploy_proxy_with_owner}, 17 | MevTx, MevWalletV1, SignedMevTx, MEV_WETH_ADDR, 18 | }; 19 | 20 | use super::{BuilderError, MevTxBuilderInternal}; 21 | 22 | impl crate::MevWalletV1 23 | where 24 | M: Middleware + 'static, 25 | { 26 | /// Deploy a new proxy wallet 27 | /// Returns a new contract once deployment has succeeded 28 | pub async fn new_proxy( 29 | client: Arc, 30 | salt: impl Into<[u8; 32]>, 31 | ) -> Result> 32 | where 33 | M: 'static, 34 | { 35 | let call = deploy_proxy(client.clone(), salt); 36 | let address = call.clone().await?; 37 | 38 | let receipt = call 39 | .send() 40 | .await? 41 | .await 42 | .map_err(M::convert_err) 43 | .map_err(ContractError::from_middleware_error)?; 44 | if receipt.is_none() { 45 | return Err( 46 | ProviderError::CustomError("Could not get deploy tx receipt".to_owned()).into(), 47 | ); 48 | } 49 | Ok(MevWalletV1::new(address, client)) 50 | } 51 | 52 | /// Deploy a new proxy wallet with a specified owner 53 | /// Returns a new contract once deployment has succeeded 54 | pub async fn new_proxy_with_owner( 55 | client: Arc, 56 | salt: impl Into<[u8; 32]>, 57 | owner: impl Into
, 58 | ) -> Result> 59 | where 60 | M: 'static, 61 | { 62 | let call = deploy_proxy_with_owner(client.clone(), salt, owner); 63 | let address = call.clone().await?; 64 | 65 | let receipt = call 66 | .send() 67 | .await? 68 | .await 69 | .map_err(M::convert_err) 70 | .map_err(ContractError::from_middleware_error)?; 71 | if receipt.is_none() { 72 | return Err( 73 | ProviderError::CustomError("Could not get deploy tx receipt".to_owned()).into(), 74 | ); 75 | } 76 | Ok(MevWalletV1::new(address, client)) 77 | } 78 | 79 | /// Create a [`MevTxBuilder`] from an ethers `TypedTransaction`. This 80 | /// convenience method allows users to convert regular contract 81 | /// interactions into MEV txns easily. 82 | pub async fn convert_tx( 83 | &self, 84 | typed_tx: ðers::types::transaction::eip2718::TypedTransaction, 85 | ) -> Result>, BuilderError> { 86 | MevTxBuilderInternal::from_typed_tx(self, typed_tx).await 87 | } 88 | 89 | /// Create a contract call object sending the signed mev transaction 90 | pub fn send(&self, tx: SignedMevTx) -> ethers::prelude::builders::ContractCall { 91 | tx.into_call(self.client()) 92 | } 93 | 94 | /// True if the input `address` is the owner of this MevWallet, false 95 | /// otherwise 96 | pub async fn is_owner(&self, address: Address) -> Result> { 97 | Ok(self.owner().await? == address) 98 | } 99 | 100 | /// Checks the `MevWeth` balance of this MevWallet 101 | pub async fn mev_weth_balance(&self) -> Result> { 102 | let mware = self.client(); 103 | IERC20::new(MEV_WETH_ADDR, mware) 104 | .balance_of(self.address()) 105 | .await 106 | } 107 | 108 | /// Return the ETH balance of this MevWallet 109 | pub async fn balance(&self) -> Result> { 110 | self.client() 111 | .get_balance(self.address(), None) 112 | .await 113 | .map_err(ContractError::from_middleware_error) 114 | } 115 | 116 | /// Simulate a tx. Returns `Ok(None)` for succesful execution. Returns Ok 117 | /// (Some(err)) for contract revert. Returns Err(e) for error during 118 | /// simulation 119 | /// 120 | pub async fn simulate( 121 | &self, 122 | call: &ContractCall, 123 | ) -> Result, ContractError> { 124 | let result = call.clone().await; 125 | 126 | // execution succeeded 127 | if result.is_ok() { 128 | return Ok(None); 129 | } 130 | let err = result.expect_err("checked by is_ok"); 131 | 132 | match err.decode_contract_revert() { 133 | Some(mev_err) => Ok(Some(mev_err)), 134 | None => Err(err), 135 | } 136 | } 137 | 138 | /// Resolve any call attribute that can be resolved, return a call 139 | pub async fn resolve_call( 140 | &self, 141 | tx: SignedMevTx, 142 | ) -> Result>, ContractError> { 143 | let mut call = tx.into_call(self.client()); 144 | 145 | loop { 146 | let res = self.simulate(&call).await?; 147 | if res.is_none() { 148 | return Ok(Some(call)); 149 | } 150 | let err = res.unwrap(); 151 | if err.terminal() { 152 | return Ok(None); 153 | } 154 | let _ = err.apply_to(&mut call); 155 | } 156 | } 157 | } 158 | 159 | impl MevWalletV1Errors { 160 | fn terminal(&self) -> bool { 161 | match self { 162 | MevWalletV1Errors::ExactBaseFee(_) => false, 163 | MevWalletV1Errors::HighBaseFee(_) => true, // todo: waiter? 164 | MevWalletV1Errors::MissingNonce(_) => true, // todo: waiter? 165 | MevWalletV1Errors::NotBefore(_) => true, // todo: waiter 166 | MevWalletV1Errors::PermanentlyInvalid(_) => true, 167 | MevWalletV1Errors::ProvideValue(_) => false, 168 | MevWalletV1Errors::Reverted(_) => true, 169 | MevWalletV1Errors::UsedNonce(_) => true, 170 | MevWalletV1Errors::WrongSigner(_) => true, 171 | MevWalletV1Errors::RevertString(_) => true, 172 | } 173 | } 174 | 175 | fn apply_to(self, call: &mut ContractCall) -> Result<(), MevWalletV1Errors> { 176 | match self { 177 | MevWalletV1Errors::ExactBaseFee(_) => { 178 | call.tx 179 | .as_eip1559_mut() 180 | .expect("no legacy txns") 181 | .max_priority_fee_per_gas = Some(U256::zero()); 182 | Ok(()) 183 | } 184 | MevWalletV1Errors::ProvideValue(ProvideValue(amnt)) => { 185 | call.tx.as_eip1559_mut().expect("no legacy txns").value = Some(amnt); 186 | Ok(()) 187 | } 188 | _ => Err(self), 189 | } 190 | } 191 | } 192 | 193 | #[async_trait] 194 | /// Extension trait for ethers middleware. Allows sending MevTxns directly 195 | pub trait MiddlewareExt: Middleware { 196 | /// Wrap a MevTx in a transaction and dispatch it 197 | async fn send_mev_tx( 198 | &self, 199 | mev_tx: SignedMevTx, 200 | ) -> Result, Self::Error> { 201 | let mut tx = mev_tx.wrap_in_tx(); 202 | 203 | // Populate max fee per gas :) 204 | let (mfpg, _) = self.estimate_eip1559_fees(None).await?; 205 | tx.as_eip1559_mut().unwrap().max_fee_per_gas = Some(mfpg); 206 | 207 | // resolve nonce & gas & such 208 | self.fill_transaction(&mut tx, None).await?; 209 | 210 | // delegate to inner provider, basically :) 211 | self.send_transaction(tx, None).await 212 | } 213 | } 214 | 215 | /// A signer extension for signing MevTxns 216 | #[async_trait] 217 | pub trait SignerExt: Signer + Sized { 218 | /// Sign a mev transaction 219 | async fn sign_mev_tx(&self, mev_tx: MevTx) -> Result { 220 | mev_tx.sign(self).await 221 | } 222 | } 223 | -------------------------------------------------------------------------------- /src/tx/mod.rs: -------------------------------------------------------------------------------- 1 | /// MevTx Builders 2 | pub mod builder; 3 | 4 | /// MevTx structs 5 | pub mod types; 6 | 7 | /// Extensions to ethers types to support MevWallet usage 8 | pub mod ext; 9 | 10 | /// Ascending-price timed auction 11 | pub mod dutch; 12 | 13 | pub use builder::*; 14 | pub use ext::*; 15 | pub use types::*; 16 | -------------------------------------------------------------------------------- /src/tx/types.rs: -------------------------------------------------------------------------------- 1 | use std::{convert::Infallible, sync::Arc}; 2 | 3 | use ethers::{ 4 | abi::{AbiEncode, Tokenize}, 5 | prelude::{builders::ContractCall, Address, Bytes, Signature, U256}, 6 | providers::Middleware, 7 | signers::Signer, 8 | types::{ 9 | transaction::{ 10 | eip2718::TypedTransaction, 11 | eip712::{EIP712Domain, Eip712}, 12 | }, 13 | Eip1559TransactionRequest, SignatureError, H256, I256, 14 | }, 15 | utils::keccak256, 16 | }; 17 | 18 | use crate::{bindings::mev_wallet_v0::MevTxCall, MevWalletV1}; 19 | 20 | /// A MEV-driven Meta-transaction. MEV Transactions are intended to be used 21 | /// with a [`MevWalletV0`] smart contract. They describe a transaction initiated 22 | /// by that contract, similar to a Gnosis Safe. 23 | /// 24 | /// Unlike a Safe, a `MevTx` does not implement a multisig. Instead, it allows 25 | /// a single signer to specify an explicit tip in `MevWeth`. This `tip` can be 26 | /// positive OR negative. A negative tip specifies that the user wants to 27 | /// receive some MEV from the transaction. 28 | #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] 29 | #[serde(rename_all = "camelCase")] 30 | pub struct MevTx { 31 | /// The chain id on which the wallet is deployed 32 | pub chain_id: u64, 33 | /// Address of the wallet from which this will be sent 34 | pub wallet: Address, 35 | /// Target 36 | pub to: Address, 37 | /// Data 38 | pub data: Bytes, 39 | /// Value to forward 40 | pub value: I256, 41 | /// True for delegatecall 42 | pub delegate: bool, 43 | /// MevWeth Tip amount 44 | pub tip: I256, 45 | /// MaxBaseFee (0 for none) 46 | pub max_base_fee: U256, 47 | /// notBefore and deadline 48 | pub timing: U256, 49 | /// Nonce 50 | pub nonce: U256, 51 | } 52 | 53 | /// EIP-712 calculation info for MevTx 54 | pub struct MevTx712<'a> { 55 | wallet: Address, 56 | chain_id: u64, 57 | tx: &'a MevTx, 58 | } 59 | 60 | impl<'a> MevTx712<'a> { 61 | /// Instnatiate a new Eip-712 struct 62 | pub fn new(wallet: Address, chain_id: u64, tx: &'a MevTx) -> Self { 63 | Self { 64 | wallet, 65 | chain_id, 66 | tx, 67 | } 68 | } 69 | } 70 | 71 | impl<'a> Eip712 for MevTx712<'a> { 72 | type Error = Infallible; 73 | 74 | fn domain(&self) -> Result { 75 | Ok(EIP712Domain { 76 | name: Some("MevTx".to_owned()), 77 | version: Some("1".to_owned()), 78 | chain_id: Some(self.chain_id.into()), 79 | verifying_contract: Some(self.wallet), 80 | salt: None, 81 | }) 82 | } 83 | 84 | fn type_hash() -> Result<[u8; 32], Self::Error> { 85 | Ok(crate::TX_TYPEHASH) 86 | } 87 | 88 | fn struct_hash(&self) -> Result<[u8; 32], Self::Error> { 89 | Ok(keccak256(self.tx.encode_struct())) 90 | } 91 | 92 | fn encode_eip712(&self) -> Result<[u8; 32], Self::Error> { 93 | // encode the digest to be compatible with solidity abi.encodePacked() 94 | // See: https://github.com/gakonst/ethers-rs/blob/master/examples/permit_hash.rs#L72 95 | 96 | let domain_separator = self.domain_separator()?; 97 | let struct_hash = self.struct_hash()?; 98 | 99 | let digest_input = [&[0x19, 0x01], &domain_separator[..], &struct_hash[..]].concat(); 100 | 101 | Ok(keccak256(digest_input)) 102 | } 103 | } 104 | 105 | impl Tokenize for &MevTx { 106 | fn into_tokens(self) -> Vec { 107 | ( 108 | crate::TX_TYPEHASH, 109 | self.to, 110 | H256::from(keccak256(&self.data)), 111 | self.value, 112 | self.delegate, 113 | self.tip, 114 | self.max_base_fee, 115 | self.timing, 116 | self.nonce, 117 | ) 118 | .into_tokens() 119 | } 120 | } 121 | 122 | impl MevTx { 123 | /// Encode the preimage of Eip-712's hashStruct 124 | pub fn encode_struct(&self) -> Vec { 125 | ethers::abi::encode(&self.into_tokens()) 126 | } 127 | 128 | /// Return an eip-712 struct referencing this tx, suitable for signing or 129 | /// recovering signatures 130 | pub fn eip_712(&self) -> MevTx712<'_> { 131 | MevTx712 { 132 | wallet: self.wallet, 133 | chain_id: self.chain_id, 134 | tx: self, 135 | } 136 | } 137 | 138 | /// Sign the MEV transaction with the provided signer. 139 | pub async fn sign(self, signer: &S) -> Result { 140 | let eip712 = self.eip_712(); 141 | let sig = signer.sign_typed_data(&eip712).await?; 142 | Ok(SignedMevTx { tx: self, sig }) 143 | } 144 | } 145 | 146 | /// A Signed MEV Tx 147 | #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] 148 | #[serde(rename_all = "camelCase")] 149 | pub struct SignedMevTx { 150 | #[serde(flatten)] 151 | tx: MevTx, 152 | #[serde(flatten)] 153 | sig: Signature, 154 | } 155 | 156 | impl SignedMevTx { 157 | /// Convert into a populated TypedTransaction, ready for population and 158 | /// dispatch 159 | pub fn wrap_in_tx(self) -> TypedTransaction { 160 | let mut r = [0u8; 32]; 161 | let mut s = [0u8; 32]; 162 | self.sig.r.to_big_endian(&mut r); 163 | self.sig.s.to_big_endian(&mut s); 164 | 165 | let data = MevTxCall { 166 | to: self.tx.to, 167 | data: self.tx.data, 168 | value: self.tx.value, 169 | delegate: self.tx.delegate, 170 | tip: self.tx.tip, 171 | max_base_fee: self.tx.max_base_fee, 172 | timing: self.tx.timing, 173 | n: self.tx.nonce, 174 | v: self.sig.v as u8, 175 | r, 176 | s, 177 | } 178 | .encode(); 179 | 180 | TypedTransaction::Eip1559(Eip1559TransactionRequest { 181 | from: None, 182 | to: Some(self.tx.wallet.into()), 183 | gas: None, 184 | value: None, 185 | data: Some(data.into()), 186 | nonce: None, 187 | access_list: Default::default(), 188 | max_priority_fee_per_gas: Some(U256::zero()), 189 | max_fee_per_gas: None, 190 | chain_id: Some(self.tx.chain_id.into()), 191 | }) 192 | } 193 | 194 | /// Convert the Signed MEV tx into a call to the contract wallet 195 | pub fn into_call(self, middleware: Arc) -> ContractCall { 196 | let contract = self.wallet_contract(middleware); 197 | let mut r = [0u8; 32]; 198 | let mut s = [0u8; 32]; 199 | self.sig.r.to_big_endian(&mut r); 200 | self.sig.s.to_big_endian(&mut s); 201 | 202 | let mut call = contract.mev_tx( 203 | self.tx.to, 204 | self.tx.data, 205 | self.tx.value, 206 | self.tx.delegate, 207 | self.tx.tip, 208 | self.tx.max_base_fee, 209 | self.tx.timing, 210 | self.tx.nonce, 211 | self.sig.v as u8, 212 | r, 213 | s, 214 | ); 215 | 216 | let req = call.tx.as_eip1559_mut().expect("no legacy tx"); 217 | if !self.tx.max_base_fee.is_zero() { 218 | req.max_fee_per_gas = Some(self.tx.max_base_fee); 219 | } 220 | req.max_priority_fee_per_gas = Some(U256::zero()); 221 | 222 | call 223 | } 224 | 225 | /// Get the chain ID 226 | pub fn chain_id(&self) -> u64 { 227 | self.tx.chain_id 228 | } 229 | 230 | /// Get the Wallet address 231 | pub fn wallet(&self) -> Address { 232 | self.tx.wallet 233 | } 234 | 235 | /// Return an instance of the wallet contract 236 | pub fn wallet_contract(&self, middleware: Arc) -> MevWalletV1 { 237 | MevWalletV1::new(self.tx.wallet, middleware) 238 | } 239 | 240 | /// Get the transaction details 241 | pub fn tx(&self) -> &MevTx { 242 | &self.tx 243 | } 244 | 245 | /// Get the signature 246 | pub fn sig(&self) -> Signature { 247 | self.sig 248 | } 249 | 250 | /// Get the signer 251 | pub fn signer(&self) -> Result { 252 | self.sig.recover_typed_data(self.tx.eip_712()) 253 | } 254 | } 255 | 256 | #[cfg(test)] 257 | mod test { 258 | use super::*; 259 | #[test] 260 | fn it_generates_json_output() { 261 | let tx = SignedMevTx { 262 | tx: MevTx { 263 | chain_id: 31337, 264 | wallet: Address::zero(), 265 | to: Address::zero(), 266 | data: "0x1234abcd".parse().unwrap(), 267 | value: 500.into(), 268 | delegate: true, 269 | tip: 9000.into(), 270 | max_base_fee: 83.into(), 271 | timing: 123845.into(), 272 | nonce: 16.into(), 273 | }, 274 | sig: Signature { 275 | r: U256::from(851929865), 276 | s: U256::from(1273717), 277 | v: 18, 278 | }, 279 | }; 280 | println!("{}", serde_json::to_string_pretty(&tx).unwrap()); 281 | } 282 | } 283 | -------------------------------------------------------------------------------- /test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # MevWeth Binary 4 | export MWB="6080604052600436106101fd5760003560e01c80638237e5381161010d578063cae9ca51116100a0578063d9d98ce41161006f578063d9d98ce4146108f2578063dd62ed3e1461092b578063ea3e3e3e14610966578063f801cf601461099f578063f8e5c102146109d85761023d565b8063cae9ca51146107e7578063cd0d009614610877578063d0e30db01461088c578063d505accf146108945761023d565b806395d89b41116100dc57806395d89b411461075e57806399c2463114610773578063a9059cbb14610788578063b760faf9146107c15761023d565b80638237e538146106be578063848316fb146106d35780638b28d32f146107065780639555a9421461071b5761023d565b8063313ce567116101905780635cffe9de1161015f5780635cffe9de1461050c5780635ddb7d7e146105a7578063613255ab1461062557806370a08231146106585780637ecebe001461068b5761023d565b8063313ce567146104125780633644e5151461043d5780634000aea0146104525780635992bfdd146104e25761023d565b8063205c2878116101cc578063205c28781461035557806323b872dd146103905780632e1a7d4d146103d357806330adf81f146103fd5761023d565b806306fdde0314610242578063095ea7b3146102cc57806313c9b33a1461031957806318160ddd146103405761023d565b3661023d57336000818152602081815260408083208054349081019091558151908152905160008051602061257b833981519152929181900390910190a3005b600080fd5b34801561024e57600080fd5b50610257610a02565b6040805160208082528351818301528351919283929083019185019080838360005b83811015610291578181015183820152602001610279565b50505050905090810190601f1680156102be5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b3480156102d857600080fd5b50610305600480360360408110156102ef57600080fd5b506001600160a01b038135169060200135610a26565b604080519115158252519081900360200190f35b34801561032557600080fd5b5061032e610a7a565b60408051918252519081900360200190f35b34801561034c57600080fd5b5061032e610a80565b34801561036157600080fd5b5061038e6004803603604081101561037857600080fd5b506001600160a01b038135169060200135610a88565b005b34801561039c57600080fd5b50610305600480360360608110156103b357600080fd5b506001600160a01b03813581169160208101359091169060400135610ba9565b3480156103df57600080fd5b5061038e600480360360208110156103f657600080fd5b5035610e82565b34801561040957600080fd5b5061032e610f99565b34801561041e57600080fd5b50610427610fbd565b6040805160ff9092168252519081900360200190f35b34801561044957600080fd5b5061032e610fc2565b34801561045e57600080fd5b506103056004803603606081101561047557600080fd5b6001600160a01b0382351691602081013591810190606081016040820135600160201b8111156104a457600080fd5b8201836020820111156104b657600080fd5b803590602001918460018302840111600160201b831117156104d757600080fd5b509092509050611022565b3480156104ee57600080fd5b5061038e6004803603602081101561050557600080fd5b50356112ac565b34801561051857600080fd5b506103056004803603608081101561052f57600080fd5b6001600160a01b03823581169260208101359091169160408201359190810190608081016060820135600160201b81111561056957600080fd5b82018360208201111561057b57600080fd5b803590602001918460018302840111600160201b8311171561059c57600080fd5b50909250905061133e565b610305600480360360408110156105bd57600080fd5b6001600160a01b038235169190810190604081016020820135600160201b8111156105e757600080fd5b8201836020820111156105f957600080fd5b803590602001918460018302840111600160201b8311171561061a57600080fd5b50909250905061173a565b34801561063157600080fd5b5061032e6004803603602081101561064857600080fd5b50356001600160a01b0316611836565b34801561066457600080fd5b5061032e6004803603602081101561067b57600080fd5b50356001600160a01b0316611862565b34801561069757600080fd5b5061032e600480360360208110156106ae57600080fd5b50356001600160a01b0316611874565b3480156106ca57600080fd5b5061032e611886565b3480156106df57600080fd5b5061038e600480360360208110156106f657600080fd5b50356001600160a01b03166118aa565b34801561071257600080fd5b5061032e6118f8565b34801561072757600080fd5b5061038e6004803603606081101561073e57600080fd5b506001600160a01b038135811691602081013590911690604001356118fe565b34801561076a57600080fd5b50610257611b15565b34801561077f57600080fd5b5061038e611b35565b34801561079457600080fd5b50610305600480360360408110156107ab57600080fd5b506001600160a01b038135169060200135611b79565b61038e600480360360208110156107d757600080fd5b50356001600160a01b0316611d63565b3480156107f357600080fd5b506103056004803603606081101561080a57600080fd5b6001600160a01b0382351691602081013591810190606081016040820135600160201b81111561083957600080fd5b82018360208201111561084b57600080fd5b803590602001918460018302840111600160201b8311171561086c57600080fd5b509092509050611da8565b34801561088357600080fd5b5061032e611e7d565b61038e611ea1565b3480156108a057600080fd5b5061038e600480360360e08110156108b757600080fd5b506001600160a01b03813581169160208101359091169060408101359060608101359060ff6080820135169060a08101359060c00135611edc565b3480156108fe57600080fd5b5061032e6004803603604081101561091557600080fd5b506001600160a01b038135169060200135612179565b34801561093757600080fd5b5061032e6004803603604081101561094e57600080fd5b506001600160a01b03813581169160200135166121e1565b34801561097257600080fd5b5061038e6004803603604081101561098957600080fd5b506001600160a01b0381351690602001356121fe565b3480156109ab57600080fd5b5061038e600480360360408110156109c257600080fd5b506001600160a01b038135169060200135612259565b3480156109e457600080fd5b5061038e600480360360208110156109fb57600080fd5b50356123cf565b6040518060400160405280600881526020016709acaec40aecae8d60c31b81525081565b3360008181526002602090815260408083206001600160a01b0387168085529083528184208690558151868152915193949093909260008051602061259b833981519152928290030190a350600192915050565b60045481565b600354470190565b3360009081526020819052604090205481811015610ad75760405162461bcd60e51b81526004018080602001828103825260218152602001806125bb6021913960400191505060405180910390fd5b3360008181526020818152604080832086860390558051868152905192939260008051602061257b833981519152929181900390910190a36040516000906001600160a01b0385169084908381818185875af1925050503d8060008114610b5a576040519150601f19603f3d011682016040523d82523d6000602084013e610b5f565b606091505b5050905080610ba3576040805162461bcd60e51b81526020600482015260196024820152600080516020612537833981519152604482015290519081900360640190fd5b50505050565b60006001600160a01b0384163314610c7c576001600160a01b03841660009081526002602090815260408083203384529091529020546000198114610c7a5782811015610c2b576040805162461bcd60e51b815260206004820152601f6024820152600080516020612517833981519152604482015290519081900360640190fd5b6001600160a01b03851660008181526002602090815260408083203380855290835292819020878603908190558151818152915190949260008051602061259b833981519152928290030190a3505b505b6001600160a01b03831615801590610c9d57506001600160a01b0383163014155b15610d51576001600160a01b03841660009081526020819052604090205482811015610cfa5760405162461bcd60e51b81526004018080602001828103825260258152602001806124f26025913960400191505060405180910390fd5b6001600160a01b0380861660008181526020818152604080832088870390559388168083529184902080548801905583518781529351919360008051602061257b833981519152929081900390910190a350610e78565b6001600160a01b03841660009081526020819052604090205482811015610da95760405162461bcd60e51b81526004018080602001828103825260218152602001806125bb6021913960400191505060405180910390fd5b6001600160a01b03851660008181526020818152604080832087860390558051878152905192939260008051602061257b833981519152929181900390910190a3604051600090339085908381818185875af1925050503d8060008114610e2c576040519150601f19603f3d011682016040523d82523d6000602084013e610e31565b606091505b5050905080610e75576040805162461bcd60e51b81526020600482015260196024820152600080516020612537833981519152604482015290519081900360640190fd5b50505b5060019392505050565b3360009081526020819052604090205481811015610ed15760405162461bcd60e51b81526004018080602001828103825260218152602001806125bb6021913960400191505060405180910390fd5b3360008181526020818152604080832086860390558051868152905192939260008051602061257b833981519152929181900390910190a3604051600090339084908381818185875af1925050503d8060008114610f4b576040519150601f19603f3d011682016040523d82523d6000602084013e610f50565b606091505b5050905080610f94576040805162461bcd60e51b81526020600482015260196024820152600080516020612537833981519152604482015290519081900360640190fd5b505050565b7f000000000000000000000000000000000000000000000000000000000000000081565b601281565b6000467f00000000000000000000000000000000000000000000000000000000000000008114610ffa57610ff581612420565b61101c565b7f00000000000000000000000000000000000000000000000000000000000000005b91505090565b60006001600160a01b038516156110d75733600090815260208190526040902054848110156110825760405162461bcd60e51b81526004018080602001828103825260258152602001806124f26025913960400191505060405180910390fd5b3360008181526020818152604080832089860390556001600160a01b038a168084529281902080548a0190558051898152905192939260008051602061257b833981519152929181900390910190a3506111ec565b33600090815260208190526040902054848110156111265760405162461bcd60e51b81526004018080602001828103825260218152602001806125bb6021913960400191505060405180910390fd5b3360008181526020818152604080832089860390558051898152905192939260008051602061257b833981519152929181900390910190a3604051600090339087908381818185875af1925050503d80600081146111a0576040519150601f19603f3d011682016040523d82523d6000602084013e6111a5565b606091505b50509050806111e9576040805162461bcd60e51b81526020600482015260196024820152600080516020612537833981519152604482015290519081900360640190fd5b50505b846001600160a01b031663a4c0ed36338686866040518563ffffffff1660e01b815260040180856001600160a01b03168152602001848152602001806020018281038252848482818152602001925080828437600081840152601f19601f82011690508083019250505095505050505050602060405180830381600087803b15801561127757600080fd5b505af115801561128b573d6000803e3d6000fd5b505050506040513d60208110156112a157600080fd5b505195945050505050565b33600090815260208190526040902054818110156112fb5760405162461bcd60e51b81526004018080602001828103825260218152602001806125bb6021913960400191505060405180910390fd5b336000818152602081815260409182902085850390558151858152915130939260008051602061257b83398151915292908290030190a350600480549091019055565b60006001600160a01b038516301461139d576040805162461bcd60e51b815260206004820152601c60248201527f574554483a20666c617368206d696e74206f6e6c792057455448313000000000604482015290519081900360640190fd5b6001600160701b038411156113e35760405162461bcd60e51b81526004018080602001828103825260248152602001806125576024913960400191505060405180910390fd5b600380548501908190556001600160701b031015611448576040805162461bcd60e51b815260206004820152601f60248201527f574554483a20746f74616c206c6f616e206c696d697420657863656564656400604482015290519081900360640190fd5b6001600160a01b0386166000818152602081815260408083208054890190558051888152905160008051602061257b833981519152929181900390910190a37f0000000000000000000000000000000000000000000000000000000000000000866001600160a01b03166323e30c8b333088600089896040518763ffffffff1660e01b815260040180876001600160a01b03168152602001866001600160a01b03168152602001858152602001848152602001806020018281038252848482818152602001925080828437600081840152601f19601f820116905080830192505050975050505050505050602060405180830381600087803b15801561154d57600080fd5b505af1158015611561573d6000803e3d6000fd5b505050506040513d602081101561157757600080fd5b5051146115cb576040805162461bcd60e51b815260206004820152601760248201527f574554483a20666c617368206c6f616e206661696c6564000000000000000000604482015290519081900360640190fd5b6001600160a01b0386166000908152600260209081526040808320308452909152902054600019811461168a578481101561163b576040805162461bcd60e51b815260206004820152601f6024820152600080516020612517833981519152604482015290519081900360640190fd5b6001600160a01b03871660008181526002602090815260408083203080855290835292819020898603908190558151818152915190949260008051602061259b833981519152928290030190a3505b6001600160a01b038716600090815260208190526040902054858110156116e25760405162461bcd60e51b81526004018080602001828103825260218152602001806125bb6021913960400191505060405180910390fd5b6001600160a01b0388166000818152602081815260408083208a8603905580518a8152905192939260008051602061257b833981519152929181900390910190a3505060038054859003905550600195945050505050565b6001600160a01b03831660008181526020818152604080832080543490810190915581519081529051929392849260008051602061257b833981519152928290030190a3604051635260769b60e11b815233600482018181523460248401819052606060448501908152606485018790526001600160a01b0389169463a4c0ed36949389928992608401848480828437600081840152601f19601f82011690508083019250505095505050505050602060405180830381600087803b15801561180257600080fd5b505af1158015611816573d6000803e3d6000fd5b505050506040513d602081101561182c57600080fd5b5051949350505050565b60006001600160a01b038216301461184f57600061185c565b6003546001600160701b03035b92915050565b60006020819052908152604090205481565b60016020526000908152604090205481565b7f000000000000000000000000000000000000000000000000000000000000000081565b600480546001600160a01b038316600081815260208181526040808320805486019055919094558051838152905192939192309260008051602061257b833981519152928290030190a35050565b60035481565b6001600160a01b03831633146119cf576001600160a01b038316600090815260026020908152604080832033845290915290205460001981146119cd578181101561197e576040805162461bcd60e51b815260206004820152601f6024820152600080516020612517833981519152604482015290519081900360640190fd5b6001600160a01b03841660008181526002602090815260408083203380855290835292819020868603908190558151818152915190949260008051602061259b833981519152928290030190a3505b505b6001600160a01b03831660009081526020819052604090205481811015611a275760405162461bcd60e51b81526004018080602001828103825260218152602001806125bb6021913960400191505060405180910390fd5b6001600160a01b03841660008181526020818152604080832086860390558051868152905192939260008051602061257b833981519152929181900390910190a36040516000906001600160a01b0385169084908381818185875af1925050503d8060008114611ab3576040519150601f19603f3d011682016040523d82523d6000602084013e611ab8565b606091505b5050905080611b0e576040805162461bcd60e51b815260206004820152601b60248201527f574554483a204574686572207472616e73666572206661696c65640000000000604482015290519081900360640190fd5b5050505050565b6040518060400160405280600481526020016309a8aa8960e31b81525081565b6004805433600081815260208181526040808320805486019055919094558051838152905192939192309260008051602061257b833981519152928290030190a350565b60006001600160a01b03831615801590611b9c57506001600160a01b0383163014155b15611c45573360009081526020819052604090205482811015611bf05760405162461bcd60e51b81526004018080602001828103825260258152602001806124f26025913960400191505060405180910390fd5b3360008181526020818152604080832087860390556001600160a01b038816808452928190208054880190558051878152905192939260008051602061257b833981519152929181900390910190a350611d5a565b3360009081526020819052604090205482811015611c945760405162461bcd60e51b81526004018080602001828103825260218152602001806125bb6021913960400191505060405180910390fd5b3360008181526020818152604080832087860390558051878152905192939260008051602061257b833981519152929181900390910190a3604051600090339085908381818185875af1925050503d8060008114611d0e576040519150601f19603f3d011682016040523d82523d6000602084013e611d13565b606091505b5050905080611d57576040805162461bcd60e51b81526020600482015260196024820152600080516020612537833981519152604482015290519081900360640190fd5b50505b50600192915050565b6001600160a01b0381166000818152602081815260408083208054349081019091558151908152905160008051602061257b833981519152929181900390910190a350565b3360008181526002602090815260408083206001600160a01b0389168085529083528184208890558151888152915193949093909260008051602061259b833981519152928290030190a3846001600160a01b031662ba451f338686866040518563ffffffff1660e01b815260040180856001600160a01b03168152602001848152602001806020018281038252848482818152602001925080828437600081840152601f19601f82011690508083019250505095505050505050602060405180830381600087803b15801561127757600080fd5b7f000000000000000000000000000000000000000000000000000000000000000081565b336000818152602081815260408083208054349081019091558151908152905160008051602061257b833981519152929181900390910190a3565b83421115611f28576040805162461bcd60e51b815260206004820152601460248201527315d155120e88115e1c1a5c9959081c195c9b5a5d60621b604482015290519081900360640190fd5b6001600160a01b0380881660008181526001602081815260408084208054938401905580517f00000000000000000000000000000000000000000000000000000000000000008184015280820195909552948b166060850152608084018a905260a084019190915260c08084018990528451808503909101815260e09093019093528151919092012046917f00000000000000000000000000000000000000000000000000000000000000008314611fe857611fe383612420565b61200a565b7f00000000000000000000000000000000000000000000000000000000000000005b82604051602001808061190160f01b81525060020183815260200182815260200192505050604051602081830303815290604052805190602001209050600060018288888860405160008152602001604052604051808581526020018460ff1681526020018381526020018281526020019450505050506020604051602081039080840390855afa1580156120a3573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b038116158015906120d957508a6001600160a01b0316816001600160a01b0316145b612121576040805162461bcd60e51b815260206004820152601460248201527315d155120e881a5b9d985b1a59081c195c9b5a5d60621b604482015290519081900360640190fd5b6001600160a01b03808c166000818152600260209081526040808320948f16808452948252918290208d905581518d8152915160008051602061259b8339815191529281900390910190a35050505050505050505050565b60006001600160a01b03831630146121d8576040805162461bcd60e51b815260206004820152601c60248201527f574554483a20666c617368206d696e74206f6e6c792057455448313000000000604482015290519081900360640190fd5b50600092915050565b600260209081526000928352604080842090915290825290205481565b6004548181101561220e57600080fd5b6001600160a01b0383166000818152602081815260409182902080548601905584840360045581518581529151309260008051602061257b83398151915292908290030190a3505050565b6001600160a01b038216331461232a576001600160a01b0382166000908152600260209081526040808320338452909152902054600019811461232857818110156122d9576040805162461bcd60e51b815260206004820152601f6024820152600080516020612517833981519152604482015290519081900360640190fd5b6001600160a01b03831660008181526002602090815260408083203380855290835292819020868603908190558151818152915190949260008051602061259b833981519152928290030190a3505b505b6001600160a01b038216600090815260208190526040902054818110156123825760405162461bcd60e51b81526004018080602001828103825260218152602001806125bb6021913960400191505060405180910390fd5b6001600160a01b0383166000818152602081815260409182902085850390558151858152915130939260008051602061257b83398151915292908290030190a35060048054909101905550565b600454818110156123df57600080fd5b336000818152602081815260409182902080548601905584840360045581518581529151309260008051602061257b83398151915292908290030190a35050565b604080518082018252600881526709acaec40aecae8d60c31b6020918201528151808301835260018152603160f81b9082015281517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f818301527fa3b25ed7788bb6df2d164c238f0a3c29680e8a8d21b2285bbc420a79a43f1688818401527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc6606082015260808101939093523060a0808501919091528251808503909101815260c090930190915281519101209056fe574554483a207472616e7366657220616d6f756e7420657863656564732062616c616e6365574554483a2072657175657374206578636565647320616c6c6f77616e636500574554483a20455448207472616e73666572206661696c656400000000000000574554483a20696e646976696475616c206c6f616e206c696d6974206578636565646564ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925574554483a206275726e20616d6f756e7420657863656564732062616c616e6365a2646970667358221220ddc664129365bd5d90a96b819e4f4df73a30f1c7886c7fbf66462fa789aab92d64736f6c63430007060033" 5 | 6 | export FOUNDRY_PROFILE=tests 7 | export FOUNDRY_GAS_PRICE=1000000000 8 | export FOUNDRY_BASE_FEE_PER_GAS=1000000000 9 | 10 | forge test "$@" -------------------------------------------------------------------------------- /test/MWB.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity ^0.8.13; 3 | 4 | import "forge-std/Base.sol"; 5 | 6 | // DO NOT EDIT 7 | // this is a hacky way of deploying a non-0.8 contract by etching its bytecode 8 | 9 | contract MWB is CommonBase { 10 | function etch(address at) public { 11 | vm.etch(at, vm.envBytes("MWB")); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /test/MevWalletV1.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity ^0.8.13; 3 | 4 | import "forge-std/Test.sol"; 5 | import {MevWalletV1} from "../contracts/MevWalletV1.sol"; 6 | import {MevWalletV1Factory} from "../contracts/MevWalletV1Factory.sol"; 7 | import {IERC20} from "../contracts/utils/IERC20.sol"; 8 | import {MWB} from "./MWB.sol"; 9 | import {IMevWeth} from "mev-weth/IMevWeth.sol"; 10 | 11 | contract MevWalletTest is Test { 12 | MevWalletV1Factory factory; 13 | MevWalletV1 mtx; 14 | 15 | uint256 priv = 1337; 16 | address payable user = payable(vm.addr(priv)); 17 | 18 | address payable searcher = payable(vm.addr(31337)); 19 | address friend = vm.addr(31333337); 20 | 21 | address payable constant mwethAddress = payable(0x00000000008C43efC014746c230049e330039Cb3); 22 | address payable constant mtxImplAddress = payable(0x00000000004096437C84E1B0927D5ED44F45F6b3); 23 | 24 | IERC20 weth = IERC20(mwethAddress); 25 | IMevWeth mev = IMevWeth(mwethAddress); 26 | 27 | function setUp() public { 28 | new MWB().etch(mwethAddress); 29 | // this leaves the impl uninitialized, which is AOK in tests :) 30 | vm.etch(mtxImplAddress, type(MevWalletV1).runtimeCode); 31 | 32 | factory = new MevWalletV1Factory(); 33 | mtx = MevWalletV1(payable(factory.createWallet(keccak256("hello"), user))); 34 | 35 | bool success; 36 | (success,) = mwethAddress.call{value: 2 ether}(""); 37 | searcher.transfer(1 ether); 38 | user.transfer(1 ether); 39 | weth.transfer(user, 1 ether); 40 | weth.transfer(address(mtx), 1 ether); 41 | 42 | // // TODO: re-enable this when we can set tx.gasprice 43 | vm.fee(1 gwei); 44 | } 45 | 46 | function buildMevTx(address to, bytes memory data, int256 value, int256 tip) internal view returns (bytes memory) { 47 | uint256 timing = 0; 48 | uint256 n = mtx.nonce(); 49 | uint256 maxBaseFee = 0; 50 | 51 | uint8 v; 52 | bytes32 r; 53 | bytes32 s; 54 | 55 | bytes32 hashStruct = 56 | keccak256(abi.encode(mtx.TX_TYPEHASH(), to, keccak256(data), value, false, tip, maxBaseFee, timing, n)); 57 | bytes32 h = keccak256(abi.encodePacked("\x19\x01", mtx._DOMAIN_SEPARATOR(), hashStruct)); 58 | 59 | (v,r,s) = vm.sign(priv, h); 60 | 61 | return abi.encodeWithSelector( 62 | mtx.mevTx.selector, 63 | to, data, value, false, tip, maxBaseFee, timing, n, v, r, s 64 | ); 65 | } 66 | 67 | function sendMevTx(bytes memory mevTx, uint256 value) internal { 68 | vm.prank(searcher); 69 | bool suc; 70 | (suc,) = address(mtx).call{value: value}(mevTx); 71 | } 72 | 73 | function getMev() internal { 74 | vm.prank(searcher); 75 | mev.getMev(); 76 | } 77 | 78 | function test_setup() public { 79 | assertEq(weth.balanceOf(user), 1 ether); 80 | assertEq(mtx.owner(), user); 81 | assertEq(block.basefee, 1 gwei, "bf != 1gwei"); 82 | assertEq(tx.gasprice, block.basefee, "bf != gp"); 83 | } 84 | 85 | function test_typehash() public { 86 | assertEq(mtx.TX_TYPEHASH(), hex"fd4c9c9ceea85482c3671626f7b1c65bc2230325b86dbcb0f971327c3062c26a"); 87 | } 88 | 89 | function test_sendEthSearcherValue() public { 90 | uint256 toSend = 0.5 ether; 91 | uint256 tip = 500 gwei; 92 | 93 | bytes memory t = buildMevTx(friend, "", int256(toSend), int256(tip)); 94 | sendMevTx(t, toSend); 95 | 96 | assertEq(friend.balance, toSend, "friend recieved"); 97 | assertGe(mev.mev(), tip + toSend, "mev amnt"); 98 | 99 | getMev(); 100 | assertGe(weth.balanceOf(searcher), tip + toSend); 101 | } 102 | 103 | function test_sendEthWalletValue() public { 104 | vm.deal(address(mtx), 15 ether); 105 | 106 | uint256 toSend = 0.5 ether; 107 | uint256 tip = 500 gwei; 108 | 109 | bytes memory t = buildMevTx(friend, "", int256(toSend), int256(tip)); 110 | sendMevTx(t, 0); 111 | 112 | emit log_uint(friend.balance); 113 | 114 | assertEq(friend.balance, toSend, "friend recieved"); 115 | assertGe(mev.mev(), tip, "mev amnt"); 116 | 117 | getMev(); 118 | assertGe(weth.balanceOf(searcher), tip); 119 | } 120 | 121 | function test_sendEthMixedVallue() public { 122 | uint256 startBal = 0.2 ether; 123 | vm.deal(address(mtx), startBal); 124 | 125 | uint256 toSend = 0.5 ether; 126 | uint256 tip = 500 gwei; 127 | 128 | bytes memory t = buildMevTx(friend, "", int256(toSend), int256(tip)); 129 | sendMevTx(t, toSend - startBal); 130 | 131 | emit log_uint(friend.balance); 132 | 133 | assertEq(friend.balance, toSend, "friend recieved"); 134 | assertGe(mev.mev(), tip, "mev amnt"); 135 | 136 | getMev(); 137 | assertGe(weth.balanceOf(searcher), tip + toSend - startBal); 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /tests/integration.rs: -------------------------------------------------------------------------------- 1 | use ethers::{abi::AbiEncode, prelude::*}; 2 | use mev_wallet::{MevTxBuilder, MevWalletV1, MEV_WETH_ADDR, TX_TYPEHASH}; 3 | use std::sync::Arc; 4 | 5 | abigen!( 6 | IERC20, 7 | r#"[ 8 | function transfer(address recipient, uint256 amount) public returns (bool) 9 | function balanceOf(address who) public view returns (uint256) 10 | ]"#, 11 | ); 12 | 13 | static PROVIDER: Lazy>> = 14 | Lazy::new(|| Arc::new(Provider::new("http://127.0.0.1:8545".parse().unwrap()))); 15 | 16 | static KEY: &str = "ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80"; 17 | 18 | static WALLET_ADDR: Lazy = Lazy::new(|| { 19 | "0xf12cf18103fe766f2d1981ea5cf309cc37b04969" 20 | .parse() 21 | .unwrap() 22 | }); 23 | 24 | static SIGNER: Lazy = 25 | Lazy::new(|| KEY.parse::().unwrap().with_chain_id(31337u64)); 26 | 27 | static SIGNER_MWARE: Lazy>, LocalWallet>>> = 28 | Lazy::new(|| Arc::new(SignerMiddleware::new(PROVIDER.clone(), SIGNER.clone()))); 29 | 30 | #[tokio::test] 31 | async fn it() { 32 | assert_eq!( 33 | SIGNER.address(), 34 | "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266" 35 | .parse() 36 | .unwrap() 37 | ); 38 | 39 | let mev_weth = IERC20::new(MEV_WETH_ADDR, SIGNER_MWARE.clone()); 40 | let wallet = MevWalletV1::new(*WALLET_ADDR, SIGNER_MWARE.clone()); 41 | 42 | assert_eq!(wallet.owner().await.unwrap(), SIGNER.address(),); 43 | assert_eq!(TX_TYPEHASH, wallet.tx_typehash().call().await.unwrap()); 44 | 45 | if mev_weth 46 | .balance_of(wallet.address()) 47 | .await 48 | .unwrap() 49 | .is_zero() 50 | { 51 | mev_weth 52 | .transfer(wallet.address(), ethers::utils::parse_ether("2").unwrap()) 53 | .send() 54 | .await 55 | .unwrap() 56 | .await 57 | .unwrap(); 58 | } 59 | 60 | let tx = MevTxBuilder::default() 61 | .wallet(&wallet) 62 | .data( 63 | TransferCall { 64 | recipient: H160::default(), 65 | amount: U256::one(), 66 | } 67 | .encode(), 68 | ) 69 | .to(mev_weth.address()) 70 | .tip(1_000_000_000) 71 | .max_base_fee(U256::from(1230) * 1_000_000_000) 72 | .with_signer(&*SIGNER) 73 | .chain_id(31337) 74 | .populate() 75 | .await 76 | .unwrap(); 77 | 78 | let again = tx.clone(); 79 | let tx = tx.build().await.unwrap(); 80 | 81 | let again = again.nonce(tx.tx().nonce + 1).build().await.unwrap(); 82 | 83 | let call = wallet.send(tx); 84 | call.clone().await.unwrap(); 85 | 86 | let receipt = call.send().await.unwrap().await.unwrap().unwrap(); 87 | assert_eq!(receipt.status, Some(1u64.into())); 88 | 89 | let again_receipt = wallet 90 | .send(again) 91 | .send() 92 | .await 93 | .unwrap() 94 | .await 95 | .unwrap() 96 | .unwrap(); 97 | assert_eq!(again_receipt.status, Some(1u64.into())); 98 | } 99 | --------------------------------------------------------------------------------