diff --git a/.gitignore b/.gitignore index f7cb527..1869051 100644 --- a/.gitignore +++ b/.gitignore @@ -6,4 +6,5 @@ onchain/out onchain/cache .direnv .pre-commit-config.yaml -**/result \ No newline at end of file +**/result +nixos.qcow2 \ No newline at end of file diff --git a/flake.lock b/flake.lock index 80eb92d..03ab6c9 100644 --- a/flake.lock +++ b/flake.lock @@ -1,5 +1,28 @@ { "nodes": { + "agenix-shell": { + "inputs": { + "flake-parts": "flake-parts", + "flake-root": "flake-root", + "git-hooks-nix": "git-hooks-nix", + "nix-github-actions": "nix-github-actions", + "nixpkgs": "nixpkgs", + "treefmt-nix": "treefmt-nix" + }, + "locked": { + "lastModified": 1747248416, + "narHash": "sha256-mthvi7EARHz01rqyJEvyZtrXooKEEoLkt7Fhu2W1djM=", + "owner": "aciceri", + "repo": "agenix-shell", + "rev": "df2787101d5feb8f82e50d100ad37fc0b6c53b75", + "type": "github" + }, + "original": { + "owner": "aciceri", + "repo": "agenix-shell", + "type": "github" + } + }, "flake-compat": { "flake": false, "locked": { @@ -16,10 +39,44 @@ "type": "github" } }, + "flake-compat_2": { + "flake": false, + "locked": { + "lastModified": 1696426674, + "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=", + "owner": "edolstra", + "repo": "flake-compat", + "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33", + "type": "github" + }, + "original": { + "owner": "edolstra", + "repo": "flake-compat", + "type": "github" + } + }, "flake-parts": { "inputs": { "nixpkgs-lib": "nixpkgs-lib" }, + "locked": { + "lastModified": 1743550720, + "narHash": "sha256-hIshGgKZCgWh6AYJpJmRgFdR3WUbkY04o82X05xqQiY=", + "owner": "hercules-ci", + "repo": "flake-parts", + "rev": "c621e8422220273271f52058f618c94e405bb0f5", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "flake-parts", + "type": "github" + } + }, + "flake-parts_2": { + "inputs": { + "nixpkgs-lib": "nixpkgs-lib_2" + }, "locked": { "lastModified": 1741352980, "narHash": "sha256-+u2UunDA4Cl5Fci3m7S643HzKmIDAe+fiXrLqYsR2fs=", @@ -49,6 +106,21 @@ "type": "github" } }, + "flake-root_2": { + "locked": { + "lastModified": 1723604017, + "narHash": "sha256-rBtQ8gg+Dn4Sx/s+pvjdq3CB2wQNzx9XGFq/JVGCB6k=", + "owner": "srid", + "repo": "flake-root", + "rev": "b759a56851e10cb13f6b8e5698af7b59c44be26e", + "type": "github" + }, + "original": { + "owner": "srid", + "repo": "flake-root", + "type": "github" + } + }, "forge-std": { "flake": false, "locked": { @@ -68,8 +140,8 @@ }, "git-hooks": { "inputs": { - "flake-compat": "flake-compat", - "gitignore": "gitignore", + "flake-compat": "flake-compat_2", + "gitignore": "gitignore_2", "nixpkgs": [ "nixpkgs" ] @@ -88,7 +160,52 @@ "type": "github" } }, + "git-hooks-nix": { + "inputs": { + "flake-compat": "flake-compat", + "gitignore": "gitignore", + "nixpkgs": [ + "agenix-shell", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1746537231, + "narHash": "sha256-Wb2xeSyOsCoTCTj7LOoD6cdKLEROyFAArnYoS+noCWo=", + "owner": "cachix", + "repo": "git-hooks.nix", + "rev": "fa466640195d38ec97cf0493d6d6882bc4d14969", + "type": "github" + }, + "original": { + "owner": "cachix", + "repo": "git-hooks.nix", + "type": "github" + } + }, "gitignore": { + "inputs": { + "nixpkgs": [ + "agenix-shell", + "git-hooks-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1709087332, + "narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=", + "owner": "hercules-ci", + "repo": "gitignore.nix", + "rev": "637db329424fd7e46cf4185293b9cc8c88c95394", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "gitignore.nix", + "type": "github" + } + }, + "gitignore_2": { "inputs": { "nixpkgs": [ "git-hooks", @@ -110,6 +227,27 @@ } }, "nix-github-actions": { + "inputs": { + "nixpkgs": [ + "agenix-shell", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1737420293, + "narHash": "sha256-F1G5ifvqTpJq7fdkT34e/Jy9VCyzd5XfJ9TO8fHhJWE=", + "owner": "nix-community", + "repo": "nix-github-actions", + "rev": "f4158fa080ef4503c8f4c820967d946c2af31ec9", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "nix-github-actions", + "type": "github" + } + }, + "nix-github-actions_2": { "inputs": { "nixpkgs": [ "nixpkgs" @@ -131,11 +269,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1742669843, - "narHash": "sha256-G5n+FOXLXcRx+3hCJ6Rt6ZQyF1zqQ0DL0sWAMn2Nk0w=", + "lastModified": 1746663147, + "narHash": "sha256-Ua0drDHawlzNqJnclTJGf87dBmaO/tn7iZ+TCkTRpRc=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "1e5b653dff12029333a6546c11e108ede13052eb", + "rev": "dda3dcd3fe03e991015e9a74b22d35950f264a54", "type": "github" }, "original": { @@ -146,6 +284,21 @@ } }, "nixpkgs-lib": { + "locked": { + "lastModified": 1743296961, + "narHash": "sha256-b1EdN3cULCqtorQ4QeWgLMrd5ZGOjLSLemfa00heasc=", + "owner": "nix-community", + "repo": "nixpkgs.lib", + "rev": "e4822aea2a6d1cdd36653c134cacfd64c97ff4fa", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "nixpkgs.lib", + "type": "github" + } + }, + "nixpkgs-lib_2": { "locked": { "lastModified": 1740877520, "narHash": "sha256-oiwv/ZK/2FhGxrCkQkB83i7GnWXPPLzoqFHpDD3uYpk=", @@ -160,18 +313,56 @@ "type": "github" } }, + "nixpkgs_2": { + "locked": { + "lastModified": 1742669843, + "narHash": "sha256-G5n+FOXLXcRx+3hCJ6Rt6ZQyF1zqQ0DL0sWAMn2Nk0w=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "1e5b653dff12029333a6546c11e108ede13052eb", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, "root": { "inputs": { - "flake-parts": "flake-parts", - "flake-root": "flake-root", + "agenix-shell": "agenix-shell", + "flake-parts": "flake-parts_2", + "flake-root": "flake-root_2", "forge-std": "forge-std", "git-hooks": "git-hooks", - "nix-github-actions": "nix-github-actions", - "nixpkgs": "nixpkgs", - "treefmt-nix": "treefmt-nix" + "nix-github-actions": "nix-github-actions_2", + "nixpkgs": "nixpkgs_2", + "treefmt-nix": "treefmt-nix_2" } }, "treefmt-nix": { + "inputs": { + "nixpkgs": [ + "agenix-shell", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1746216483, + "narHash": "sha256-4h3s1L/kKqt3gMDcVfN8/4v2jqHrgLIe4qok4ApH5x4=", + "owner": "numtide", + "repo": "treefmt-nix", + "rev": "29ec5026372e0dec56f890e50dbe4f45930320fd", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "treefmt-nix", + "type": "github" + } + }, + "treefmt-nix_2": { "inputs": { "nixpkgs": [ "nixpkgs" diff --git a/flake.nix b/flake.nix index ddf89a1..f1e1dab 100644 --- a/flake.nix +++ b/flake.nix @@ -10,6 +10,7 @@ url = "github:cachix/git-hooks.nix"; inputs.nixpkgs.follows = "nixpkgs"; }; + agenix-shell.url = "github:aciceri/agenix-shell"; flake-root.url = "github:srid/flake-root"; nix-github-actions = { url = "github:nix-community/nix-github-actions"; @@ -22,15 +23,23 @@ }; outputs = inputs: - inputs.flake-parts.lib.mkFlake { inherit inputs; } ({ config, lib, ... }: { - systems = [ "x86_64-linux" ]; + inputs.flake-parts.lib.mkFlake { inherit inputs; } (flake@{ config, lib, moduleWithSystem, withSystem, ... }: { + systems = [ "x86_64-linux" "aarch64-linux" ]; - imports = [ - inputs.git-hooks.flakeModule - inputs.treefmt-nix.flakeModule - inputs.flake-root.flakeModule + imports = with inputs; [ + git-hooks.flakeModule + treefmt-nix.flakeModule + flake-root.flakeModule + agenix-shell.flakeModules.agenix-shell ]; + agenix-shell = { + secrets = { + ALCHEMY_KEY.file = ./secrets/alchemy_key.age; + WALLET_PRIVATE_KEY.file = ./secrets/wallet_private_key.age; + }; + }; + perSystem = { pkgs, config, ... }: { treefmt.config = { flakeFormatter = true; @@ -38,6 +47,7 @@ programs = { nixpkgs-fmt.enable = true; rustfmt.enable = true; + actionlint.enable = true; }; }; @@ -52,9 +62,11 @@ }; devShells.default = pkgs.mkShell { - packages = with pkgs; [ cargo rustc rust-analyzer clippy foundry ]; + packages = with pkgs; [ cargo rustc rust-analyzer clippy foundry typst tinymist age ragenix ]; inputsFrom = [ config.flake-root.devShell ]; shellHook = '' + source ${lib.getExe config.agenix-shell.installationScript} + # forge will use this directory to download the solc compilers mkdir -p $HOME/.svm @@ -95,21 +107,167 @@ OPENSSL_INCLUDE_DIR = "${pkgs.openssl.dev}/include"; PKG_CONFIG_PATH = "${pkgs.openssl.dev}/lib/pkgconfig:$PKG_CONFIG_PATH"; }; + meta.mainProgram = "arbi"; }; arbi_sample_config_kdl = pkgs.writeText "arbi-sample-config.kdl" '' endpoint "wss://eth-mainnet.g.alchemy.com/v2/" pairs_file "pairs.json" + concurrency 5 ''; - }; + + run-forge-tests = pkgs.writeShellScriptBin "run-forge-tests" '' + pushd "$FLAKE_ROOT/onchain" + forge test \ + --fork-url "wss://mainnet.infura.io/ws/v3/$ALCHEMY_KEY" \ + --via-ir \ + -vvv + popd + ''; + + run-vm = pkgs.writeShellScriptBin "run-vm" (lib.getExe flake.config.flake.nixosConfigurations.vm.config.system.build.vm); + } // lib.genAttrs [ "polygon-mainnet" ] (network: pkgs.writeShellScriptBin "deploy-${network}" '' + pushd "$FLAKE_ROOT/onchain" + forge create \ + --rpc-url "wss://${network}.infura.io/ws/v3/$ALCHEMY_KEY" \ + --private-key "$WALLET_PRIVATE_KEY" \ + --via-ir \ + --broadcast \ + src/ArbitrageManager.sol:ArbitrageManager + popd + ''); + checks = { inherit (config.packages) arbi; }; }; - flake.githubActions = inputs.nix-github-actions.lib.mkGithubMatrix { - checks = lib.getAttrs [ "x86_64-linux" ] config.flake.checks; + flake = { + githubActions = inputs.nix-github-actions.lib.mkGithubMatrix { + checks = lib.getAttrs [ "x86_64-linux" ] config.flake.checks; + }; + + nixosConfigurations.vm = withSystem "x86_64-linux" (ctx: inputs.nixpkgs.lib.nixosSystem { + system = "x86_64-linux"; + modules = [ + ({ pkgs, modulesPath, ... }: { + imports = [ + "${modulesPath}/virtualisation/qemu-vm.nix" + config.flake.nixosModules.arbi + ]; + + services.getty.autologinUser = "root"; + services.openssh.settings.PasswordAuthentication = lib.mkForce true; + services.openssh.settings.PermitRootLogin = lib.mkForce "yes"; + users.users.root.password = ""; + + virtualisation = { + graphics = false; + memorySize = 2048; + diskSize = 10000; + forwardPorts = [ + { + from = "host"; + host.port = 2222; + guest.port = 22; + } + ]; + }; + + system.stateVersion = "25.05"; + + services.arbi = { + enable = true; + log_level = "debug"; + configFile = pkgs.writeText "arbi-config.kdl" '' + endpoint "wss://eth-mainnet.g.alchemy.com/v2/kkDMaLVYpWQA0GsCYNFvAODnAxCCiamv" + pairs_file "pairs.json" + concurrency 5 + ''; + }; + }) + ]; + }); + + nixosModules = { + arbi = moduleWithSystem ({ config }: nixos@{ lib, utils, ... }: + let + cfg = nixos.config.services.arbi; + in + { + options.services.arbi = { + enable = lib.mkEnableOption "arbi"; + package = lib.mkOption { + type = lib.types.package; + default = config.packages.arbi; + }; + log_level = lib.mkOption { + type = lib.types.enum [ "debug" "trace" "warn" "error" "info" ]; + default = "info"; + }; + configFile = lib.mkOption { + type = lib.types.path; + }; + dataDir = lib.mkOption { + type = lib.types.path; + default = "/var/lib/arbi"; + }; + user = lib.mkOption { + type = lib.types.str; + default = "arbi"; + }; + group = lib.mkOption { + type = lib.types.str; + default = "arbi"; + }; + }; + config = lib.mkIf cfg.enable { + environment.systemPackages = [ cfg.package ]; + + users.users.arbi = lib.mkIf (cfg.user == "arbi") { + isSystemUser = true; + group = cfg.group; + }; + + users.groups.arbi = lib.mkIf (cfg.group == "arbi") { }; + + systemd.tmpfiles.settings."10-arbi" = { + ${cfg.dataDir}.d = { + inherit (cfg) user group; + mode = "0755"; + }; + }; + + systemd.services.arbi = { + description = "Arbitrage bot"; + + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + + environment.ARBI_LOG_LEVEL = cfg.log_level; + + serviceConfig = { + ExecStart = utils.escapeSystemdExecArgs [ + (lib.getExe cfg.package) + "--config" + cfg.configFile + "run" + ]; + + KillSignal = "SIGINT"; + Restart = "on-failure"; + RestartSec = "5s"; + User = cfg.user; + Group = cfg.group; + WorkingDirectory = cfg.dataDir; + UMask = "0022"; + }; + }; + }; + }); + default = config.flake.nixosModules.arbi; + }; }; }); } diff --git a/notes/notes.pdf b/notes/notes.pdf new file mode 100644 index 0000000..7384bf5 Binary files /dev/null and b/notes/notes.pdf differ diff --git a/notes/notes.typ b/notes/notes.typ new file mode 100644 index 0000000..d1afca4 --- /dev/null +++ b/notes/notes.typ @@ -0,0 +1,86 @@ + += Notes + +Miscellaneous notes about *arbi*. + +== Uniswap V2's optimal input amount + +We consider two Uniswap V2-like pairs $A$ and $B$ both relative to the same two tokens. +Let $X_A$ and $Y_A$ the reserves of the two tokens on the pair $A$ and $Y_A$ and $Y_B$ the reserves on the pair $B$ and assume that we want to perform 2 chained this way. + +$ + ... ->^y^* A ->^(x_"out") B ->^(y_"out") ... +$ + +with $y^*$ the optimum amount to swap in order to maximize the gain function $G(y) = y_"out" - y^*$ + +Let $0 <= f <= 1$ be the fee ($.03$ by deault on Uniswap V2), we know#footnote[https://www.youtube.com/watch?v=9EKksG-fF1k] that the optimum is one of the roots of the following second-grade equation: + +$ + k^2y^2 + 2k Y_A X_B y + (Y_A X_B)^2 - (1-f)^2 X_A Y_B Y_A X_B = 0 +$ + +where + +$ + k = (1-f)X_B + (1-f)^2 X_A +$ + +In the Uniswap V2 implementation we have that $1-f = phi/1000$ (with $phi = 997$). +Then we can rewrite: + +$ + k^2y^2 + 2k Y_A X_B y + (Y_A X_B)^2 - (phi/1000)^2 X_A Y_B Y_A X_B = 0 +$ + +and + +$ + k = phi/1000 X_B + phi^2/1000^2 X_A +$ + +Let $a$, $b$ and $c$ be the three second-grade equation coefficients. + +$ + a = k^2 +$ + +$ + b = 2k Y_A X_B +$ + +$ + c = (Y_A X_B)^2 - (phi/1000)^2 X_A Y_B Y_A X_B +$ + +Since $b$ is even we can find the roots with + +$ + y_i = (-b/2 plus.minus sqrt((b^2-4a c)/4))/a +$ + +Replacing our values: + +$ + (- k Y_A X_B y plus.minus sqrt(k^2 (Y_A X_B) ^2 ((Y_A X_B) ^2 -phi^2/1000^2 X_A Y_B X_B Y_A)))/k^2 +$ +$ + = -(Y_A X_B)/k plus.minus 1/k^2 sqrt(k^2 ((Y_A X_B)^2) -(Y_A X_B)^2 + phi^2/1000^2X_A Y_B X_B Y_A) +$ +$ + = -(Y_A X_B)/k plus.minus 1/k sqrt((phi^2 X_B Y_B X_B Y_A )/1000^2) +$ + +Which, since the square root is positive, can be positive only considering $+$. In conclusion we get the following formula for the optimal amount of token $Y$: + +$ + y^* = 1/k (sqrt((phi^2 X_A Y_B X_B Y_A) / 1000^2) - Y_A X_B) +$ + +=== Solidity implementation details + +- Integer square roots can be effectively and cheaply computed using the Babylonian method #footnote[https://ethereum.stackexchange.com/a/97540/66173] +- The square root can lead to overflow, in that case it can be convenient splitting it into something like +$ + sqrt(phi times X_A div 1000 times Y_B) sqrt(phi times X_B div 1000 times Y_A) +$ diff --git a/offchain/Cargo.lock b/offchain/Cargo.lock index d756d81..6345990 100644 --- a/offchain/Cargo.lock +++ b/offchain/Cargo.lock @@ -59,9 +59,9 @@ dependencies = [ [[package]] name = "alloy-chains" -version = "0.1.67" +version = "0.1.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2826a55d29df2765ec264190cd1c26dd1d6232ef811825024a25cdf5eaaadbdf" +checksum = "28e2652684758b0d9b389d248b209ed9fd9989ef489a550265fe4bb8454fe7eb" dependencies = [ "alloy-primitives", "num_enum", @@ -129,9 +129,9 @@ dependencies = [ [[package]] name = "alloy-core" -version = "0.8.24" +version = "0.8.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d23ccdb29eedfa1d83f32efbc958d0944e6928e252295dd5eafc516ed57f3a0a" +checksum = "9d8bcce99ad10fe02640cfaec1c6bc809b837c783c1d52906aa5af66e2a196f6" dependencies = [ "alloy-dyn-abi", "alloy-json-abi", @@ -142,9 +142,9 @@ dependencies = [ [[package]] name = "alloy-dyn-abi" -version = "0.8.24" +version = "0.8.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ada55b5ab26624766bb8c65f72516dee93eaf28d5d87fc18ff4324cd8c2a948d" +checksum = "eb8e762aefd39a397ff485bc86df673465c4ad3ec8819cc60833a8a3ba5cdc87" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -154,7 +154,7 @@ dependencies = [ "itoa", "serde", "serde_json", - "winnow 0.7.4", + "winnow 0.7.10", ] [[package]] @@ -230,9 +230,9 @@ dependencies = [ [[package]] name = "alloy-json-abi" -version = "0.8.24" +version = "0.8.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df4054f177d1600f17e2bc152f6a927592641b19861e6005cc51bdf7d4fa27a6" +checksum = "fe6beff64ad0aa6ad1019a3db26fef565aefeb011736150ab73ed3366c3cfd1b" dependencies = [ "alloy-primitives", "alloy-sol-type-parser", @@ -295,9 +295,9 @@ dependencies = [ [[package]] name = "alloy-primitives" -version = "0.8.24" +version = "0.8.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7283185baefbe66136649dc316c9dcc6f0e9f1d635ae19783615919f83bc298a" +checksum = "8c77490fe91a0ce933a1f219029521f20fc28c2c0ca95d53fa4da9c00b8d9d4e" dependencies = [ "alloy-rlp", "bytes", @@ -305,8 +305,8 @@ dependencies = [ "const-hex", "derive_more 2.0.1", "foldhash", - "hashbrown 0.15.2", - "indexmap 2.8.0", + "hashbrown 0.15.3", + "indexmap 2.9.0", "itoa", "k256", "keccak-asm", @@ -402,7 +402,7 @@ checksum = "a40e1ef334153322fd878d07e86af7a529bcb86b2439525920a88eba87bcf943" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -589,42 +589,42 @@ dependencies = [ [[package]] name = "alloy-sol-macro" -version = "0.8.24" +version = "0.8.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f99b007e002f1082b28827cc47d9c72562d412a98c06f29aa438118ff3036c43" +checksum = "e10ae8e9a91d328ae954c22542415303919aabe976fe7a92eb06db1b68fd59f2" dependencies = [ "alloy-sol-macro-expander", "alloy-sol-macro-input", "proc-macro-error2", "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] name = "alloy-sol-macro-expander" -version = "0.8.24" +version = "0.8.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c0a9cb9b1afbcd3325e0fff9fdf98e6d095643fae9e5584e80597f0b79b6d6e" +checksum = "83ad5da86c127751bc607c174d6c9fe9b85ef0889a9ca0c641735d77d4f98f26" dependencies = [ "alloy-json-abi", "alloy-sol-macro-input", "const-hex", "heck", - "indexmap 2.8.0", + "indexmap 2.9.0", "proc-macro-error2", "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", "syn-solidity", "tiny-keccak", ] [[package]] name = "alloy-sol-macro-input" -version = "0.8.24" +version = "0.8.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "530c4863e707b95f99b37792cdfa94d30004ec552aed41e200a1d9264d44e669" +checksum = "ba3d30f0d3f9ba3b7686f3ff1de9ee312647aac705604417a2f40c604f409a9e" dependencies = [ "alloy-json-abi", "const-hex", @@ -634,25 +634,25 @@ dependencies = [ "proc-macro2", "quote", "serde_json", - "syn 2.0.100", + "syn 2.0.101", "syn-solidity", ] [[package]] name = "alloy-sol-type-parser" -version = "0.8.24" +version = "0.8.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74b210dd863afa9da93c488601a1f23bee1e3ce47e15519582320c205645a7a0" +checksum = "6d162f8524adfdfb0e4bd0505c734c985f3e2474eb022af32eef0d52a4f3935c" dependencies = [ "serde", - "winnow 0.7.4", + "winnow 0.7.10", ] [[package]] name = "alloy-sol-types" -version = "0.8.24" +version = "0.8.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f5ff802859e2797d022dc812b5b4ee40d829e0fb446c269a87826c7f0021976" +checksum = "d43d5e60466a440230c07761aa67671d4719d46f43be8ea6e7ed334d8db4a9ab" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -826,6 +826,7 @@ dependencies = [ "env_logger", "eyre", "futures-util", + "itertools 0.14.0", "kdl", "log", "miette", @@ -986,7 +987,7 @@ checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -997,7 +998,7 @@ checksum = "e539d3fca749fcee5236ab05e93a52867dd549cc157c8cb7f99595f3cedffdb5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -1013,13 +1014,13 @@ dependencies = [ [[package]] name = "auto_impl" -version = "1.2.1" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e12882f59de5360c748c4cbf569a042d5fb0eb515f7bea9c1f470b47f6ffbd73" +checksum = "ffdcb70bdbc4d478427380519163274ac86e52916e10f0a8889adf0f96d3fee7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -1030,9 +1031,9 @@ checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" [[package]] name = "backtrace" -version = "0.3.74" +version = "0.3.75" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" +checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002" dependencies = [ "addr2line", "cfg-if", @@ -1174,9 +1175,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.17" +version = "1.2.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fcb57c740ae1daf453ae85f16e37396f672b039e00d9d866e07ddb24e328e3a" +checksum = "32db95edf998450acc7881c932f94cd9b05c87b4b2599e8bab064753da4acfd1" dependencies = [ "shlex", ] @@ -1189,9 +1190,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.40" +version = "0.4.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a7964611d71df112cb1730f2ee67324fcf4d0fc6606acbbe9bfe06df124637c" +checksum = "c469d952047f47f91b68d1cba3f10d63c11d73e4636f24f08daf0278abf01c4d" dependencies = [ "android-tzdata", "iana-time-zone", @@ -1202,9 +1203,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.32" +version = "4.5.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6088f3ae8c3608d19260cd7445411865a485688711b78b5be70d78cd96136f83" +checksum = "ed93b9805f8ba930df42c2590f05453d5ec36cbb85d018868a5b24d31f6ac000" dependencies = [ "clap_builder", "clap_derive", @@ -1212,9 +1213,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.32" +version = "4.5.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22a7ef7f676155edfb82daa97f99441f3ebf4a58d5e32f295a56259f1b6facc8" +checksum = "379026ff283facf611b0ea629334361c4211d1b12ee01024eec1591133b04120" dependencies = [ "anstream", "anstyle", @@ -1231,7 +1232,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -1312,9 +1313,9 @@ dependencies = [ [[package]] name = "crc" -version = "3.2.1" +version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69e6e4d7b33a94f0991c26729976b10ebde1d34c3ee82408fb536164fa10d636" +checksum = "9710d3b3739c2e349eb44fe848ad0b7c8cb1e42bd87ee49371df2f7acaf3e675" dependencies = [ "crc-catalog", ] @@ -1361,9 +1362,9 @@ dependencies = [ [[package]] name = "darling" -version = "0.20.10" +version = "0.20.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989" +checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee" dependencies = [ "darling_core", "darling_macro", @@ -1371,27 +1372,27 @@ dependencies = [ [[package]] name = "darling_core" -version = "0.20.10" +version = "0.20.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5" +checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e" dependencies = [ "fnv", "ident_case", "proc-macro2", "quote", "strsim", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] name = "darling_macro" -version = "0.20.10" +version = "0.20.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" +checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" dependencies = [ "darling_core", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -1410,15 +1411,15 @@ dependencies = [ [[package]] name = "data-encoding" -version = "2.8.0" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "575f75dfd25738df5b91b8e43e14d44bda14637a58fae779fd2b064f8bf3e010" +checksum = "2a2330da5de22e8a3cb63252ce2abb30116bf5265e89c0e01bc17015ce30a476" [[package]] name = "der" -version = "0.7.9" +version = "0.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0" +checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb" dependencies = [ "const-oid", "zeroize", @@ -1426,9 +1427,9 @@ dependencies = [ [[package]] name = "deranged" -version = "0.4.1" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28cfac68e08048ae1883171632c2aef3ebc555621ae56fbccce1cbf22dd7f058" +checksum = "9c9e6a11ca8224451684bc0d7d5a7adbf8f2fd6887261a1cfc3c0432f9d4068e" dependencies = [ "powerfmt", "serde", @@ -1471,7 +1472,7 @@ checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -1482,7 +1483,7 @@ checksum = "bda628edc44c4bb645fbe0f758797143e4e07926f7ebf4e9bdfbd3d2ce621df3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", "unicode-xid", ] @@ -1515,7 +1516,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -1586,9 +1587,9 @@ dependencies = [ [[package]] name = "env_logger" -version = "0.11.7" +version = "0.11.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3716d7a920fb4fac5d84e9d4bce8ceb321e9414b4409da61b07b75c1e3d0697" +checksum = "13c863f0904021b108aa8b2f55046443e6b1ebde8fd4a15c399893aae4fa069f" dependencies = [ "anstream", "anstyle", @@ -1605,9 +1606,9 @@ checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] name = "errno" -version = "0.3.10" +version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" +checksum = "976dd42dc7e85965fe702eb8164f21f450704bdde31faefd6471dba214cb594e" dependencies = [ "libc", "windows-sys 0.59.0", @@ -1771,7 +1772,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -1823,9 +1824,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" dependencies = [ "cfg-if", "libc", @@ -1834,9 +1835,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73fea8450eea4bac3940448fb7ae50d91f034f941199fcd9d909a5a07aa455f0" +checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" dependencies = [ "cfg-if", "libc", @@ -1881,9 +1882,9 @@ checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" [[package]] name = "hashbrown" -version = "0.15.2" +version = "0.15.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" +checksum = "84b26c544d002229e640969970a2e74021aadf6e2f96372b9c58eff97de08eb3" dependencies = [ "allocator-api2", "equivalent", @@ -1998,9 +1999,9 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.10" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df2dcfbe0677734ab2f3ffa7fa7bfd4706bfdc1ef393f2ee30184aed67e631b4" +checksum = "497bbc33a26fdd4af9ed9c70d63f61cf56a938375fbb32df34db9b1cd6d643f2" dependencies = [ "bytes", "futures-channel", @@ -2008,6 +2009,7 @@ dependencies = [ "http", "http-body", "hyper", + "libc", "pin-project-lite", "socket2", "tokio", @@ -2017,9 +2019,9 @@ dependencies = [ [[package]] name = "iana-time-zone" -version = "0.1.62" +version = "0.1.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2fd658b06e56721792c5df4475705b6cda790e9298d19d2f8af083457bcd127" +checksum = "b0c919e5debc312ad217002b8048a17b7d83f80703865bbfcfebb0458b0b27d8" dependencies = [ "android_system_properties", "core-foundation-sys", @@ -2041,21 +2043,22 @@ dependencies = [ [[package]] name = "icu_collections" -version = "1.5.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" +checksum = "200072f5d0e3614556f94a9930d5dc3e0662a652823904c3a75dc3b0af7fee47" dependencies = [ "displaydoc", + "potential_utf", "yoke", "zerofrom", "zerovec", ] [[package]] -name = "icu_locid" -version = "1.5.0" +name = "icu_locale_core" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" +checksum = "0cde2700ccaed3872079a65fb1a78f6c0a36c91570f28755dda67bc8f7d9f00a" dependencies = [ "displaydoc", "litemap", @@ -2064,31 +2067,11 @@ dependencies = [ "zerovec", ] -[[package]] -name = "icu_locid_transform" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" -dependencies = [ - "displaydoc", - "icu_locid", - "icu_locid_transform_data", - "icu_provider", - "tinystr", - "zerovec", -] - -[[package]] -name = "icu_locid_transform_data" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e" - [[package]] name = "icu_normalizer" -version = "1.5.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" +checksum = "436880e8e18df4d7bbc06d58432329d6458cc84531f7ac5f024e93deadb37979" dependencies = [ "displaydoc", "icu_collections", @@ -2096,67 +2079,54 @@ dependencies = [ "icu_properties", "icu_provider", "smallvec", - "utf16_iter", - "utf8_iter", - "write16", "zerovec", ] [[package]] name = "icu_normalizer_data" -version = "1.5.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516" +checksum = "00210d6893afc98edb752b664b8890f0ef174c8adbb8d0be9710fa66fbbf72d3" [[package]] name = "icu_properties" -version = "1.5.1" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" +checksum = "2549ca8c7241c82f59c80ba2a6f415d931c5b58d24fb8412caa1a1f02c49139a" dependencies = [ "displaydoc", "icu_collections", - "icu_locid_transform", + "icu_locale_core", "icu_properties_data", "icu_provider", - "tinystr", + "potential_utf", + "zerotrie", "zerovec", ] [[package]] name = "icu_properties_data" -version = "1.5.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569" +checksum = "8197e866e47b68f8f7d95249e172903bec06004b18b2937f1095d40a0c57de04" [[package]] name = "icu_provider" -version = "1.5.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" +checksum = "03c80da27b5f4187909049ee2d72f276f0d9f99a42c306bd0131ecfe04d8e5af" dependencies = [ "displaydoc", - "icu_locid", - "icu_provider_macros", + "icu_locale_core", "stable_deref_trait", "tinystr", "writeable", "yoke", "zerofrom", + "zerotrie", "zerovec", ] -[[package]] -name = "icu_provider_macros" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.100", -] - [[package]] name = "ident_case" version = "1.0.1" @@ -2176,9 +2146,9 @@ dependencies = [ [[package]] name = "idna_adapter" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" +checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" dependencies = [ "icu_normalizer", "icu_properties", @@ -2201,7 +2171,7 @@ checksum = "a0eb5a3343abf848c0984fe4604b2b105da9539376e24fc0a3b0007411ae4fd9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -2223,12 +2193,12 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.8.0" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3954d50fe15b02142bf25d3b8bdadb634ec3948f103d04ffe3031bc8fe9d7058" +checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e" dependencies = [ "equivalent", - "hashbrown 0.15.2", + "hashbrown 0.15.3", "serde", ] @@ -2291,9 +2261,9 @@ checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" [[package]] name = "jiff" -version = "0.2.5" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c102670231191d07d37a35af3eb77f1f0dbf7a71be51a962dcd57ea607be7260" +checksum = "f02000660d30638906021176af16b17498bd0d12813dbfe7b276d8bc7f3c0806" dependencies = [ "jiff-static", "log", @@ -2304,13 +2274,13 @@ dependencies = [ [[package]] name = "jiff-static" -version = "0.2.5" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cdde31a9d349f1b1f51a0b3714a5940ac022976f4b49485fc04be052b183b4c" +checksum = "f3c30758ddd7188629c6713fc45d1188af4f44c90582311d0c8d8c9907f60c48" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -2376,27 +2346,27 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.171" +version = "0.2.172" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6" +checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" [[package]] name = "libm" -version = "0.2.11" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8355be11b20d696c8f18f6cc018c4e372165b1fa8126cef092399c9951984ffa" +checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de" [[package]] name = "linux-raw-sys" -version = "0.9.3" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe7db12097d22ec582439daf8618b8fdd1a7bef6270e9af3b1ebcd30893cf413" +checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" [[package]] name = "litemap" -version = "0.7.5" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23fb14cb19457329c82206317a5663005a4d404783dc74f4252769b0d5f42856" +checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956" [[package]] name = "lock_api" @@ -2420,7 +2390,7 @@ version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "227748d55f2f0ab4735d87fd623798cb6b664512fe979705f829c9f81c934465" dependencies = [ - "hashbrown 0.15.2", + "hashbrown 0.15.3", ] [[package]] @@ -2431,7 +2401,7 @@ checksum = "1b27834086c65ec3f9387b096d66e99f221cf081c2b738042aa252bcd41204e3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -2442,9 +2412,9 @@ checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "miette" -version = "7.5.0" +version = "7.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a955165f87b37fd1862df2a59547ac542c77ef6d17c666f619d1ad22dd89484" +checksum = "5f98efec8807c63c752b5bd61f862c165c115b0a35685bdcfd9238c7aeb592b7" dependencies = [ "backtrace", "backtrace-ext", @@ -2456,19 +2426,18 @@ dependencies = [ "supports-unicode", "terminal_size", "textwrap", - "thiserror 1.0.69", "unicode-width 0.1.14", ] [[package]] name = "miette-derive" -version = "7.5.0" +version = "7.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf45bf44ab49be92fd1227a3be6fc6f617f1a337c06af54981048574d8783147" +checksum = "db5b29714e950dbb20d5e6f74f9dcec4edbcc1067bb7f8ed198c097b8c1a818b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -2479,9 +2448,9 @@ checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" [[package]] name = "miniz_oxide" -version = "0.8.5" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e3e04debbb59698c15bacbb6d93584a8c0ca9cc3213cb423d31f760d8843ce5" +checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a" dependencies = [ "adler2", ] @@ -2621,7 +2590,7 @@ checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -2648,15 +2617,15 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.21.1" +version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d75b0bedcc4fe52caa0e03d9f1151a323e4aa5e2d78ba3580400cd3c9e2bc4bc" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" [[package]] name = "openssl" -version = "0.10.71" +version = "0.10.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e14130c6a98cd258fdcb0fb6d744152343ff729cbfcb28c656a9d12b999fbcd" +checksum = "fedfea7d58a1f73118430a55da6a286e7b044961736ce96a16a17068ea25e5da" dependencies = [ "bitflags", "cfg-if", @@ -2675,7 +2644,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -2686,9 +2655,9 @@ checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" [[package]] name = "openssl-sys" -version = "0.9.106" +version = "0.9.108" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bb61ea9811cc39e3c2069f40b8b8e2e70d8569b361f879786cc7ed48b777cdd" +checksum = "e145e1651e858e820e4860f7b9c5e169bc1d8ce1c86043be79fa7b7634821847" dependencies = [ "cc", "libc", @@ -2727,7 +2696,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -2803,7 +2772,7 @@ checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -2849,6 +2818,15 @@ dependencies = [ "portable-atomic", ] +[[package]] +name = "potential_utf" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5a7c30837279ca13e7c867e9e40053bc68740f988cb07f7ca6df43cc734b585" +dependencies = [ + "zerovec", +] + [[package]] name = "powerfmt" version = "0.2.0" @@ -2903,14 +2881,14 @@ dependencies = [ "proc-macro-error-attr2", "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] name = "proc-macro2" -version = "1.0.94" +version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84" +checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" dependencies = [ "unicode-ident", ] @@ -2976,13 +2954,12 @@ dependencies = [ [[package]] name = "rand" -version = "0.9.0" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3779b94aeb87e8bd4e834cee3650289ee9e0d5677f976ecdb6d219e5f4f6cd94" +checksum = "9fbfd9d094a40bf3ae768db9361049ace4c0e04a4fd6b359518bd7b73a73dd97" dependencies = [ "rand_chacha 0.9.0", "rand_core 0.9.3", - "zerocopy", ] [[package]] @@ -3011,7 +2988,7 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom 0.2.15", + "getrandom 0.2.16", ] [[package]] @@ -3020,7 +2997,7 @@ version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" dependencies = [ - "getrandom 0.3.2", + "getrandom 0.3.3", ] [[package]] @@ -3040,9 +3017,9 @@ checksum = "d3edd4d5d42c92f0a659926464d4cce56b562761267ecf0f469d85b7de384175" [[package]] name = "redox_syscall" -version = "0.5.10" +version = "0.5.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b8c0c260b63a8219631167be35e6a988e9554dbd323f8bd08439c8ed1302bd1" +checksum = "928fca9cf2aa042393a8325b9ead81d2f0df4cb12e1e24cef072922ccd99c5af" dependencies = [ "bitflags", ] @@ -3134,7 +3111,7 @@ checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" dependencies = [ "cc", "cfg-if", - "getrandom 0.2.15", + "getrandom 0.2.16", "libc", "untrusted", "windows-sys 0.52.0", @@ -3169,7 +3146,7 @@ dependencies = [ "primitive-types", "proptest", "rand 0.8.5", - "rand 0.9.0", + "rand 0.9.1", "rlp", "ruint-macro", "serde", @@ -3221,9 +3198,9 @@ dependencies = [ [[package]] name = "rustix" -version = "1.0.3" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e56a18552996ac8d29ecc3b190b4fdbb2d91ca4ec396de7bbffaf43f3d637e96" +checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266" dependencies = [ "bitflags", "errno", @@ -3234,9 +3211,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.25" +version = "0.23.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "822ee9188ac4ec04a2f0531e55d035fb2de73f18b41a63c70c2712503b6fb13c" +checksum = "730944ca083c1c233a75c09f199e973ca499344a2b7ba9e755c457e86fb4a321" dependencies = [ "once_cell", "ring", @@ -3257,15 +3234,18 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.11.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "917ce264624a4b4db1c364dcc35bfca9ded014d0a958cd47ad3e960e988ea51c" +checksum = "229a4a4c221013e7e1f1a043678c5cc39fe5171437c88fb47151a21e6f5b5c79" +dependencies = [ + "zeroize", +] [[package]] name = "rustls-webpki" -version = "0.103.1" +version = "0.103.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fef8b8769aaccf73098557a87cd1816b4f9c7c16811c9c77142aa695c16f2c03" +checksum = "e4a72fe2bcf7a6ac6fd7d0b9e5cb68aeb7d4c0a0271730218b3e92d43b4eb435" dependencies = [ "ring", "rustls-pki-types", @@ -3396,7 +3376,7 @@ checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -3433,7 +3413,7 @@ dependencies = [ "chrono", "hex", "indexmap 1.9.3", - "indexmap 2.8.0", + "indexmap 2.9.0", "serde", "serde_derive", "serde_json", @@ -3450,7 +3430,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -3476,9 +3456,9 @@ dependencies = [ [[package]] name = "sha2" -version = "0.10.8" +version = "0.10.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" dependencies = [ "cfg-if", "cpufeatures", @@ -3513,9 +3493,9 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "signal-hook-registry" -version = "1.4.2" +version = "1.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" +checksum = "9203b8055f63a2a00e2f593bb0510367fe707d7ff1e5c872de2f537b339e5410" dependencies = [ "libc", ] @@ -3541,18 +3521,18 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.14.0" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcf8323ef1faaee30a44a340193b1ac6814fd9b7b4e88e9d4519a3e4abe1cfd" +checksum = "8917285742e9f3e1683f0a9c4e6b57960b7314d0b08d30d1ecd426713ee2eee9" dependencies = [ "serde", ] [[package]] name = "socket2" -version = "0.5.8" +version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8" +checksum = "4f5fd57c80058a56cf5c777ab8a126398ece8e442983605d280a44ce79d0edef" dependencies = [ "libc", "windows-sys 0.52.0", @@ -3605,7 +3585,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -3648,9 +3628,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.100" +version = "2.0.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0" +checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf" dependencies = [ "proc-macro2", "quote", @@ -3659,14 +3639,14 @@ dependencies = [ [[package]] name = "syn-solidity" -version = "0.8.24" +version = "0.8.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36dbbf0d465ab9fdfea3093e755ae8839bdc1263dbe18d35064d02d6060f949e" +checksum = "4560533fbd6914b94a8fb5cc803ed6801c3455668db3b810702c57612bac9412" dependencies = [ "paste", "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -3680,13 +3660,13 @@ dependencies = [ [[package]] name = "synstructure" -version = "0.13.1" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -3697,12 +3677,12 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "tempfile" -version = "3.19.1" +version = "3.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7437ac7763b9b123ccf33c338a5cc1bac6f69b45a136c19bdd8a65e3916435bf" +checksum = "e8a64e3985349f2441a1a9ef0b853f869006c3855f2cda6862a94d26ebb9d6a1" dependencies = [ "fastrand", - "getrandom 0.3.2", + "getrandom 0.3.3", "once_cell", "rustix", "windows-sys 0.59.0", @@ -3754,7 +3734,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -3765,7 +3745,7 @@ checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -3819,9 +3799,9 @@ dependencies = [ [[package]] name = "tinystr" -version = "0.7.6" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" +checksum = "5d4f6d1145dcb577acf783d4e601bc1d76a13337bb54e6233add580b07344c8b" dependencies = [ "displaydoc", "zerovec", @@ -3829,9 +3809,9 @@ dependencies = [ [[package]] name = "tokio" -version = "1.44.1" +version = "1.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f382da615b842244d4b8738c82ed1275e6c5dd90c459a30941cd07080b06c91a" +checksum = "2513ca694ef9ede0fb23fe71a4ee4107cb102b9dc1930f6d0fd77aae068ae165" dependencies = [ "backtrace", "bytes", @@ -3853,7 +3833,7 @@ checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -3901,14 +3881,14 @@ dependencies = [ "tokio", "tokio-rustls", "tungstenite", - "webpki-roots", + "webpki-roots 0.26.11", ] [[package]] name = "tokio-util" -version = "0.7.14" +version = "0.7.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b9590b93e6fcc1739458317cccd391ad3955e2bde8913edf6f95f9e65a8f034" +checksum = "66a539a9ad6d5d281510d5bd368c973d636c02dbf8a67300bfb6b950696ad7df" dependencies = [ "bytes", "futures-core", @@ -3919,19 +3899,19 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "0.6.8" +version = "0.6.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" +checksum = "3da5db5a963e24bc68be8b17b6fa82814bb22ee8660f192bb182771d498f09a3" [[package]] name = "toml_edit" -version = "0.22.24" +version = "0.22.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17b4795ff5edd201c7cd6dca065ae59972ce77d1b80fa0a84d94950ece7d1474" +checksum = "310068873db2c5b3e7659d2cc35d21855dbafa50d1ce336397c666e3cb08137e" dependencies = [ - "indexmap 2.8.0", + "indexmap 2.9.0", "toml_datetime", - "winnow 0.7.4", + "winnow 0.7.10", ] [[package]] @@ -3980,7 +3960,7 @@ checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -4021,7 +4001,7 @@ dependencies = [ "http", "httparse", "log", - "rand 0.9.0", + "rand 0.9.1", "rustls", "rustls-pki-types", "sha1", @@ -4112,12 +4092,6 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" -[[package]] -name = "utf16_iter" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" - [[package]] name = "utf8_iter" version = "1.0.4" @@ -4203,7 +4177,7 @@ dependencies = [ "log", "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", "wasm-bindgen-shared", ] @@ -4238,7 +4212,7 @@ checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -4278,9 +4252,18 @@ dependencies = [ [[package]] name = "webpki-roots" -version = "0.26.8" +version = "0.26.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2210b291f7ea53617fbafcc4939f10914214ec15aace5ba62293a668f322c5c9" +checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9" +dependencies = [ + "webpki-roots 1.0.0", +] + +[[package]] +name = "webpki-roots" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2853738d1cc4f2da3a225c18ec6c3721abb31961096e9dbf5ab35fa88b19cfdb" dependencies = [ "rustls-pki-types", ] @@ -4293,11 +4276,37 @@ checksum = "dd7cf3379ca1aac9eea11fba24fd7e315d621f8dfe35c8d7d2be8b793726e07d" [[package]] name = "windows-core" -version = "0.52.0" +version = "0.61.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +checksum = "4763c1de310c86d75a878046489e2e5ba02c649d185f21c67d4cf8a56d098980" dependencies = [ - "windows-targets 0.52.6", + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings 0.4.0", +] + +[[package]] +name = "windows-implement" +version = "0.60.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", +] + +[[package]] +name = "windows-interface" +version = "0.59.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", ] [[package]] @@ -4313,7 +4322,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4286ad90ddb45071efd1a66dfa43eb02dd0dfbae1545ad6cc3c51cf34d7e8ba3" dependencies = [ "windows-result", - "windows-strings", + "windows-strings 0.3.1", "windows-targets 0.53.0", ] @@ -4335,6 +4344,15 @@ dependencies = [ "windows-link", ] +[[package]] +name = "windows-strings" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a2ba9642430ee452d5a7aa78d72907ebe8cfda358e8cb7918a2050581322f97" +dependencies = [ + "windows-link", +] + [[package]] name = "windows-sys" version = "0.52.0" @@ -4492,9 +4510,9 @@ dependencies = [ [[package]] name = "winnow" -version = "0.7.4" +version = "0.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e97b544156e9bebe1a0ffbc03484fc1ffe3100cbce3ffb17eac35f7cdd7ab36" +checksum = "c06928c8748d81b05c9be96aad92e1b6ff01833332f281e8cfca3be4b35fc9ec" dependencies = [ "memchr", ] @@ -4508,17 +4526,11 @@ dependencies = [ "bitflags", ] -[[package]] -name = "write16" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" - [[package]] name = "writeable" -version = "0.5.5" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" +checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb" [[package]] name = "ws_stream_wasm" @@ -4550,9 +4562,9 @@ dependencies = [ [[package]] name = "yoke" -version = "0.7.5" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40" +checksum = "5f41bb01b8226ef4bfd589436a297c53d118f65921786300e427be8d487695cc" dependencies = [ "serde", "stable_deref_trait", @@ -4562,34 +4574,34 @@ dependencies = [ [[package]] name = "yoke-derive" -version = "0.7.5" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" +checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", "synstructure", ] [[package]] name = "zerocopy" -version = "0.8.24" +version = "0.8.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2586fea28e186957ef732a5f8b3be2da217d65c5969d4b1e17f973ebbe876879" +checksum = "a1702d9583232ddb9174e01bb7c15a2ab8fb1bc6f227aa1233858c351a3ba0cb" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.24" +version = "0.8.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a996a8f63c5c4448cd959ac1bab0aaa3306ccfd060472f85943ee0750f0169be" +checksum = "28a6e20d751156648aa063f3800b706ee209a32c0b4d9f24be3d980b01be55ef" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -4609,7 +4621,7 @@ checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", "synstructure", ] @@ -4630,14 +4642,25 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", +] + +[[package]] +name = "zerotrie" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36f0bbd478583f79edad978b407914f61b2972f5af6fa089686016be8f9af595" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", ] [[package]] name = "zerovec" -version = "0.10.4" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" +checksum = "4a05eb080e015ba39cc9e23bbe5e7fb04d5fb040350f99f34e338d5fdd294428" dependencies = [ "yoke", "zerofrom", @@ -4646,11 +4669,11 @@ dependencies = [ [[package]] name = "zerovec-derive" -version = "0.10.3" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" +checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] diff --git a/offchain/Cargo.toml b/offchain/Cargo.toml index d2c2685..d922c3a 100644 --- a/offchain/Cargo.toml +++ b/offchain/Cargo.toml @@ -9,6 +9,7 @@ clap = { version = "4.5.32", features = ["derive", "env"] } env_logger = "0.11.7" eyre = "0.6.12" futures-util = "0.3.31" +itertools = "0.14.0" kdl = "6.3.4" log = "0.4.27" miette = { version = "7.5.0", features = ["fancy"] } diff --git a/offchain/abi/ArbitrageManager.json b/offchain/abi/ArbitrageManager.json new file mode 120000 index 0000000..accb340 --- /dev/null +++ b/offchain/abi/ArbitrageManager.json @@ -0,0 +1 @@ +../../onchain/out/ArbitrageManager.sol/ArbitrageManager.json \ No newline at end of file diff --git a/offchain/src/pairs.rs b/offchain/src/pairs.rs index 09ab735..771bf6d 100644 --- a/offchain/src/pairs.rs +++ b/offchain/src/pairs.rs @@ -1,7 +1,9 @@ use std::{collections::HashMap, path::PathBuf, str::FromStr}; +use alloy::primitives::U256; use alloy::primitives::{aliases::U112, Address}; +use itertools::Itertools; use miette::{miette, Result}; use serde::de::{self, Visitor}; use serde::{Deserialize, Deserializer, Serialize}; @@ -10,11 +12,11 @@ use log::{debug, info}; #[derive(Debug, Serialize, Deserialize)] pub struct Pair { - token0: Address, - token1: Address, - reserve0: U112, - reserve1: U112, - factory: Address, + pub token0: Address, + pub token1: Address, + pub reserve0: U112, + pub reserve1: U112, + pub factory: Address, } #[derive(Debug, Eq, Hash, PartialEq)] @@ -68,6 +70,14 @@ impl<'de> Deserialize<'de> for AddressPair { } } +#[derive(Debug)] +pub struct ArbitrageOpportunity { + pair_a: Address, + pair_b: Address, + direction: bool, // true means token0 -> token1 -> token0 + optimum: U256, +} + #[derive(Debug, Serialize, Deserialize)] pub struct Pairs { pairs: HashMap, @@ -97,10 +107,17 @@ impl Pairs { } } - fn get(&self, address: Address) -> Option<&Pair> { + #[allow(dead_code)] + pub fn get(&self, address: Address) -> Option<&Pair> { self.pairs.get(&address) } + pub fn get_tokens(&self, address: Address) -> Option<(Address, Address)> { + self.pairs + .get(&address) + .map(|pair| (pair.token0, pair.token1)) + } + pub fn add( &mut self, pair: Address, @@ -133,11 +150,11 @@ impl Pairs { info!("First time seeing pair {}, adding it", { pair }); match self.by_tokens.get_mut(&AddressPair(token0, token1)) { - Some(tokens) => { - tokens.push(pair); + Some(pairs) => { + pairs.push(pair); info!( "Already know {} pairs with tokens {:?} and {:?}", - tokens.len(), + pairs.len(), token0, token1 ); @@ -161,6 +178,107 @@ impl Pairs { let data = serde_json::to_string(&self).map_err(|e| miette!(e))?; std::fs::write(filename, data).map_err(|e| miette!(e))?; + info!("{} Pairs saved to {:?}", self.pairs.len(), filename); + Ok(()) } + + pub fn len(&self) -> usize { + self.pairs.len() + } + + pub fn iter(&self) -> std::collections::hash_map::Iter<'_, Address, Pair> { + self.pairs.iter() + } + + pub fn get_addresses(&self) -> Vec
{ + self.pairs.keys().cloned().collect() + } + + pub fn get_reserves(&self, address: Address) -> Option<(U112, U112)> { + self.pairs + .get(&address) + .map(|pair| (pair.reserve0, pair.reserve1)) + } + + pub fn update_reserves( + &mut self, + address: Address, + reserve0: U112, + reserve1: U112, + ) -> Result<()> { + if let Some(pair) = self.pairs.get_mut(&address) { + pair.reserve0 = reserve0; + pair.reserve1 = reserve1; + info!( + "Updated reserves for pair {}: reserve0: {}, reserve1: {}", + address, reserve0, reserve1 + ); + Ok(()) + } else { + debug!("Pair {} not found", address); + Ok(()) // TODO return Err + } + } + + // TODO at the moment we return all the opportunities, instead we should return only the two opportunities + // (token0 -> token1 -> token0 and token1 -> token0 -> token1) with the highest amountIn + // Remember: choosing an opportunity invalidates the other ones + pub fn look_for_opportunity( + &self, + token0: Address, + token1: Address, + ) -> Vec { + let mut opportunities: Vec = Vec::new(); + if let Some(pairs) = self.by_tokens.get(&AddressPair(token0, token1)) { + pairs.iter() + .permutations(2) + .any(|pairs| { + let pair_a = self.get(*pairs[0]).unwrap(); + let pair_b = self.get(*pairs[1]).unwrap(); + + if let Some(optimum) = optimal_in(pair_a.reserve0, pair_a.reserve1, pair_b.reserve0, pair_b.reserve1) { + info!("Found arbitrage opportunity between pairs {} and {} swapping {} along token0 -> token1 -> token0", pairs[0], pairs[1], optimum); + opportunities.push(ArbitrageOpportunity{ + pair_a: *pairs[0], + pair_b: *pairs[1], + direction: true, + optimum + }); + } + if let Some(optimum) = optimal_in(pair_a.reserve1, pair_a.reserve0, pair_b.reserve1, pair_b.reserve0) { + info!("Found arbitrage opportunity between pairs {} and {} swapping {} along token1 -> token0 -> token1", pairs[0], pairs[1], optimum); + opportunities.push(ArbitrageOpportunity{ + pair_a: *pairs[0], + pair_b: *pairs[1], + direction: false, + optimum + }); + } + false + }); + } + opportunities + } +} + +fn optimal_in(x_a: U112, y_a: U112, x_b: U112, y_b: U112) -> Option { + let x_a = U256::from(x_a); + let x_b = U256::from(x_b); + let y_a = U256::from(y_a); + let y_b = U256::from(y_b); + let f = U256::from(997); + let ff = f.pow(U256::from(2)); + let _1000 = U256::from(1000); + let _1000000 = U256::from(1000000); + + let k = f * x_b / _1000 + ff / _1000 * x_a / _1000; + let phi = (ff * x_a * y_b * x_b * y_a / _1000000).root(2); + let psi = y_a * x_b; + + if psi >= phi { + None + } else { + Some((phi - psi) / k) + } } diff --git a/offchain/src/priority_queue.rs b/offchain/src/priority_queue.rs deleted file mode 100644 index 958eb8e..0000000 --- a/offchain/src/priority_queue.rs +++ /dev/null @@ -1,44 +0,0 @@ -mod pairs; - -use alloy::primitives::Address; -use futures_util::Stream; -use log::debug; -use std::{ - pin::Pin, - task::{Context, Poll}, -}; - -#[derive(Debug)] -pub enum Action { - ProcessPair(Address), -} - -pub struct PriorityQueue(pub Vec); - -impl PriorityQueue { - pub fn new() -> Self { - PriorityQueue(Vec::new()) - } - - pub fn push(&mut self, action: Action) { - debug!("Adding action {:?} to the priority queue", action); - self.0.push(action); - } -} - -impl Stream for PriorityQueue { - type Item = Action; - - fn poll_next(mut self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { - match self.0.pop() { - None => Poll::Ready(None), - Some(action) => { - debug!("Consuming action {:?} to the priority queue", action); - - match action { - Action::ProcessPair(pair) => Poll::Ready(Some(Action::ProcessPair(pair))), - } - } - } - } -} diff --git a/offchain/src/run.rs b/offchain/src/run.rs index d27199e..1e2a01f 100644 --- a/offchain/src/run.rs +++ b/offchain/src/run.rs @@ -1,30 +1,28 @@ -#[path = "pairs.rs"] -mod pairs; -#[path = "priority_queue.rs"] -mod priority_queue; - -use std::{ - sync::{Arc, Mutex}, - time::Duration, -}; +#![allow(clippy::too_many_arguments)] use crate::config::Config; use alloy::{ eips::BlockNumberOrTag, - primitives::Address, + primitives::{aliases::U112, Address, U256}, providers::{ - fillers::FillProvider, DynProvider, Provider, ProviderBuilder, RootProvider, WsConnect, + fillers::{BlobGasFiller, ChainIdFiller, FillProvider, GasFiller, JoinFill, NonceFiller}, + Provider, ProviderBuilder, RootProvider, WsConnect, }, - pubsub::PubSubFrontend, - rpc::types::Filter, + rpc::{client::RpcClient, types::Filter}, + transports::layers::RetryBackoffLayer, }; -use futures_util::{stream, StreamExt}; -use miette::{miette, Result}; - +use futures_util::StreamExt; use log::{debug, info}; +use miette::miette; +use std::{ + collections::HashSet, + sync::{Arc, Mutex}, + time::{Duration, Instant}, +}; +#[path = "pairs.rs"] +mod pairs; use pairs::Pairs; -use priority_queue::{Action, PriorityQueue}; -use tokio::time::sleep; +use tokio::sync::mpsc; alloy::sol!( #[allow(missing_docs)] @@ -33,14 +31,27 @@ alloy::sol!( "abi/IUniswapV2Pair.json" ); -async fn process_swaps( - ws: WsConnect, - priority_queue: Arc>, -) -> eyre::Result<()> { - let provider = ProviderBuilder::new().on_ws(ws).await.unwrap(); +#[derive(Debug)] +pub enum Action { + ProcessNewPair(Address), + ProcessOldPair(Address, U256, U256, U256, U256), +} +type AlloyProvider = FillProvider< + JoinFill< + alloy::providers::Identity, + JoinFill>>, + >, + RootProvider, +>; + +async fn subscribe( + provider: Arc, + pairs: Arc>, + tx: mpsc::Sender, +) -> eyre::Result<()> { let filter = Filter::new() - .event("Swap(address,uint256,uint256,uint256,uint256,address)") + .event("Swap(address,uint256,uint256,uint256,uint256,address)") // TODO manage also sync and skim .from_block(BlockNumberOrTag::Latest); let sub = provider.subscribe_logs(&filter).await?; @@ -55,61 +66,130 @@ async fn process_swaps( info!("Processing block number {:?}", block_number); } - priority_queue - .lock() - .unwrap() - .push(Action::ProcessPair(log.address())); + let IUniswapV2Pair::Swap { + sender: _, + amount0In, + amount1In, + amount0Out, + amount1Out, + to: _, + } = log.log_decode()?.inner.data; + + let pair_address = log.address(); + let pair_already_known = pairs.lock().unwrap().get(pair_address).is_some(); + + debug!("Event by pair {:?}", pair_address); + + if pair_already_known { + tx.send(Action::ProcessOldPair( + pair_address, + amount0In, + amount1In, + amount0Out, + amount1Out, + )) + .await?; + } else { + tx.send(Action::ProcessNewPair(pair_address)).await?; + } } Ok(()) } -async fn process_pair( - ws: WsConnect, +async fn process_new_pair( pairs: Arc>, pair_address: Address, + provider: Arc, ) -> eyre::Result<()> { - let provider = ProviderBuilder::new().on_ws(ws).await.unwrap(); + let result: eyre::Result<()> = async { + let pair = IUniswapV2Pair::new(pair_address, provider.clone()); // todo can avoid che clone? + let token0 = pair.token0().call().await?._0; + let token1 = pair.token1().call().await?._0; + let reserve0 = pair.getReserves().call().await?.reserve0; + let reserve1 = pair.getReserves().call().await?.reserve1; + let factory = pair.factory().call().await?._0; - let pair = IUniswapV2Pair::new(pair_address, provider); - let token0 = pair.token0().call().await?._0; - let token1 = pair.token1().call().await?._0; - let reserve0 = pair.getReserves().call().await?.reserve0; - let reserve1 = pair.getReserves().call().await?.reserve1; - let factory = pair.factory().call().await?._0; + pairs + .lock() + .unwrap() + .add(pair_address, token0, token1, reserve0, reserve1, factory); + Ok(()) + } + .await; + + if let Err(e) = &result { + eprintln!("error adding the new pair {}: {}", pair_address, e); + } + + result +} + +fn process_old_pair( + pairs: Arc>, + pair_address: Address, + amount0_in: U256, + amount1_in: U256, + amount0_out: U256, + amount1_out: U256, +) -> eyre::Result<()> { + let (reserve0, reserve1) = pairs.lock().unwrap().get_reserves(pair_address).unwrap(); pairs .lock() .unwrap() - .add(pair_address, token0, token1, reserve0, reserve1, factory); + .update_reserves( + pair_address, + reserve0 - U112::from(amount0_out) + U112::from(amount0_in), + reserve1 - U112::from(amount1_in) + U112::from(amount1_out), + ) + .unwrap(); // TODO manage error + Ok(()) // TODO manage errors +} + +async fn process_known_pairs( + pairs: Arc>, + provider: Arc, +) -> eyre::Result<()> { + let addresses = pairs.lock().unwrap().get_addresses(); + let len = addresses.len(); + + info!("Recovering state of {:?} saved pairs", len); + + for (i, address) in addresses.into_iter().enumerate() { + info!("Processing pair {}/{}: {:?}", i + 1, len, address); + + let result: eyre::Result<()> = async { + let pair = IUniswapV2Pair::new(address, provider.clone()); + let reserves = pair.getReserves().call().await?; + let reserve0 = reserves.reserve0; + let reserve1 = reserves.reserve1; + + let _ = pairs + .lock() + .unwrap() + .update_reserves(address, reserve0, reserve1); // TODO manage error, should be ok however + + Ok(()) + } + .await; + + if let Err(e) = &result { + eprintln!("Error processing pair {}: {}", address, e); + return result; + } + } Ok(()) } -async fn consume_priority_queue( - ws: WsConnect, - pairs: Arc>, - priority_queue: Arc>, - config: Config, -) { - let mut guard = priority_queue.lock().unwrap(); - let actions: Vec = guard.0.drain(..).collect(); //move all actions to temporary vector in order to unlock the mutex - drop(guard); //release before the expensive operation +fn look_for_opportunity(pairs: Arc>, involved_pairs: &HashSet
) { + let pairs = pairs.lock().unwrap(); - stream::iter(actions) - .for_each_concurrent(config.concurrency, |action| { - let pairs_clone = pairs.clone(); - let ws = ws.clone(); - async move { - match action { - Action::ProcessPair(pair_address) => { - info!("Processing pair: {:?}", pair_address); - process_pair(ws, pairs_clone, pair_address).await; - } - } - } - }) - .await; + for pair_address in involved_pairs { + let (token0, token1) = pairs.get_tokens(*pair_address).unwrap(); + let _opportunities = pairs.look_for_opportunity(token0, token1); + } } async fn manage_interruption(pairs: Arc>, config: Config) -> eyre::Result<()> { @@ -126,25 +206,93 @@ async fn manage_interruption(pairs: Arc>, config: Config) -> eyre:: std::process::exit(0); } -pub fn run(config: Config) -> Result<()> { - let runtime = tokio::runtime::Runtime::new().unwrap(); +pub fn run(config: Config) -> miette::Result<()> { + let runtime = tokio::runtime::Runtime::new().map_err(|e| miette!(e))?; let pairs = Arc::new(Mutex::new(Pairs::new(&config.pairs_file)?)); - let priority_queue = Arc::new(Mutex::new(PriorityQueue::new())); let ws = WsConnect::new(&config.endpoint); + let (tx, mut rx) = mpsc::channel::(5000); + runtime.block_on(async { tokio::spawn(manage_interruption(pairs.clone(), config.clone())); - // process all the `Swap` events adding actions to the queue - tokio::spawn(process_swaps(ws.clone(), priority_queue.clone())); + let retry_layer = RetryBackoffLayer::new(50, 500, 100); + let client = RpcClient::builder() + .layer(retry_layer) + .ws(ws) + .await + .map_err(|e| miette!(e))?; + let provider = Arc::new(ProviderBuilder::new().on_client(client)); + let signer: PrivateKeySigner = "".parse().unwrap(); + + info!("Subscribing to the events..."); + tokio::spawn(subscribe(provider.clone(), pairs.clone(), tx.clone())); + + info!("Processing known pairs..."); + process_known_pairs(pairs.clone(), provider.clone()) + .await + .map_err(|e| miette!(e))?; + info!("Finished processing known pairs..."); + + let mut queue_last_time_not_empty = Instant::now(); + let mut block_processed = false; + let mut involved_pairs: HashSet
= HashSet::new(); loop { - consume_priority_queue(ws.clone(), pairs.clone(), priority_queue.clone(), config.clone()).await; + let action = rx.try_recv(); - debug!("The entire queue has been processed, waiting 100ms before checking if new actions are available..."); - sleep(Duration::from_millis(100)).await; + if let Ok(action) = action { + queue_last_time_not_empty = Instant::now(); + block_processed = false; + let len = rx.len(); + + debug!( + "Processing action {:?}, {:?} actions left", + action, len + ); + + match action { + Action::ProcessNewPair(pair_address) => { + process_new_pair(pairs.clone(), pair_address, provider.clone()) + .await + .map_err(|e| miette!(e))?; + involved_pairs.insert(pair_address); + } + Action::ProcessOldPair( + pair_address, + amount0_in, + amount1_in, + amount0_out, + amount1_out, + ) => { + process_old_pair( + pairs.clone(), + pair_address, + amount0_in, + amount1_in, + amount0_out, + amount1_out, + ) + .map_err(|e| miette!(e))?; + involved_pairs.insert(pair_address); + } + } + } else { + if !block_processed && Instant::now().duration_since(queue_last_time_not_empty) > Duration::from_millis(50) { + info!("The actions queue has been empty for 100ms, we assume the entire block has been processed"); + info!("Involved pairs: {:?}", involved_pairs); + + look_for_opportunity(pairs.clone(), &involved_pairs); + + block_processed = true; + involved_pairs.clear(); + }; + + std::thread::sleep(Duration::from_millis(10)); + } } - }); - Ok(()) + #[allow(unreachable_code)] + Ok(()) + }) } diff --git a/onchain/src/ArbitrageManager.sol b/onchain/src/ArbitrageManager.sol index adafc7f..85aec93 100644 --- a/onchain/src/ArbitrageManager.sol +++ b/onchain/src/ArbitrageManager.sol @@ -3,8 +3,10 @@ pragma solidity ^0.8.28; import {IUniswapV2Pair} from "./IUniswapV2Pair.sol"; import {IERC20} from "./IERC20.sol"; +import {IUniswapV2Callee} from "./IUniswapV2Callee.sol"; -contract ArbitrageManager { + +contract ArbitrageManager is IUniswapV2Callee { uint256 constant f = 997; function sqrt(uint256 x) @@ -54,36 +56,59 @@ contract ArbitrageManager { returns(uint256) { uint256 k = f * X_B / 1000 + f ** 2 / 1000 * X_A / 1000; - uint256 phi = sqrt(f * X_A) * sqrt(Y_B * X_B / 1000 * Y_A); + uint256 phi = sqrt(f ** 2 * X_A * Y_B * X_B * Y_A / 1000**2); uint256 psi = Y_A * X_B; if (psi >= phi) return 0; else return (phi - psi) / k; } - function swap(address _pairA, address _pairB, uint256 amountIn, bool direction) - external - returns (uint256 amountOut) + function flashArbitrage(address firstPair, address secondPair, bool tokenDir) + public + returns (uint256 gain) { - IUniswapV2Pair pairA = IUniswapV2Pair(_pairA); - IUniswapV2Pair pairB = IUniswapV2Pair(_pairB); + IUniswapV2Pair pairA = IUniswapV2Pair(firstPair); + IUniswapV2Pair pairB = IUniswapV2Pair(secondPair); - IERC20 tokenA = direction ? IERC20(pairA.token0()) : IERC20(pairA.token1()); + IERC20 firstToken = tokenDir ? IERC20(pairA.token0()) : IERC20(pairA.token1()); + IERC20 secondToken = tokenDir ? IERC20(pairA.token1()) : IERC20(pairA.token0()); + + (uint256 X_A, uint256 Y_A,) = pairA.getReserves(); + (uint256 X_B, uint256 Y_B,) = pairB.getReserves(); - // Transfer the input tokens from the sender to pairA - tokenA.transferFrom(msg.sender, address(pairA), amountIn); + uint256 amountIn = optimalIn(tokenDir ? X_B : X_A, tokenDir ? Y_B : Y_A, tokenDir ? X_A : X_B, tokenDir ? Y_A : Y_B); + uint256 firstAmountOut = getAmountOut(amountIn, tokenDir ? X_A : Y_A, tokenDir ? Y_A : X_A); + uint256 secondAmountOut = getAmountOut(firstAmountOut, tokenDir ? Y_B : X_B, tokenDir ? X_B : Y_B); - // Perform the first swap on pairA - (uint256 reserve0A, uint256 reserve1A,) = pairA.getReserves(); - amountOut = getAmountOut(amountIn, direction ? reserve0A : reserve1A, direction ? reserve1A : reserve0A); - pairA.swap(direction ? 0 : amountOut, direction ? amountOut : 0, address(pairB), new bytes(0)); + require(secondAmountOut > amountIn, "Not profitable"); - // Perform the second swap on pairB - (uint256 reserve0B, uint256 reserve1B,) = pairB.getReserves(); - amountOut = getAmountOut(amountOut, direction ? reserve1B : reserve0B, direction ? reserve0B : reserve1B); - pairB.swap(direction ? amountOut : 0, direction ? 0 : amountOut, msg.sender, new bytes(0)); + bytes memory data = abi.encode(pairA, pairB, firstToken, secondToken, amountIn, secondAmountOut); + pairA.swap(tokenDir ? 0 : firstAmountOut, tokenDir ? firstAmountOut : 0, address(this), data); + uint256 profit = secondAmountOut - amountIn; + firstToken.transfer(msg.sender, profit); + + return profit; + } - // Ensure that the arbitrage is profitable - require(amountOut > amountIn, "Arbitrage not profitable"); + function uniswapV2Call(address sender, uint256 amount0, uint256 amount1, bytes memory data) + public + { + (address pairA, address pairB, address firstToken, address secondToken, uint256 amountIn, uint256 secondAmountOut) = abi.decode(data, (address, address, address, address, uint256, uint256)); + + bool tokenDir = amount0 == 0; + IERC20(secondToken).transfer(pairB, tokenDir ? amount1 : amount0); + IUniswapV2Pair(pairB).swap(tokenDir ? secondAmountOut : 0, tokenDir ? 0 : secondAmountOut, sender, new bytes(0)); + IERC20(firstToken).transfer(pairA, amountIn); + } + + fallback() external { + ( + address sender, + uint256 amount0, + uint256 amount1, + bytes memory data + ) = abi.decode(msg.data[4:], (address, uint256, uint256, bytes)); + + uniswapV2Call(sender, amount0, amount1, data); } } diff --git a/onchain/src/IUniswapV2Callee.sol b/onchain/src/IUniswapV2Callee.sol new file mode 100644 index 0000000..b21067d --- /dev/null +++ b/onchain/src/IUniswapV2Callee.sol @@ -0,0 +1,5 @@ +pragma solidity ^0.8.28; + +interface IUniswapV2Callee { + function uniswapV2Call(address sender, uint amount0, uint amount1, bytes calldata data) external; +} diff --git a/onchain/test/ArbitrageManager.t.sol b/onchain/test/ArbitrageManager.t.sol index 87268f3..b03cddb 100644 --- a/onchain/test/ArbitrageManager.t.sol +++ b/onchain/test/ArbitrageManager.t.sol @@ -16,7 +16,6 @@ contract ArbitrageTest is Test { IUniswapV2Pair sushiswapPair = IUniswapV2Pair(0x397FF1542f962076d0BFE58eA045FfA2d347ACa0); function setUp() public { - mainnetFork = vm.createFork("https://eth-mainnet.g.alchemy.com/v2/kkDMaLVYpWQA0GsCYNFvAODnAxCCiamv"); // TODO use an env variable vm.selectFork(mainnetFork); vm.rollFork(22_147_269); arbitrageManager = new ArbitrageManager(); @@ -34,12 +33,13 @@ contract ArbitrageTest is Test { n = 115792089237316195423570985008687907853269984665640564039457584007913129639935; assertEq(340282366920938463463374607431768211456 - 1, arbitrageManager.sqrt(n)); // +-1 is an acceptable rounding error } - - function test_computeAmountIn() public { + + function test_swapUsingOptimum() public { (uint256 X_A, uint256 Y_A, ) = uniswapPair.getReserves(); // (USDT, WETH) (uint256 X_B, uint256 Y_B, ) = sushiswapPair.getReserves(); // (USDT, WETH) console.log("Uniswap pair reserves", X_A, Y_A); + console.log("Sushiswap pair reserves", X_B, Y_B); console.log("Uniswap pair ratio", Y_A/X_A); console.log("Sushiswap pair ratio", Y_B/X_B); @@ -53,6 +53,7 @@ contract ArbitrageTest is Test { (X_A, Y_A, ) = uniswapPair.getReserves(); (X_B, Y_B, ) = sushiswapPair.getReserves(); console.log("Uniswap pair reserves", X_A, Y_A); + console.log("Sushiswap pair reserves", X_B, Y_B); console.log("Uniswap pair ratio", Y_A/X_A); console.log("Sushiswap pair ratio", Y_B/X_B); @@ -74,4 +75,47 @@ contract ArbitrageTest is Test { console.log("Uniswap pair ratio", Y_A/X_A); console.log("Sushiswap pair ratio", Y_B/X_B); } + + function computeGain(uint256 X_A, uint256 Y_A, uint256 X_B, uint256 Y_B, int256 delta) + internal view returns(uint256) + { + uint256 optimum = (delta > 0) ? + arbitrageManager.optimalIn(X_A, Y_A, X_B, Y_B) + uint256(delta) + : arbitrageManager.optimalIn(X_A, Y_A, X_B, Y_B) - uint256(-delta); + uint256 amountOut = arbitrageManager.getAmountOut(optimum, Y_A, X_A); + amountOut = arbitrageManager.getAmountOut(amountOut, X_B, Y_B); + return amountOut - optimum; + } + + function test_computeOptimum() public view { + (uint256 X_A, uint256 Y_A, ) = uniswapPair.getReserves(); // (USDT, WETH) + (uint256 X_B, uint256 Y_B, ) = sushiswapPair.getReserves(); // (USDT, WETH) + Y_A -= Y_A / 5; // unbalancing the pair + + // Using delta too low (~< 10**8) seems to produce a better gain, + // I *believe this has to do to some rounding, it should be neglibile + uint256[4] memory deltas = [uint256(0), uint256(10**8), uint256(10**9), uint256(10**10)]; + + uint256 gain = computeGain(X_A, Y_A, X_B, Y_B, 0); + + for (uint256 i; i < deltas.length; i++) { + assertGe(gain, computeGain(X_A, Y_A, X_B, Y_B, int256(deltas[i])), "Computed optimum isnt't really optimal"); + assertGe(gain, computeGain(X_A, Y_A, X_B, Y_B, -int256(deltas[i])), "Computed optimum isnt't really optimal"); + } + } + + function test_flashArbitrage () public { + uint256 initialWethBalance = weth.balanceOf(address(this)); + + console.log("initial weth balance", initialWethBalance); + (, uint256 Y_A, ) = uniswapPair.getReserves(); // (USDT, WETH) + uint256 unbalance = Y_A / 5; + vm.prank(address(uniswapPair)); // it works only for the next call + weth.transfer(address(0), unbalance); + uniswapPair.sync(); + + uint256 profit = arbitrageManager.flashArbitrage(address(uniswapPair), address(sushiswapPair), false); + console.log("profit", profit); + assertEq(initialWethBalance + profit, weth.balanceOf(address(this)), "There was no profit"); + } } diff --git a/secrets/alchemy_key.age b/secrets/alchemy_key.age new file mode 100644 index 0000000..cf74662 --- /dev/null +++ b/secrets/alchemy_key.age @@ -0,0 +1,5 @@ +age-encryption.org/v1 +-> ssh-ed25519 Zh7Kmw 6NFxuvVROgzHIvJPZqniuXinr9XMhNtt4hwW0do9Gio +g8FOQSOHN0xF7QV1fa9lkq62Fim+TQaWWLqGjppn2QE +--- /bcjNPkDej+yknSFozObJz/QAY4fzzVOm6V4iE5BBHc +¿4¬î| ¶>Þ,K8’ö¢hb< 'U¾œq× Zµb¬zEÂùÀ*OPSÏKôŸ“PqµqkÇ tpŸ¤zcë \ No newline at end of file diff --git a/secrets/secrets.nix b/secrets/secrets.nix new file mode 100644 index 0000000..8a2e8ec --- /dev/null +++ b/secrets/secrets.nix @@ -0,0 +1,7 @@ +let + aciceri = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIm9Sl/I+5G4g4f6iE4oCUJteP58v+wMIew9ZuLB+Gea"; +in +{ + "alchemy_key.age".publicKeys = [ aciceri ]; + "wallet_private_key.age".publicKeys = [ aciceri ]; +} diff --git a/secrets/wallet_private_key.age b/secrets/wallet_private_key.age new file mode 100644 index 0000000..e4ed16e --- /dev/null +++ b/secrets/wallet_private_key.age @@ -0,0 +1,12 @@ +-----BEGIN AGE ENCRYPTED FILE----- +YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IFpoN0ttdyB0VC96 +SmZNQjJpMlR3eXU2bzNmK1BYMk5ta3JpSDFCZGpmS2k4R3B6L1NFClJZS055amRO +UEJXL1IvUjN3Mnppbks5emxaUlpoRkhLUEZVRUhwKzFpRUkKLT4gfkwoQUlUJlot +Z3JlYXNlIHojVEdeIF1VSmlVIFxfYApnd1o4SitNK1NKR0dMaDBEUUk4QndKY3hB +YTFTUGtsL0JRWVIzM1lzbmhUUlpxdSs0d3RMd2NQU3Y2ZG9MdHNMCk1rcFFvYzBX +dnVmMjcrcnBFbHdVb0pNbjlObnNtRkx4ZDNYZkRSWWN3dnF3UkxIQ1ptSmJjSGN4 +d1BKZgotLS0gdHlBMHpGeGdqdElFUWJZVWVoc0x4MGEvc3lGMzhkUGFHTHlCbkNy +c2JIawp8pD+QIU4hfw8ySNWye098z1ZQSXn267JuzH1oE20GY0ubK2TDWfxUHNht +jBhdgTnVPqQmBX8N0wDeB16AWmC/YuPEz52zZzgZ85Hy61N7E9m5ZDOaBhb1VJpD +Pf9T0uo= +-----END AGE ENCRYPTED FILE-----