Start writing notes Notes on Uniswap V2's optimum Fix error in formula Test `computeAmountInt` using various deltas Add `concurrency` to the default configuration file Remove unused imports Correctly propagate error Allow dead code Make the priority queue a real FIFO Refactor: remove priority queue as stream and use channels Increase buffer size New `flashArbitrage` function Comment with some ideas Add pragma version Refactor: decrease the amount of calls Remove unused code Re-enable tests Remove comment Process known pairs when started Avoid re-allocating a new provider every time Ignore `nixos.qcow2` file created by the VM Add support for `aarch64-linux` Add NixOS module and VM configuration Add `itertools` Add arbitrage opportunity detection Implement `fallback` method for non standard callbacks Add more logs Fix sign error in optimum formula Add deployment scripts and `agenix-shell` secrets Bump cargo packages Fix typo Print out an error if processing a pair goes wrong Add `actionlint` to formatters Fix typo Add TODO comment Remove not relevant anymore comment Big refactor - process actions always in the correct order avoiding corner cases - avoid using semaphores New API key Add `age` to dev shell Used by Emacs' `agenix-mode` on my system Fix parametric deploy scripts Add `run-forge-tests` flake app Remove fork URL from Solidity source Remove `pairDir` argument Add link to `ArbitrageManager`'s ABI WIP
273 lines
8.9 KiB
Nix
273 lines
8.9 KiB
Nix
{
|
|
inputs = {
|
|
flake-parts.url = "github:hercules-ci/flake-parts";
|
|
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
|
treefmt-nix = {
|
|
url = "github:numtide/treefmt-nix";
|
|
inputs.nixpkgs.follows = "nixpkgs";
|
|
};
|
|
git-hooks = {
|
|
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";
|
|
inputs.nixpkgs.follows = "nixpkgs";
|
|
};
|
|
forge-std = {
|
|
flake = false;
|
|
url = "github:foundry-rs/forge-std/v1.9.6";
|
|
};
|
|
};
|
|
|
|
outputs = inputs:
|
|
inputs.flake-parts.lib.mkFlake { inherit inputs; } (flake@{ config, lib, moduleWithSystem, withSystem, ... }: {
|
|
systems = [ "x86_64-linux" "aarch64-linux" ];
|
|
|
|
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;
|
|
flakeCheck = true;
|
|
programs = {
|
|
nixpkgs-fmt.enable = true;
|
|
rustfmt.enable = true;
|
|
actionlint.enable = true;
|
|
};
|
|
};
|
|
|
|
pre-commit = {
|
|
check.enable = false;
|
|
settings.hooks = {
|
|
treefmt = {
|
|
enable = true;
|
|
package = config.treefmt.build.wrapper;
|
|
};
|
|
};
|
|
};
|
|
|
|
devShells.default = pkgs.mkShell {
|
|
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
|
|
|
|
# forge needs forge-std to work
|
|
mkdir -p $FLAKE_ROOT/onchain/lib/
|
|
ln -sf ${inputs.forge-std.outPath} $FLAKE_ROOT/onchain/lib/forge-std
|
|
|
|
if [ ! -f "$FLAKE_ROOT/offchain/config.kdl" ]; then \
|
|
cp ${config.packages.arbi_sample_config_kdl} $FLAKE_ROOT/offchain/config.kdl
|
|
fi
|
|
export ARBI_CONFIG="$FLAKE_ROOT/offchain/config.kdl"
|
|
|
|
${config.pre-commit.installationScript}
|
|
'';
|
|
env = {
|
|
OPENSSL_DIR = pkgs.openssl.dev;
|
|
OPENSSL_NO_VENDOR = true;
|
|
OPENSSL_LIB_DIR = "${pkgs.openssl.out}/lib";
|
|
OPENSSL_INCLUDE_DIR = "${pkgs.openssl.dev}/include";
|
|
PKG_CONFIG_PATH = "${pkgs.openssl.dev}/lib/pkgconfig:$PKG_CONFIG_PATH";
|
|
RUST_BACKTRACE = true;
|
|
ARBI_LOG_LEVEL = "debug";
|
|
};
|
|
};
|
|
|
|
packages = {
|
|
default = config.packages.arbi;
|
|
|
|
arbi = pkgs.rustPlatform.buildRustPackage {
|
|
pname = "arbi";
|
|
version = "0.1.0";
|
|
cargoLock.lockFile = ./offchain/Cargo.lock;
|
|
src = ./offchain;
|
|
env = {
|
|
OPENSSL_DIR = pkgs.openssl.dev;
|
|
OPENSSL_NO_VENDOR = true;
|
|
OPENSSL_LIB_DIR = "${pkgs.openssl.out}/lib";
|
|
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/<REDACTED>"
|
|
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;
|
|
};
|
|
|
|
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;
|
|
};
|
|
};
|
|
});
|
|
}
|