diff --git a/.envrc.local-example b/.envrc.local-example index 24f5e91..9bdbbba 100644 --- a/.envrc.local-example +++ b/.envrc.local-example @@ -37,4 +37,8 @@ export BSCSCAN_API_KEY="" export POLYGONSCAN_API_KEY="" export SNOWSCAN_API_KEY="" -export MULTISIG_SAFE=0x617c8dE5BdE54ffbb8d92716CC947858cA38f582 \ No newline at end of file +export MULTISIG_SAFE=0x617c8dE5BdE54ffbb8d92716CC947858cA38f582 + +export CHAIN="mainnet" +export WALLET_TYPE="local" +export NUM_EXITS=1 \ No newline at end of file diff --git a/.github/workflows/foundry-forge-tests.yaml b/.github/workflows/foundry-forge-tests.yaml index d7d49ab..8a6aefe 100644 --- a/.github/workflows/foundry-forge-tests.yaml +++ b/.github/workflows/foundry-forge-tests.yaml @@ -21,6 +21,9 @@ env: BSC_MAINNET_RPC_URL: ${{ secrets.BSC_MAINNET_RPC_URL }} OPTIMISM_RPC_URL: ${{ secrets.OPTIMISM_RPC_URL }} AVALANCHE_RPC_URL: ${{ secrets.AVALANCHE_RPC_URL }} + CHAIN: ${{ secrets.CHAIN }} + WALLET_TYPE: ${{ secrets.WALLET_TYPE }} + PRIVATE_KEY: ${{ secrets.PRIVATE_KEY }} jobs: tests: @@ -45,5 +48,5 @@ jobs: - name: Run Forge tests run: | - forge test --deny-warnings -vvv + forge test --deny-warnings -vvv --ffi id: test diff --git a/.gitmodules b/.gitmodules index 3cd7ce3..6fa1efa 100644 --- a/.gitmodules +++ b/.gitmodules @@ -17,3 +17,6 @@ [submodule "lib/solady"] path = lib/solady url = https://github.com/Vectorized/solady +[submodule "lib/forge-safe"] + path = lib/forge-safe + url = https://github.com/ind-igo/forge-safe diff --git a/flake.lock b/flake.lock index c74dad0..5f2a3b9 100644 --- a/flake.lock +++ b/flake.lock @@ -1,5 +1,21 @@ { "nodes": { + "devour-flake": { + "flake": false, + "locked": { + "lastModified": 1694098737, + "narHash": "sha256-O51F4YFOzlaQAc9b6xjkAqpvrvCtw/Os2M7TU0y4SKQ=", + "owner": "srid", + "repo": "devour-flake", + "rev": "30a34036b29b0d12989ef6c8be77aa949d85aef5", + "type": "github" + }, + "original": { + "owner": "srid", + "repo": "devour-flake", + "type": "github" + } + }, "devshell": { "inputs": { "nixpkgs": [ @@ -8,11 +24,11 @@ "systems": "systems" }, "locked": { - "lastModified": 1683635384, - "narHash": "sha256-9goJTd05yOyD/McaMqZ4BUB8JW+mZMnZQJZ7VQ6C/Lw=", + "lastModified": 1701787589, + "narHash": "sha256-ce+oQR4Zq9VOsLoh9bZT8Ip9PaMLcjjBUHVPzW5d7Cw=", "owner": "numtide", "repo": "devshell", - "rev": "5143ea68647c4cf5227e4ad2100db6671fc4c369", + "rev": "44ddedcbcfc2d52a76b64fb6122f209881bd3e1e", "type": "github" }, "original": { @@ -30,11 +46,11 @@ "systems": "systems_2" }, "locked": { - "lastModified": 1683635384, - "narHash": "sha256-9goJTd05yOyD/McaMqZ4BUB8JW+mZMnZQJZ7VQ6C/Lw=", + "lastModified": 1698410321, + "narHash": "sha256-MphuSlgpmKwtJncGMohryHiK55J1n6WzVQ/OAfmfoMc=", "owner": "numtide", "repo": "devshell", - "rev": "5143ea68647c4cf5227e4ad2100db6671fc4c369", + "rev": "1aed986e3c81a4f6698e85a7452cbfcc4b31a36e", "type": "github" }, "original": { @@ -45,12 +61,14 @@ }, "ethereum-nix": { "inputs": { + "devour-flake": "devour-flake", "devshell": "devshell_2", "flake-compat": "flake-compat", "flake-parts": "flake-parts", "flake-root": "flake-root", "foundry-nix": "foundry-nix", - "hercules-ci-effects": "hercules-ci-effects", + "lib-extras": "lib-extras", + "mynixpkgs": "mynixpkgs", "nixpkgs": [ "nixpkgs" ], @@ -58,11 +76,11 @@ "treefmt-nix": "treefmt-nix" }, "locked": { - "lastModified": 1684937187, - "narHash": "sha256-rBdTMLqGD+zgdUIGnmy4xWfVzkdhSB4tAKfHTJxUqqE=", + "lastModified": 1701261481, + "narHash": "sha256-3wB5yK3Sb0XtcoKJi/iBxqn4PAuvcJvHnPVy43g1U3Y=", "owner": "nix-community", "repo": "ethereum.nix", - "rev": "2352c691a2dcbfda6efea80c4b3816bdb8861c78", + "rev": "1f8b13ca5282b0f304fa8edb4da42fd46f7af0b0", "type": "github" }, "original": { @@ -73,11 +91,11 @@ }, "flake-compat": { "locked": { - "lastModified": 1680531544, - "narHash": "sha256-8qbiDTYb1kGaDADRXTItpcMKQ1TeQVkuof6oEwHUvVA=", + "lastModified": 1688025799, + "narHash": "sha256-ktpB4dRtnksm9F5WawoIkEneh1nrEvuxb5lJFt1iOyw=", "owner": "nix-community", "repo": "flake-compat", - "rev": "95e78dc12268c5e4878621845c511077f3798729", + "rev": "8bf105319d44f6b9f0d764efa4fdef9f1cc9ba1c", "type": "github" }, "original": { @@ -89,27 +107,11 @@ "flake-compat_2": { "flake": false, "locked": { - "lastModified": 1673956053, - "narHash": "sha256-4gtG9iQuiKITOjNQQeQIpoIB6b16fm+504Ch3sNKLd8=", + "lastModified": 1696426674, + "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=", "owner": "edolstra", "repo": "flake-compat", - "rev": "35bb57c0c8d8b62bbfd284272c928ceb64ddbde9", - "type": "github" - }, - "original": { - "owner": "edolstra", - "repo": "flake-compat", - "type": "github" - } - }, - "flake-compat_3": { - "flake": false, - "locked": { - "lastModified": 1673956053, - "narHash": "sha256-4gtG9iQuiKITOjNQQeQIpoIB6b16fm+504Ch3sNKLd8=", - "owner": "edolstra", - "repo": "flake-compat", - "rev": "35bb57c0c8d8b62bbfd284272c928ceb64ddbde9", + "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33", "type": "github" }, "original": { @@ -126,11 +128,11 @@ ] }, "locked": { - "lastModified": 1683560683, - "narHash": "sha256-XAygPMN5Xnk/W2c1aW0jyEa6lfMDZWlQgiNtmHXytPc=", + "lastModified": 1696343447, + "narHash": "sha256-B2xAZKLkkeRFG5XcHHSXXcP7To9Xzr59KXeZiRf4vdQ=", "owner": "hercules-ci", "repo": "flake-parts", - "rev": "006c75898cf814ef9497252b022e91c946ba8e17", + "rev": "c9afaba3dfa4085dbd2ccb38dfade5141e33d9d4", "type": "github" }, "original": { @@ -144,51 +146,11 @@ "nixpkgs-lib": "nixpkgs-lib" }, "locked": { - "lastModified": 1678379998, - "narHash": "sha256-TZdfNqftHhDuIFwBcN9MUThx5sQXCTeZk9je5byPKRw=", - "owner": "hercules-ci", - "repo": "flake-parts", - "rev": "c13d60b89adea3dc20704c045ec4d50dd964d447", - "type": "github" - }, - "original": { - "id": "flake-parts", - "type": "indirect" - } - }, - "flake-parts_3": { - "inputs": { - "nixpkgs-lib": [ - "ethereum-nix", - "hercules-ci-effects", - "hercules-ci-agent", - "nixpkgs" - ] - }, - "locked": { - "lastModified": 1678379998, - "narHash": "sha256-TZdfNqftHhDuIFwBcN9MUThx5sQXCTeZk9je5byPKRw=", - "owner": "hercules-ci", - "repo": "flake-parts", - "rev": "c13d60b89adea3dc20704c045ec4d50dd964d447", - "type": "github" - }, - "original": { - "owner": "hercules-ci", - "repo": "flake-parts", - "type": "github" - } - }, - "flake-parts_4": { - "inputs": { - "nixpkgs-lib": "nixpkgs-lib_2" - }, - "locked": { - "lastModified": 1683560683, - "narHash": "sha256-XAygPMN5Xnk/W2c1aW0jyEa6lfMDZWlQgiNtmHXytPc=", + "lastModified": 1701473968, + "narHash": "sha256-YcVE5emp1qQ8ieHUnxt1wCZCC3ZfAS+SRRWZ2TMda7E=", "owner": "hercules-ci", "repo": "flake-parts", - "rev": "006c75898cf814ef9497252b022e91c946ba8e17", + "rev": "34fed993f1674c8d06d58b37ce1e0fe5eebcb9f5", "type": "github" }, "original": { @@ -199,11 +161,11 @@ }, "flake-root": { "locked": { - "lastModified": 1680964220, - "narHash": "sha256-dIdTYcf+KW9a4pKHsEbddvLVSfR1yiAJynzg2x0nfWg=", + "lastModified": 1692742795, + "narHash": "sha256-f+Y0YhVCIJ06LemO+3Xx00lIcqQxSKJHXT/yk1RTKxw=", "owner": "srid", "repo": "flake-root", - "rev": "f1c0b93d05bdbea6c011136ba1a135c80c5b326c", + "rev": "d9a70d9c7a5fd7f3258ccf48da9335e9b47c3937", "type": "github" }, "original": { @@ -214,11 +176,11 @@ }, "flake-root_2": { "locked": { - "lastModified": 1680964220, - "narHash": "sha256-dIdTYcf+KW9a4pKHsEbddvLVSfR1yiAJynzg2x0nfWg=", + "lastModified": 1692742795, + "narHash": "sha256-f+Y0YhVCIJ06LemO+3Xx00lIcqQxSKJHXT/yk1RTKxw=", "owner": "srid", "repo": "flake-root", - "rev": "f1c0b93d05bdbea6c011136ba1a135c80c5b326c", + "rev": "d9a70d9c7a5fd7f3258ccf48da9335e9b47c3937", "type": "github" }, "original": { @@ -242,29 +204,14 @@ "type": "github" } }, - "flake-utils_2": { - "locked": { - "lastModified": 1667395993, - "narHash": "sha256-nuEHfE/LcWyuSWnS8t12N1wc105Qtau+/OdUAjtQ0rA=", - "owner": "numtide", - "repo": "flake-utils", - "rev": "5aed5285a952e0b949eb3ba02c12fa4fcfef535f", - "type": "github" - }, - "original": { - "owner": "numtide", - "repo": "flake-utils", - "type": "github" - } - }, "forge-std": { "flake": false, "locked": { - "lastModified": 1678985799, - "narHash": "sha256-/Sire5lMLFhDJ9lGSDYU2Mr581ihcIRsxRGKEAQOhT4=", - "ref": "2b58ecbcf3dfde7a75959dc7b4eb3d0670278de6", - "rev": "2b58ecbcf3dfde7a75959dc7b4eb3d0670278de6", - "revCount": 218, + "lastModified": 1689098449, + "narHash": "sha256-sX+xZvnChWHByXd+RGx4xLn3b1gr5ahHDmGlVP5178I=", + "ref": "HEAD", + "rev": "74cfb77e308dd188d2f58864aaf44963ae6b88b1", + "revCount": 264, "type": "git", "url": "file:./lib/forge-std" }, @@ -282,11 +229,11 @@ ] }, "locked": { - "lastModified": 1683191603, - "narHash": "sha256-KdSKdwz3+VJQIHc8sCDwPo2OQtPs5jbxwrLXseHLlfc=", + "lastModified": 1696410815, + "narHash": "sha256-uku47D/L+VzO3sVoZbnexPQPGeQtMwMFBesyaA1vKtE=", "owner": "shazow", "repo": "foundry.nix", - "rev": "5a53423b983f623420733f62c01fa69d1f4cdda9", + "rev": "a56126a754d73f85d904768fed569a9e250388d9", "type": "github" }, "original": { @@ -296,122 +243,125 @@ "type": "github" } }, - "gitignore": { + "haumea": { "inputs": { "nixpkgs": [ "ethereum-nix", - "hercules-ci-effects", - "hercules-ci-agent", - "pre-commit-hooks-nix", + "mynixpkgs", "nixpkgs" ] }, "locked": { - "lastModified": 1660459072, - "narHash": "sha256-8DFJjXG8zqoONA1vXtgeKXy68KdJL5UaXR8NtVMUbx8=", - "owner": "hercules-ci", - "repo": "gitignore.nix", - "rev": "a20de23b925fd8264fd7fad6454652e142fd7f73", - "type": "github" - }, - "original": { - "owner": "hercules-ci", - "repo": "gitignore.nix", - "type": "github" - } - }, - "haskell-flake": { - "locked": { - "lastModified": 1678138103, - "narHash": "sha256-D0lao82bV3t2gEFjHiU6RN233t+1MnkQV+bq8MEu2ic=", - "owner": "hercules-ci", - "repo": "haskell-flake", - "rev": "1e1660e6dd00838ba73bc7952e6e73be67da18d1", + "lastModified": 1685133229, + "narHash": "sha256-FePm/Gi9PBSNwiDFq3N+DWdfxFq0UKsVVTJS3cQPn94=", + "owner": "nix-community", + "repo": "haumea", + "rev": "34dd58385092a23018748b50f9b23de6266dffc2", "type": "github" }, "original": { - "owner": "hercules-ci", - "ref": "0.1-extraLibraries", - "repo": "haskell-flake", - "type": "github" - } - }, - "hercules-ci-agent": { - "inputs": { - "flake-parts": "flake-parts_3", - "haskell-flake": "haskell-flake", - "nix-darwin": "nix-darwin", - "nixpkgs": "nixpkgs", - "pre-commit-hooks-nix": "pre-commit-hooks-nix" - }, - "locked": { - "lastModified": 1678446614, - "narHash": "sha256-Z6Gsba5ahn/N0QlF0vJfIEfnZgCs4qr1IZtXAqjbE7s=", - "owner": "hercules-ci", - "repo": "hercules-ci-agent", - "rev": "0b90d1a87c117a5861785cb85833dd1c9df0b6ef", + "owner": "nix-community", + "ref": "v0.2.2", + "repo": "haumea", "type": "github" - }, - "original": { - "id": "hercules-ci-agent", - "type": "indirect" } }, - "hercules-ci-effects": { + "lib-extras": { "inputs": { - "flake-parts": "flake-parts_2", - "hercules-ci-agent": "hercules-ci-agent", - "nixpkgs": "nixpkgs_2" + "devshell": [ + "ethereum-nix", + "devshell" + ], + "flake-parts": [ + "ethereum-nix", + "flake-parts" + ], + "flake-root": [ + "ethereum-nix", + "flake-root" + ], + "nixpkgs": [ + "ethereum-nix", + "nixpkgs" + ], + "treefmt-nix": [ + "ethereum-nix", + "treefmt-nix" + ] }, "locked": { - "lastModified": 1681898675, - "narHash": "sha256-nIJ7CAdiHv4i1no/VgDoeTJLzbLYwu5+/Ycoyzn0S78=", - "owner": "hercules-ci", - "repo": "hercules-ci-effects", - "rev": "15ff4f63e5f28070391a5b09a82f6d5c6cc5c9d0", + "lastModified": 1699974671, + "narHash": "sha256-4EsuPiX4pGEg8ME9ONn8ebY1ZKYLOp9DRCcdTrOj8sY=", + "owner": "aldoborrero", + "repo": "lib-extras", + "rev": "83c8935af27738b8b155e0077522220d81865269", "type": "github" }, "original": { - "owner": "hercules-ci", - "repo": "hercules-ci-effects", + "owner": "aldoborrero", + "ref": "v0.2.2", + "repo": "lib-extras", "type": "github" } }, - "nix-darwin": { + "mynixpkgs": { "inputs": { + "devour-flake": [ + "ethereum-nix", + "devour-flake" + ], + "devshell": [ + "ethereum-nix", + "devshell" + ], + "flake-parts": [ + "ethereum-nix", + "flake-parts" + ], + "flake-root": [ + "ethereum-nix", + "flake-root" + ], + "haumea": "haumea", + "lib-extras": [ + "ethereum-nix", + "lib-extras" + ], "nixpkgs": [ "ethereum-nix", - "hercules-ci-effects", - "hercules-ci-agent", - "nixpkgs" + "nixpkgs-unstable" + ], + "treefmt-nix": [ + "ethereum-nix", + "treefmt-nix" ] }, "locked": { - "lastModified": 1673295039, - "narHash": "sha256-AsdYgE8/GPwcelGgrntlijMg4t3hLFJFCRF3tL5WVjA=", - "owner": "LnL7", - "repo": "nix-darwin", - "rev": "87b9d090ad39b25b2400029c64825fc2a8868943", + "lastModified": 1701258184, + "narHash": "sha256-RAmIb5DVd4I4jn0Nzwd0iPmO4YZwzsx7Wno5a30k8IM=", + "owner": "aldoborrero", + "repo": "mynixpkgs", + "rev": "0d415cfc494dfc105a0fd5ebf83411be82a4d9b3", "type": "github" }, "original": { - "owner": "LnL7", - "repo": "nix-darwin", + "owner": "aldoborrero", + "repo": "mynixpkgs", "type": "github" } }, "nixpkgs": { "locked": { - "lastModified": 1678293141, - "narHash": "sha256-lLlQHaR0y+q6nd6kfpydPTGHhl1rS9nU9OQmztzKOYs=", + "lastModified": 1688392541, + "narHash": "sha256-lHrKvEkCPTUO+7tPfjIcb7Trk6k31rz18vkyqmkeJfY=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "c90c4025bb6e0c4eaf438128a3b2640314b1c58d", + "rev": "ea4c80b39be4c09702b0cb3b42eab59e2ba4f24b", "type": "github" }, "original": { "owner": "NixOS", - "ref": "nixos-unstable", + "ref": "nixos-22.11", "repo": "nixpkgs", "type": "github" } @@ -419,11 +369,11 @@ "nixpkgs-lib": { "locked": { "dir": "lib", - "lastModified": 1678375444, - "narHash": "sha256-XIgHfGvjFvZQ8hrkfocanCDxMefc/77rXeHvYdzBMc8=", + "lastModified": 1701253981, + "narHash": "sha256-ztaDIyZ7HrTAfEEUt9AtTDNoCYxUdSd6NrRHaYOIxtk=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "130fa0baaa2b93ec45523fdcde942f6844ee9f6e", + "rev": "e92039b55bcd58469325ded85d4f58dd5a4eaf58", "type": "github" }, "original": { @@ -434,47 +384,13 @@ "type": "github" } }, - "nixpkgs-lib_2": { - "locked": { - "dir": "lib", - "lastModified": 1682879489, - "narHash": "sha256-sASwo8gBt7JDnOOstnps90K1wxmVfyhsTPPNTGBPjjg=", - "owner": "NixOS", - "repo": "nixpkgs", - "rev": "da45bf6ec7bbcc5d1e14d3795c025199f28e0de0", - "type": "github" - }, - "original": { - "dir": "lib", - "owner": "NixOS", - "ref": "nixos-unstable", - "repo": "nixpkgs", - "type": "github" - } - }, - "nixpkgs-stable": { - "locked": { - "lastModified": 1673800717, - "narHash": "sha256-SFHraUqLSu5cC6IxTprex/nTsI81ZQAtDvlBvGDWfnA=", - "owner": "NixOS", - "repo": "nixpkgs", - "rev": "2f9fd351ec37f5d479556cd48be4ca340da59b8f", - "type": "github" - }, - "original": { - "owner": "NixOS", - "ref": "nixos-22.11", - "repo": "nixpkgs", - "type": "github" - } - }, "nixpkgs-unstable": { "locked": { - "lastModified": 1684585791, - "narHash": "sha256-lYPboblKrchmbkGMoAcAivomiOscZCjtGxxTSCY51SM=", + "lastModified": 1698336494, + "narHash": "sha256-sO72WDBKyijYD1GcKPlGsycKbMBiTJMBCnmOxLAs880=", "owner": "nixos", "repo": "nixpkgs", - "rev": "eea79d584eff53bf7a76aeb63f8845da6d386129", + "rev": "808c0d8c53c7ae50f82aca8e7df263225cf235bf", "type": "github" }, "original": { @@ -486,11 +402,11 @@ }, "nixpkgs-unstable_2": { "locked": { - "lastModified": 1684973047, - "narHash": "sha256-ZLnSr35L6C49pCZS9fZCCqkIKNAeQzykov2QfosNG9w=", + "lastModified": 1702272962, + "narHash": "sha256-D+zHwkwPc6oYQ4G3A1HuadopqRwUY/JkMwHz1YF7j4Q=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "21eb6c6ba74dcbe3ea5926ee46287300fb066630", + "rev": "e97b3e4186bcadf0ef1b6be22b8558eab1cdeb5d", "type": "github" }, "original": { @@ -500,71 +416,13 @@ "type": "github" } }, - "nixpkgs_2": { - "locked": { - "lastModified": 1678891326, - "narHash": "sha256-cjgrjKx7y+hO9I8O2b6QvBaTt9w7Xhk/5hsnJYTUb2I=", - "owner": "NixOS", - "repo": "nixpkgs", - "rev": "1544ef240132d4357d9a39a40c8e6afd1678b052", - "type": "github" - }, - "original": { - "owner": "NixOS", - "repo": "nixpkgs", - "type": "github" - } - }, - "nixpkgs_3": { - "locked": { - "lastModified": 1684936879, - "narHash": "sha256-BOSq/QiX7MDs8tUnAt4+nYTJctgYkzVSNL95qlfMYeM=", - "owner": "NixOS", - "repo": "nixpkgs", - "rev": "99fe1b870522d6ee3e692c2b6e663d6868a3fde4", - "type": "github" - }, - "original": { - "owner": "NixOS", - "ref": "nixos-22.11", - "repo": "nixpkgs", - "type": "github" - } - }, - "pre-commit-hooks-nix": { - "inputs": { - "flake-compat": "flake-compat_2", - "flake-utils": "flake-utils_2", - "gitignore": "gitignore", - "nixpkgs": [ - "ethereum-nix", - "hercules-ci-effects", - "hercules-ci-agent", - "nixpkgs" - ], - "nixpkgs-stable": "nixpkgs-stable" - }, - "locked": { - "lastModified": 1678376203, - "narHash": "sha256-3tyYGyC8h7fBwncLZy5nCUjTJPrHbmNwp47LlNLOHSM=", - "owner": "cachix", - "repo": "pre-commit-hooks.nix", - "rev": "1a20b9708962096ec2481eeb2ddca29ed747770a", - "type": "github" - }, - "original": { - "owner": "cachix", - "repo": "pre-commit-hooks.nix", - "type": "github" - } - }, "process-compose-flake": { "locked": { - "lastModified": 1680797953, - "narHash": "sha256-lFYbfId1IX6jh/wUf+gt3LyvE9HblS4hcBQcougSzX0=", + "lastModified": 1701368682, + "narHash": "sha256-YkZbzfOkv68YOX4fK6VQvNHpysyZ/x3gePL3wbo8giA=", "owner": "Platonic-Systems", "repo": "process-compose-flake", - "rev": "aee1b8d126a5efe5945513eb2fb343f3d68dca4b", + "rev": "8edcd4de7c631eac2ce5f8e2a0782e0ca606da9b", "type": "github" }, "original": { @@ -577,11 +435,11 @@ "inputs": { "devshell": "devshell", "ethereum-nix": "ethereum-nix", - "flake-compat": "flake-compat_3", - "flake-parts": "flake-parts_4", + "flake-compat": "flake-compat_2", + "flake-parts": "flake-parts_2", "flake-root": "flake-root_2", "forge-std": "forge-std", - "nixpkgs": "nixpkgs_3", + "nixpkgs": "nixpkgs", "nixpkgs-unstable": "nixpkgs-unstable_2", "process-compose-flake": "process-compose-flake", "solmate": "solmate", @@ -591,11 +449,11 @@ "solmate": { "flake": false, "locked": { - "lastModified": 1675183387, - "narHash": "sha256-7jLCdPyDNpjs6sXV3DWg7dYN37VrRGmmWlcSpXhgEBY=", - "ref": "1b3adf677e7e383cc684b5d5bd441da86bf4bf1c", - "rev": "1b3adf677e7e383cc684b5d5bd441da86bf4bf1c", - "revCount": 419, + "lastModified": 1680718407, + "narHash": "sha256-GRD/OsTkjczhrofU2GVhkn7gi+bbEcT5OsvIFOZPB1c=", + "ref": "HEAD", + "rev": "e8f96f25d48fe702117ce76c79228ca4f20206cb", + "revCount": 421, "type": "git", "url": "file:./lib/solmate" }, @@ -642,11 +500,11 @@ ] }, "locked": { - "lastModified": 1684416994, - "narHash": "sha256-KkZ9diPRl3Y05TngWYs/QhZKnI/3tA3s+2Hhmei8FnE=", + "lastModified": 1698438538, + "narHash": "sha256-AWxaKTDL3MtxaVTVU5lYBvSnlspOS0Fjt8GxBgnU0Do=", "owner": "numtide", "repo": "treefmt-nix", - "rev": "42045102f90cfd23ca44ae4ef8362180fefcd7fd", + "rev": "5deb8dc125a9f83b65ca86cf0c8167c46593e0b1", "type": "github" }, "original": { @@ -662,11 +520,11 @@ ] }, "locked": { - "lastModified": 1684751370, - "narHash": "sha256-kgeynoy//2NIHgIjssco21UdUgRXr4Gdczt8/JwkJnU=", + "lastModified": 1702461037, + "narHash": "sha256-ssyGxfGHRuuLHuMex+vV6RMOt7nAo07nwufg9L5GkLg=", "owner": "numtide", "repo": "treefmt-nix", - "rev": "4e92552731aca44ece86731ec4c7848a2c46aa67", + "rev": "d06b70e5163a903f19009c3f97770014787a080f", "type": "github" }, "original": { diff --git a/lib/forge-safe b/lib/forge-safe new file mode 160000 index 0000000..4ce820b --- /dev/null +++ b/lib/forge-safe @@ -0,0 +1 @@ +Subproject commit 4ce820bad668846207f34b6f439021ad151f0a1e diff --git a/nix/process-compose.nix b/nix/process-compose.nix index c9dffcc..64cb709 100644 --- a/nix/process-compose.nix +++ b/nix/process-compose.nix @@ -22,16 +22,6 @@ }; }; - config.devshells.default = { - commands = let - category = "development"; - in [ - { - inherit category; - help = "Run local Ethereum network for development"; - package = self'.packages.dev-net; - } - ]; - }; + }; } diff --git a/nix/shell.nix b/nix/shell.nix index 77520a3..48f100e 100644 --- a/nix/shell.nix +++ b/nix/shell.nix @@ -408,6 +408,59 @@ $REWARDS ''; } + { + category = "multisig"; + name = "process-queue-rewards"; + help = "Construct batch tx to process withdraw queue with rewards and send tx to safe for other signatures"; + command = '' + forge script $PRJ_ROOT/script/ProcessQueueRewards.s.sol:ProcessQueueRewardsScript \ + --chain-id 1 \ + --rpc-url $RPC_MAINNET \ + --private-key $PRIVATE_KEY \ + --ffi \ + -vvvvv + ''; + } + { + category = "multisig"; + name = "process-queue-exits"; + help = "Construct batch tx to process withdraw queue with exits and send tx to safe for other signatures"; + command = '' + forge script $PRJ_ROOT/script/ProcessQueueExits.s.sol:ProcessQueueExitsScript \ + --chain-id 1 \ + --rpc-url $RPC_MAINNET \ + --private-key $PRIVATE_KEY \ + --ffi \ + -vvvvv + ''; + } + { + category = "multisig"; + name = "process-queue-exits-and-rewards"; + help = "Construct batch tx to process withdraw queue with exits and rewards and send tx to safe for other signatures"; + command = '' + forge script $PRJ_ROOT/script/ProcessQueueExitsandRewards.s.sol:ProcessQueueExitsandRewardsScript \ + --sig "run(uint256)" $NUM_EXITS\ + --chain-id 1 \ + --rpc-url $RPC_MAINNET \ + --private-key $PRIVATE_KEY \ + --ffi \ + -vvvvv + ''; + } + { + category = "multisig"; + name = "process-queue-exits-and-rewards-test"; + help = "Construct batch tx to process withdraw queue with exits and rewards and send tx to safe for other signatures"; + command = '' + forge script $PRJ_ROOT/script/ProcessQueueExitsandRewards.s.sol:ProcessQueueExitsandRewardsScript \ + --sig "run(uint256)" $NUM_EXITS\ + --chain-id 1 \ + --fork-url $RPC_MAINNET \ + --ffi \ + -vvvvv + ''; + } { category = "tests"; name = "tests"; diff --git a/remappings.txt b/remappings.txt index 87b4aeb..a7fbe19 100644 --- a/remappings.txt +++ b/remappings.txt @@ -6,3 +6,4 @@ safe-contracts/=lib/safe-tools/lib/safe-contracts/contracts/ safe-tools/=lib/safe-tools/src/ properties/=lib/properties/contracts/ solady/utils/=lib/solady/src/utils/ +forge-safe/=lib/forge-safe/src/ diff --git a/script/ProcessQueueExits.s.sol b/script/ProcessQueueExits.s.sol new file mode 100644 index 0000000..eaac8bf --- /dev/null +++ b/script/ProcessQueueExits.s.sol @@ -0,0 +1,79 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.19; + +import "forge-std/Script.sol"; +import { ITinyMevEth } from "src/interfaces/ITinyMevEth.sol"; +import { BatchScript } from "forge-safe/BatchScript.sol"; + +interface IMevEthQueue is ITinyMevEth { + function processWithdrawalQueue(uint256 newRequestsFinalisedUntil) external; + function requestsFinalisedUntil() external returns (uint256); + function queueLength() external returns (uint256); + function withdrawalQueue(uint256 ticketNumber) external returns (bool claimed, address receiver, uint128 amount, uint128 accumulatedAmount); + function claim(uint256 ticketNumber) external; + function addOperator(address) external; + function operators(address) external returns (bool); +} + +/// @notice script to process withdraw queue +contract ProcessQueueExitsScript is BatchScript { + error NothingToProcess(); + + function run() public { + address safe = 0x617c8dE5BdE54ffbb8d92716CC947858cA38f582; + IMevEthQueue mevEth = IMevEthQueue(0x24Ae2dA0f361AA4BE46b48EB19C91e02c5e4f27E); + + // assuming safe balance = exits balance + uint256 numExits = safe.balance / 32 ether; + uint256 exitsBalance = numExits * 32 ether; + + if (numExits == 0) { + revert NothingToProcess(); + } + + // get the withdraw queue offset + uint256 queueOffset = mevEth.requestsFinalisedUntil(); + // calculate how many tickets can be processed with balance + uint256 requestLen = queueOffset; + uint256 queueLen = mevEth.queueLength(); + uint256 amountToProcess; + (,,, uint128 initAccumulatedAmount) = mevEth.withdrawalQueue(queueOffset); + for (uint256 i = queueOffset + 1; i < queueLen; i++) { + (,, uint256 amount, uint128 accumulatedAmount) = mevEth.withdrawalQueue(i); + if (accumulatedAmount - initAccumulatedAmount > exitsBalance) { + break; + } + amountToProcess += amount; + requestLen = i; + } + if (requestLen == queueOffset) { + revert NothingToProcess(); + } + numExits = amountToProcess / 32 ether + 1; + + bytes memory txn; + for (uint256 i; i < numExits; i++) { + txn = abi.encodeWithSelector(mevEth.grantValidatorWithdraw.selector); + addToBatch(address(mevEth), 32 ether, txn); + } + + if (!mevEth.operators(safe)) { + txn = abi.encodeWithSelector(mevEth.addOperator.selector, safe); + addToBatch(address(mevEth), 0, txn); + } + + txn = abi.encodeWithSelector(mevEth.processWithdrawalQueue.selector, requestLen); + + addToBatch(address(mevEth), 0, txn); + + for (uint256 i = queueOffset + 1; i < requestLen + 1; i++) { + txn = abi.encodeWithSelector(mevEth.claim.selector, i); + addToBatch(address(mevEth), 0, txn); + } + + vm.startBroadcast(); + + executeBatch(safe, false); + vm.stopBroadcast(); + } +} diff --git a/script/ProcessQueueExitsandRewards.s.sol b/script/ProcessQueueExitsandRewards.s.sol new file mode 100644 index 0000000..d760116 --- /dev/null +++ b/script/ProcessQueueExitsandRewards.s.sol @@ -0,0 +1,105 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.19; + +import "forge-std/Script.sol"; +import { ITinyMevEth } from "src/interfaces/ITinyMevEth.sol"; +import { BatchScript } from "forge-safe/BatchScript.sol"; + +interface IMevEthQueue is ITinyMevEth { + function processWithdrawalQueue(uint256 newRequestsFinalisedUntil) external; + function requestsFinalisedUntil() external returns (uint256); + function queueLength() external returns (uint256); + function withdrawalQueue(uint256 ticketNumber) external returns (bool claimed, address receiver, uint128 amount, uint128 accumulatedAmount); + function claim(uint256 ticketNumber) external; + function addOperator(address) external; + function operators(address) external returns (bool); +} + +/// @notice script to process withdraw queue +contract ProcessQueueExitsandRewardsScript is BatchScript { + error NothingToProcess(); + + function run(uint256 numExits) public { + address safe = 0x617c8dE5BdE54ffbb8d92716CC947858cA38f582; + address treasury = 0xe664B134d96fdB0bf7951E0c0557B87Bac5e5277; + IMevEthQueue mevEth = IMevEthQueue(0x24Ae2dA0f361AA4BE46b48EB19C91e02c5e4f27E); + + uint256 exitsBalance = numExits * 32 ether; + // assuming rewards balance = remaing multisig balance - 10% + uint256 rewardsBalance = (safe.balance - exitsBalance) * 90 / 100; + + uint256 payout = exitsBalance + rewardsBalance + address(mevEth).balance; + + if (numExits == 0 && rewardsBalance == 0) { + revert NothingToProcess(); + } + + // get the withdraw queue offset + uint256 queueOffset = mevEth.requestsFinalisedUntil(); + // calculate how many tickets can be processed with balance + uint256 requestLen = queueOffset; + uint256 queueLen = mevEth.queueLength(); + // uint256 queueLen = 5; + uint256 amountToProcess; + uint256 rewardsToProcess; + (,,, uint128 initAccumulatedAmount) = mevEth.withdrawalQueue(queueOffset); + for (uint256 i = queueOffset + 1; i < queueLen + 1; i++) { + (,, uint256 amount, uint128 accumulatedAmount) = mevEth.withdrawalQueue(i); + if (accumulatedAmount - initAccumulatedAmount > payout) { + break; + } + amountToProcess += amount; + requestLen = i; + } + if (requestLen == queueOffset) { + revert NothingToProcess(); + } + + if (amountToProcess < exitsBalance) { + numExits = amountToProcess / 32 ether + 1; + // rewards to process are zero for queue + } else { + // numExits stays the max as original + rewardsToProcess = amountToProcess - exitsBalance; + if (address(mevEth).balance >= rewardsToProcess) { + rewardsToProcess = 0; + } else { + rewardsToProcess = rewardsToProcess - address(mevEth).balance; + } + } + + // build tx + bytes memory txn; + // each exit payment called separately + for (uint256 i; i < numExits; i++) { + txn = abi.encodeWithSelector(mevEth.grantValidatorWithdraw.selector); + addToBatch(address(mevEth), 32 ether, txn); + } + // rewards payout for queue + if (rewardsToProcess > 0) { + txn = abi.encodeWithSelector(mevEth.grantRewards.selector); + addToBatch(address(mevEth), rewardsToProcess, txn); + // transfer admin fee + addToBatch(treasury, rewardsToProcess / 10, new bytes(0)); + } + + if (!mevEth.operators(safe)) { + txn = abi.encodeWithSelector(mevEth.addOperator.selector, safe); + addToBatch(address(mevEth), 0, txn); + } + + txn = abi.encodeWithSelector(mevEth.processWithdrawalQueue.selector, requestLen); + + addToBatch(address(mevEth), 0, txn); + + for (uint256 i = queueOffset + 1; i < requestLen + 1; i++) { + txn = abi.encodeWithSelector(mevEth.claim.selector, i); + addToBatch(address(mevEth), 0, txn); + } + + vm.startBroadcast(); + + executeBatch(safe, false); + vm.stopBroadcast(); + } +} diff --git a/script/ProcessQueueRewards.s.sol b/script/ProcessQueueRewards.s.sol new file mode 100644 index 0000000..28082dd --- /dev/null +++ b/script/ProcessQueueRewards.s.sol @@ -0,0 +1,70 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.19; + +import "forge-std/Script.sol"; +import { ITinyMevEth } from "src/interfaces/ITinyMevEth.sol"; +import { BatchScript } from "forge-safe/BatchScript.sol"; + +interface IMevEthQueue is ITinyMevEth { + function processWithdrawalQueue(uint256 newRequestsFinalisedUntil) external; + function requestsFinalisedUntil() external returns (uint256); + function queueLength() external returns (uint256); + function withdrawalQueue(uint256 ticketNumber) external returns (bool claimed, address receiver, uint128 amount, uint128 accumulatedAmount); + function claim(uint256 ticketNumber) external; + function addOperator(address) external; + function operators(address) external returns (bool); +} + +/// @notice script to process withdraw queue +contract ProcessQueueRewardsScript is BatchScript { + error NothingToProcess(); + + function run() public { + address safe = 0x617c8dE5BdE54ffbb8d92716CC947858cA38f582; + IMevEthQueue mevEth = IMevEthQueue(0x24Ae2dA0f361AA4BE46b48EB19C91e02c5e4f27E); + + // assuming rewards balance = multisig balance - 10% + uint256 rewardsBalance = safe.balance * 90 / 100; + // get the withdraw queue offset + uint256 queueOffset = mevEth.requestsFinalisedUntil(); + // calculate how many tickets can be processed with balance + uint256 requestLen = queueOffset; + uint256 queueLen = mevEth.queueLength(); + uint256 amountToProcess; + (,,, uint128 initAccumulatedAmount) = mevEth.withdrawalQueue(queueOffset); + for (uint256 i = queueOffset + 1; i < queueLen; i++) { + (,, uint256 amount, uint128 accumulatedAmount) = mevEth.withdrawalQueue(i); + if (accumulatedAmount - initAccumulatedAmount > rewardsBalance) { + break; + } + amountToProcess += amount; + requestLen = i; + } + if (requestLen == queueOffset) { + revert NothingToProcess(); + } + + bytes memory txn = abi.encodeWithSelector(mevEth.grantRewards.selector); + + addToBatch(address(mevEth), amountToProcess, txn); + + if (!mevEth.operators(safe)) { + txn = abi.encodeWithSelector(mevEth.addOperator.selector, safe); + addToBatch(address(mevEth), 0, txn); + } + + txn = abi.encodeWithSelector(mevEth.processWithdrawalQueue.selector, requestLen); + + addToBatch(address(mevEth), 0, txn); + + for (uint256 i = queueOffset + 1; i < requestLen + 1; i++) { + txn = abi.encodeWithSelector(mevEth.claim.selector, i); + addToBatch(address(mevEth), 0, txn); + } + + vm.startBroadcast(); + + executeBatch(safe, false); + vm.stopBroadcast(); + } +} diff --git a/test/ProcessQueueExits.t.sol b/test/ProcessQueueExits.t.sol new file mode 100644 index 0000000..cd31c5b --- /dev/null +++ b/test/ProcessQueueExits.t.sol @@ -0,0 +1,24 @@ +/// SPDX: License-Identifier: MIT +pragma solidity ^0.8.19; + +// Test utils +import "forge-std/Test.sol"; + +import "script/ProcessQueueExits.s.sol"; + +contract ProcessQueueRewardsTest is Test { + string RPC_ETH_MAINNET = vm.envString("RPC_MAINNET"); + uint256 FORK_ID; + ProcessQueueExitsScript process; + + function setUp() public virtual { + FORK_ID = vm.createSelectFork(RPC_ETH_MAINNET, 18_977_178); + process = new ProcessQueueExitsScript(); + } + + function testProcessQueueExits() public virtual { + vm.selectFork(FORK_ID); + vm.deal(0x617c8dE5BdE54ffbb8d92716CC947858cA38f582, 4 * 32 ether); + process.run(); + } +} diff --git a/test/ProcessQueueExitsandRewards.t.sol b/test/ProcessQueueExitsandRewards.t.sol new file mode 100644 index 0000000..2af9851 --- /dev/null +++ b/test/ProcessQueueExitsandRewards.t.sol @@ -0,0 +1,27 @@ +/// SPDX: License-Identifier: MIT +pragma solidity ^0.8.19; + +// Test utils +import "forge-std/Test.sol"; + +import "script/ProcessQueueExitsandRewards.s.sol"; + +contract ProcessQueueExitsandRewardsTest is Test { + string RPC_ETH_MAINNET = vm.envString("RPC_MAINNET"); + uint256 FORK_ID; + ProcessQueueExitsandRewardsScript process; + IMevEthQueue mevEth = IMevEthQueue(0x24Ae2dA0f361AA4BE46b48EB19C91e02c5e4f27E); + + function setUp() public virtual { + FORK_ID = vm.createSelectFork(RPC_ETH_MAINNET, 18_977_178); + process = new ProcessQueueExitsandRewardsScript(); + } + + function testProcessQueueExitsandRewards() public virtual { + vm.selectFork(FORK_ID); + vm.deal(0x617c8dE5BdE54ffbb8d92716CC947858cA38f582, 4 * 32 ether + 49 ether); + vm.deal(address(mevEth), 0.51327282151044822 ether); + process.run(5); + assertEq(mevEth.requestsFinalisedUntil(), 150); + } +} diff --git a/test/ProcessQueueRewards.t.sol b/test/ProcessQueueRewards.t.sol new file mode 100644 index 0000000..d0c7850 --- /dev/null +++ b/test/ProcessQueueRewards.t.sol @@ -0,0 +1,24 @@ +/// SPDX: License-Identifier: MIT +pragma solidity ^0.8.19; + +// Test utils +import "forge-std/Test.sol"; + +import "script/ProcessQueueRewards.s.sol"; + +contract ProcessQueueRewardsTest is Test { + string RPC_ETH_MAINNET = vm.envString("RPC_MAINNET"); + uint256 FORK_ID; + ProcessQueueRewardsScript process; + + function setUp() public virtual { + FORK_ID = vm.createSelectFork(RPC_ETH_MAINNET, 18_977_178); + process = new ProcessQueueRewardsScript(); + } + + function testProcessQueueRewards() public virtual { + vm.selectFork(FORK_ID); + vm.deal(0x617c8dE5BdE54ffbb8d92716CC947858cA38f582, 100 ether); + process.run(); + } +} diff --git a/test/unit/CreamRedeem.t.sol b/test/unit/CreamRedeem.t.sol index 8de6a7f..11a37de 100644 --- a/test/unit/CreamRedeem.t.sol +++ b/test/unit/CreamRedeem.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity 0.8.19; +pragma solidity ^0.8.19; import "forge-std/Test.sol"; import "../MevEthTest.sol"; @@ -16,7 +16,7 @@ contract CreamRedeemTest is MevEthTest { address constant CRETH2_HOLDER = 0x36cc7B13029B5DEe4034745FB4F24034f3F2ffc6; function setUp() public override { - MAINNET_FORK_ID = vm.createSelectFork(RPC_ETH_MAINNET); + MAINNET_FORK_ID = vm.createSelectFork(RPC_ETH_MAINNET, 18_282_730); vm.selectFork(MAINNET_FORK_ID); // deploy mevEth (mainnet)