├── migrations ├── .keep └── 00000000000000_diesel_initial_setup │ ├── down.sql │ └── up.sql ├── book ├── architecture │ ├── actor.md │ └── state_management.md ├── images │ ├── .you-can-edit-svgs-with-draw-io │ └── kabu_logo.jpg ├── SUMMARY.md ├── tips_and_tricks │ ├── address_book.md │ └── custom_messages.md ├── js │ └── mermaid-init.js ├── tips_and_tricks.md └── introduction.md ├── .env.test.example ├── crates ├── types │ ├── blockchain │ │ ├── src │ │ │ ├── new_block.rs │ │ │ ├── fetchstate.rs │ │ │ ├── lib.rs │ │ │ ├── accountnoncetx.rs │ │ │ ├── chain_parameters.rs │ │ │ └── mempool_tx.rs │ │ └── Cargo.toml │ ├── events │ │ ├── src │ │ │ ├── tasks.rs │ │ │ ├── health_event.rs │ │ │ ├── lib.rs │ │ │ ├── defi_events.rs │ │ │ ├── message.rs │ │ │ └── node.rs │ │ └── Cargo.toml │ ├── entities │ │ ├── src │ │ │ ├── block_history │ │ │ │ └── mod.rs │ │ │ ├── private.rs │ │ │ ├── lib.rs │ │ │ ├── strategy_config.rs │ │ │ ├── datafetcher.rs │ │ │ └── account_nonce_balance.rs │ │ ├── Cargo.toml │ │ └── build.rs │ ├── swap │ │ ├── src │ │ │ ├── lib.rs │ │ │ ├── calculation_result.rs │ │ │ └── swap_encoder.rs │ │ └── Cargo.toml │ └── market │ │ ├── src │ │ ├── lib.rs │ │ ├── swap_direction.rs │ │ └── pool_config.rs │ │ └── Cargo.toml ├── defi │ ├── abi │ │ ├── src │ │ │ ├── balancer │ │ │ │ └── mod.rs │ │ │ ├── uniswap3 │ │ │ │ └── mod.rs │ │ │ ├── uniswap4 │ │ │ │ └── mod.rs │ │ │ ├── curve │ │ │ │ ├── mod.rs │ │ │ │ └── common.rs │ │ │ ├── uniswap2 │ │ │ │ ├── mod.rs │ │ │ │ └── router.rs │ │ │ ├── lido │ │ │ │ ├── mod.rs │ │ │ │ ├── wsteth.rs │ │ │ │ └── steth.rs │ │ │ ├── pancake │ │ │ │ ├── mod.rs │ │ │ │ ├── quoter.rs │ │ │ │ └── pool.rs │ │ │ ├── maverick │ │ │ │ ├── mod.rs │ │ │ │ └── quoter.rs │ │ │ ├── maverick2 │ │ │ │ └── mod.rs │ │ │ ├── uniswap_periphery │ │ │ │ ├── mod.rs │ │ │ │ ├── ticklens.rs │ │ │ │ ├── quoter.rs │ │ │ │ └── custorm_quoter.rs │ │ │ ├── lib.rs │ │ │ ├── weth.rs │ │ │ ├── erc20.rs │ │ │ ├── errors.rs │ │ │ └── multicaller.rs │ │ └── Cargo.toml │ ├── price │ │ ├── src │ │ │ └── lib.rs │ │ └── Cargo.toml │ ├── pools │ │ ├── src │ │ │ ├── db_reader │ │ │ │ └── mod.rs │ │ │ ├── virtual_impl │ │ │ │ ├── mod.rs │ │ │ │ └── tick_provider.rs │ │ │ ├── protocols │ │ │ │ ├── protocol.rs │ │ │ │ ├── mod.rs │ │ │ │ ├── sushiswap.rs │ │ │ │ ├── uniswapv2.rs │ │ │ │ └── uniswapv3.rs │ │ │ ├── errors │ │ │ │ └── mod.rs │ │ │ ├── state_readers │ │ │ │ ├── mod.rs │ │ │ │ └── erc20.rs │ │ │ └── lib.rs │ │ └── Cargo.toml │ ├── preloader │ │ ├── src │ │ │ └── lib.rs │ │ └── Cargo.toml │ ├── uniswap-v3-math │ │ ├── src │ │ │ ├── tick_provider.rs │ │ │ ├── unsafe_math.rs │ │ │ ├── tick.rs │ │ │ ├── error.rs │ │ │ ├── lib.rs │ │ │ └── bit_math.rs │ │ └── Cargo.toml │ ├── address-book │ │ └── Cargo.toml │ ├── health-monitor │ │ ├── src │ │ │ └── lib.rs │ │ └── Cargo.toml │ └── market │ │ ├── src │ │ ├── lib.rs │ │ └── logs_parser.rs │ │ └── Cargo.toml ├── rpc │ ├── state │ │ ├── src │ │ │ ├── lib.rs │ │ │ └── app_state.rs │ │ └── Cargo.toml │ └── handler │ │ ├── src │ │ ├── handler │ │ │ ├── mod.rs │ │ │ ├── blocks.rs │ │ │ └── ws.rs │ │ ├── dto │ │ │ ├── mod.rs │ │ │ ├── block.rs │ │ │ ├── pagination.rs │ │ │ ├── quote.rs │ │ │ └── flashbots.rs │ │ ├── lib.rs │ │ ├── openapi.rs │ │ └── router.rs │ │ └── Cargo.toml ├── metrics │ ├── src │ │ └── lib.rs │ └── Cargo.toml ├── node │ ├── exex │ │ ├── src │ │ │ ├── lib.rs │ │ │ └── reth_exex_worker.rs │ │ └── Cargo.toml │ ├── debug-provider │ │ ├── README.md │ │ ├── src │ │ │ ├── lib.rs │ │ │ └── cachefolder.rs │ │ └── Cargo.toml │ ├── json-rpc │ │ ├── src │ │ │ ├── lib.rs │ │ │ └── wait_for_node_sync_actor.rs │ │ └── Cargo.toml │ ├── node-config │ │ ├── Cargo.toml │ │ └── src │ │ │ └── lib.rs │ └── reth-api │ │ ├── Cargo.toml │ │ └── src │ │ └── traits.rs ├── broadcast │ ├── broadcaster │ │ ├── src │ │ │ └── lib.rs │ │ └── Cargo.toml │ └── accounts │ │ ├── src │ │ ├── accounts_monitor │ │ │ └── mod.rs │ │ ├── signers │ │ │ └── mod.rs │ │ └── lib.rs │ │ └── Cargo.toml ├── storage │ └── db │ │ ├── src │ │ ├── lib.rs │ │ └── pool.rs │ │ └── Cargo.toml ├── core │ ├── router │ │ ├── src │ │ │ └── lib.rs │ │ └── Cargo.toml │ ├── block-history │ │ ├── src │ │ │ └── lib.rs │ │ └── Cargo.toml │ ├── mempool │ │ ├── src │ │ │ ├── lib.rs │ │ │ └── mempool_builder.rs │ │ └── Cargo.toml │ ├── blockchain │ │ ├── src │ │ │ ├── lib.rs │ │ │ ├── app_state.rs │ │ │ ├── blockchain_state.rs │ │ │ └── blockchain_tokens.rs │ │ └── Cargo.toml │ ├── config │ │ ├── src │ │ │ ├── lib.rs │ │ │ └── config_loader.rs │ │ └── Cargo.toml │ ├── node │ │ ├── src │ │ │ ├── lib.rs │ │ │ └── traits.rs │ │ └── Cargo.toml │ └── components │ │ ├── Cargo.toml │ │ └── src │ │ ├── kabu_node.rs │ │ └── lib.rs ├── execution │ ├── estimator │ │ ├── src │ │ │ └── lib.rs │ │ └── Cargo.toml │ └── multicaller │ │ ├── src │ │ ├── pool_abi_encoder │ │ │ ├── pools │ │ │ │ ├── mod.rs │ │ │ │ └── curve.rs │ │ │ └── mod.rs │ │ └── lib.rs │ │ └── Cargo.toml ├── strategy │ ├── merger │ │ ├── src │ │ │ └── lib.rs │ │ └── Cargo.toml │ └── backrun │ │ ├── src │ │ ├── mod.rs │ │ ├── lib.rs │ │ ├── backrun_config.rs │ │ ├── swap_calculator.rs │ │ ├── affected_pools_logs.rs │ │ └── affected_pools_state.rs │ │ └── Cargo.toml └── evm │ ├── utils │ ├── src │ │ ├── error_handler.rs │ │ ├── lib.rs │ │ ├── geth_state_update.rs │ │ ├── remv_db_direct_access.rs │ │ └── nweth.rs │ └── Cargo.toml │ └── db │ ├── src │ ├── lib.rs │ ├── error.rs │ ├── database_loom.rs │ └── database_helpers.rs │ └── Cargo.toml ├── rust-toolchain.toml ├── .github └── assets │ └── kabu_logo.jpg ├── rustfmt.toml ├── .gitignore ├── diesel.toml ├── .pre-commit-config.yaml ├── book.toml ├── bin ├── kabu │ └── src │ │ └── arguments.rs └── keys │ └── Cargo.toml ├── taplo.toml ├── CONTRIBUTING.md ├── deny.toml ├── LICENSE-MIT ├── testing └── backtest-runner │ ├── test_21035613.toml │ ├── test_18567709.toml │ ├── test_21063544.toml │ ├── test_20927846.toml │ ├── test_19109955.toml │ ├── test_19101578.toml │ ├── test_20935488.toml │ ├── test_18498188.toml │ ├── src │ └── flashbots_mock.rs │ ├── Cargo.toml │ ├── README.md │ └── test_20937428.toml └── config.example.toml /migrations/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /book/architecture/actor.md: -------------------------------------------------------------------------------- 1 | # Actor 2 | -------------------------------------------------------------------------------- /book/images/.you-can-edit-svgs-with-draw-io: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.env.test.example: -------------------------------------------------------------------------------- 1 | MAINNET_WS= 2 | MAINNET_HTTP= -------------------------------------------------------------------------------- /crates/types/blockchain/src/new_block.rs: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /crates/defi/abi/src/balancer/mod.rs: -------------------------------------------------------------------------------- 1 | pub use vault::*; 2 | 3 | mod vault; 4 | -------------------------------------------------------------------------------- /crates/defi/abi/src/uniswap3/mod.rs: -------------------------------------------------------------------------------- 1 | pub use pool::*; 2 | 3 | mod pool; 4 | -------------------------------------------------------------------------------- /crates/defi/abi/src/uniswap4/mod.rs: -------------------------------------------------------------------------------- 1 | pub use pool::*; 2 | 3 | mod pool; 4 | -------------------------------------------------------------------------------- /rust-toolchain.toml: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | channel = "stable" 3 | profile = "default" 4 | -------------------------------------------------------------------------------- /crates/rpc/state/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub use app_state::AppState; 2 | 3 | mod app_state; 4 | -------------------------------------------------------------------------------- /book/images/kabu_logo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cakevm/kabu/HEAD/book/images/kabu_logo.jpg -------------------------------------------------------------------------------- /crates/defi/price/src/lib.rs: -------------------------------------------------------------------------------- 1 | mod price_actor; 2 | 3 | pub use price_actor::PriceComponent; 4 | -------------------------------------------------------------------------------- /.github/assets/kabu_logo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cakevm/kabu/HEAD/.github/assets/kabu_logo.jpg -------------------------------------------------------------------------------- /crates/defi/pools/src/db_reader/mod.rs: -------------------------------------------------------------------------------- 1 | pub use uniswapv3::UniswapV3DbReader; 2 | 3 | mod uniswapv3; 4 | -------------------------------------------------------------------------------- /crates/metrics/src/lib.rs: -------------------------------------------------------------------------------- 1 | mod influxdb_actor; 2 | 3 | pub use influxdb_actor::InfluxDbWriterComponent; 4 | -------------------------------------------------------------------------------- /crates/node/exex/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub use reth_exex_worker::mempool_worker; 2 | 3 | mod reth_exex_worker; 4 | -------------------------------------------------------------------------------- /crates/defi/abi/src/curve/mod.rs: -------------------------------------------------------------------------------- 1 | pub use common::*; 2 | pub use pools::*; 3 | 4 | mod common; 5 | mod pools; 6 | -------------------------------------------------------------------------------- /crates/defi/abi/src/uniswap2/mod.rs: -------------------------------------------------------------------------------- 1 | pub use pool::*; 2 | pub use router::*; 3 | 4 | mod pool; 5 | mod router; 6 | -------------------------------------------------------------------------------- /crates/rpc/handler/src/handler/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod blocks; 2 | pub mod flashbots; 3 | pub mod pools; 4 | pub mod ws; 5 | -------------------------------------------------------------------------------- /crates/defi/abi/src/lido/mod.rs: -------------------------------------------------------------------------------- 1 | pub use steth::IStEth; 2 | pub use wsteth::IWStEth; 3 | 4 | mod steth; 5 | mod wsteth; 6 | -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | max_width = 140 2 | reorder_imports = true 3 | use_field_init_shorthand = true 4 | use_small_heuristics = "Max" 5 | -------------------------------------------------------------------------------- /crates/broadcast/broadcaster/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub use flashbots::{FlashbotsBroadcastComponent, RelayConfig}; 2 | 3 | mod flashbots; 4 | -------------------------------------------------------------------------------- /crates/defi/pools/src/virtual_impl/mod.rs: -------------------------------------------------------------------------------- 1 | pub use uniswapv3::UniswapV3PoolVirtual; 2 | 3 | pub mod tick_provider; 4 | mod uniswapv3; 5 | -------------------------------------------------------------------------------- /crates/rpc/handler/src/dto/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod block; 2 | pub mod flashbots; 3 | pub mod pagination; 4 | pub mod pool; 5 | pub mod quote; 6 | -------------------------------------------------------------------------------- /crates/defi/abi/src/pancake/mod.rs: -------------------------------------------------------------------------------- 1 | pub use pool::IPancakeV3Pool; 2 | pub use quoter::IPancakeQuoterV2; 3 | 4 | mod pool; 5 | mod quoter; 6 | -------------------------------------------------------------------------------- /crates/storage/db/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub use pool::{DbPool, PoolError, check_and_run_migrations, init_db_pool_with_migrations}; 2 | 3 | mod pool; 4 | -------------------------------------------------------------------------------- /crates/core/router/src/lib.rs: -------------------------------------------------------------------------------- 1 | mod swap_router_component; 2 | 3 | pub use swap_router_component::{SwapRouterComponent, SwapRouterComponentBuilder}; 4 | -------------------------------------------------------------------------------- /crates/defi/abi/src/maverick/mod.rs: -------------------------------------------------------------------------------- 1 | pub use pool::{IMaverickPool, State}; 2 | pub use quoter::IMaverickQuoter; 3 | 4 | mod pool; 5 | mod quoter; 6 | -------------------------------------------------------------------------------- /crates/defi/preloader/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub use preloader_actor::{MarketStatePreloadedOneShotComponent, preload_market_state}; 2 | 3 | mod preloader_actor; 4 | -------------------------------------------------------------------------------- /crates/defi/abi/src/maverick2/mod.rs: -------------------------------------------------------------------------------- 1 | mod factory; 2 | mod pool; 3 | mod quoter; 4 | 5 | pub use factory::*; 6 | pub use pool::*; 7 | pub use quoter::*; 8 | -------------------------------------------------------------------------------- /crates/execution/estimator/src/lib.rs: -------------------------------------------------------------------------------- 1 | mod evm; 2 | mod hardhat; 3 | 4 | pub use evm::EvmEstimatorComponent; 5 | pub use hardhat::HardhatEstimatorActor; 6 | -------------------------------------------------------------------------------- /crates/core/block-history/src/lib.rs: -------------------------------------------------------------------------------- 1 | mod block_history_actor; 2 | mod block_history_actor_test; 3 | 4 | pub use block_history_actor::BlockHistoryComponent; 5 | -------------------------------------------------------------------------------- /crates/node/debug-provider/README.md: -------------------------------------------------------------------------------- 1 | # Debug provider 2 | 3 | ## AnvilDebugProvider 4 | 5 | ## DebugProvider 6 | 7 | ## HttpCachedTransport 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | .idea 3 | .vscode 4 | .cache 5 | *.iml 6 | crates/types/entities/src/private.rs 7 | config.toml 8 | *.bkp 9 | .claude 10 | .env.test 11 | .env 12 | -------------------------------------------------------------------------------- /crates/core/mempool/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub use mempool_builder::MempoolBuilder; 2 | pub use mempool_component::MempoolComponent; 3 | 4 | mod mempool_builder; 5 | mod mempool_component; 6 | -------------------------------------------------------------------------------- /crates/defi/uniswap-v3-math/src/tick_provider.rs: -------------------------------------------------------------------------------- 1 | use alloy::primitives::U256; 2 | 3 | pub trait TickProvider { 4 | fn get_tick(&self, tick: i16) -> eyre::Result; 5 | } 6 | -------------------------------------------------------------------------------- /crates/rpc/handler/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub use router::router; 2 | pub use web_actor::WebServerComponent; 3 | 4 | mod dto; 5 | mod handler; 6 | mod openapi; 7 | mod router; 8 | mod web_actor; 9 | -------------------------------------------------------------------------------- /crates/broadcast/accounts/src/accounts_monitor/mod.rs: -------------------------------------------------------------------------------- 1 | mod account_monitor_component; 2 | 3 | pub use account_monitor_component::{AccountMonitorComponent, AccountMonitorComponentBuilder}; 4 | -------------------------------------------------------------------------------- /crates/types/events/src/tasks.rs: -------------------------------------------------------------------------------- 1 | use kabu_types_market::{PoolClass, PoolId}; 2 | 3 | #[derive(Clone, Debug)] 4 | pub enum LoomTask { 5 | FetchAndAddPools(Vec<(PoolId, PoolClass)>), 6 | } 7 | -------------------------------------------------------------------------------- /crates/types/blockchain/src/fetchstate.rs: -------------------------------------------------------------------------------- 1 | use std::sync::{Arc, RwLock}; 2 | 3 | #[derive(Debug, Clone)] 4 | pub enum FetchState { 5 | Fetching(Arc>>), 6 | Ready(T), 7 | } 8 | -------------------------------------------------------------------------------- /crates/defi/abi/src/uniswap_periphery/mod.rs: -------------------------------------------------------------------------------- 1 | pub use custorm_quoter::ICustomQuoter; 2 | pub use quoter::IQuoterV2; 3 | pub use ticklens::ITickLens; 4 | 5 | mod custorm_quoter; 6 | mod quoter; 7 | mod ticklens; 8 | -------------------------------------------------------------------------------- /crates/defi/pools/src/protocols/protocol.rs: -------------------------------------------------------------------------------- 1 | use alloy::primitives::Address; 2 | 3 | #[allow(dead_code)] 4 | pub trait Protocol { 5 | fn get_pool_address_vec_for_tokens(token0: Address, token1: Address) -> Vec
; 6 | } 7 | -------------------------------------------------------------------------------- /crates/core/blockchain/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub use app_state::AppState; 2 | pub use blockchain::Blockchain; 3 | pub use blockchain_state::BlockchainState; 4 | 5 | mod app_state; 6 | mod blockchain; 7 | mod blockchain_state; 8 | mod blockchain_tokens; 9 | -------------------------------------------------------------------------------- /crates/types/entities/src/block_history/mod.rs: -------------------------------------------------------------------------------- 1 | pub use block_history_impl::{BlockHistory, BlockHistoryEntry, BlockHistoryManager}; 2 | pub use block_history_state::BlockHistoryState; 3 | 4 | mod block_history_impl; 5 | mod block_history_state; 6 | -------------------------------------------------------------------------------- /crates/broadcast/accounts/src/signers/mod.rs: -------------------------------------------------------------------------------- 1 | pub use initialize_actor::InitializeSignersOneShotBlockingComponent; 2 | pub use signers_component::{SignersComponent, SignersComponentBuilder}; 3 | 4 | mod initialize_actor; 5 | mod signers_component; 6 | -------------------------------------------------------------------------------- /crates/defi/pools/src/errors/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod uniswap_v2; 2 | pub mod uniswap_v3; 3 | pub mod curve; 4 | pub mod maverick; 5 | 6 | pub use uniswap_v2::UniswapV2Error; 7 | pub use uniswap_v3::UniswapV3Error; 8 | pub use curve::CurveError; 9 | pub use maverick::MaverickError; -------------------------------------------------------------------------------- /crates/node/json-rpc/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub use mempool_processing_component::{MempoolProcessingComponent, MempoolProcessingComponentBuilder}; 2 | pub use wait_for_node_sync_actor::WaitForNodeSyncOneShotBlockingActor; 3 | 4 | mod mempool_processing_component; 5 | mod wait_for_node_sync_actor; 6 | -------------------------------------------------------------------------------- /crates/broadcast/accounts/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub use crate::accounts_monitor::{AccountMonitorComponent, AccountMonitorComponentBuilder}; 2 | pub use crate::signers::{InitializeSignersOneShotBlockingComponent, SignersComponent, SignersComponentBuilder}; 3 | 4 | mod accounts_monitor; 5 | mod signers; 6 | -------------------------------------------------------------------------------- /crates/types/entities/src/private.rs: -------------------------------------------------------------------------------- 1 | // This file is auto-generated by build.rs 2 | // DO NOT EDIT MANUALLY 3 | // To change the key, set the KEY_ENCRYPTION_PWD environment variable (32 hex chars) 4 | 5 | pub const KEY_ENCRYPTION_PWD: [u8; 16] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; 6 | -------------------------------------------------------------------------------- /crates/strategy/merger/src/lib.rs: -------------------------------------------------------------------------------- 1 | mod diffpath_merger_actor; 2 | mod samepath_merger_actor; 3 | mod swappath_merger_actor; 4 | 5 | pub use diffpath_merger_actor::DiffPathMergerComponent; 6 | pub use samepath_merger_actor::SamePathMergerComponent; 7 | pub use swappath_merger_actor::ArbSwapPathMergerComponent; 8 | -------------------------------------------------------------------------------- /diesel.toml: -------------------------------------------------------------------------------- 1 | # For documentation on how to configure this file, 2 | # see https://diesel.rs/guides/configuring-diesel-cli 3 | 4 | [print_schema] 5 | file = "src/storage/db/schema.rs" 6 | custom_type_derives = ["diesel::query_builder::QueryId", "Clone"] 7 | 8 | [migrations_directory] 9 | dir = "migrations" 10 | -------------------------------------------------------------------------------- /crates/node/node-config/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "kabu-node-config" 3 | edition.workspace = true 4 | exclude.workspace = true 5 | homepage.workspace = true 6 | license.workspace = true 7 | repository.workspace = true 8 | rust-version.workspace = true 9 | version.workspace = true 10 | 11 | [dependencies] 12 | -------------------------------------------------------------------------------- /crates/defi/address-book/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "kabu-defi-address-book" 3 | version.workspace = true 4 | edition.workspace = true 5 | rust-version.workspace = true 6 | license.workspace = true 7 | homepage.workspace = true 8 | repository.workspace = true 9 | 10 | [dependencies] 11 | alloy-primitives.workspace = true 12 | -------------------------------------------------------------------------------- /crates/defi/uniswap-v3-math/src/unsafe_math.rs: -------------------------------------------------------------------------------- 1 | use alloy::primitives::U256; 2 | 3 | use crate::U256_1; 4 | 5 | pub fn div_rounding_up(a: U256, b: U256) -> U256 { 6 | let quotient = a.wrapping_div(b); 7 | let remainder = a.wrapping_rem(b); 8 | if remainder.is_zero() { quotient } else { quotient + U256_1 } 9 | } 10 | -------------------------------------------------------------------------------- /crates/core/config/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub use config_loader::{ConfigLoader, ConfigLoaderSync, LoadConfigError, load_from_file, load_from_file_sync}; 2 | pub use kabu_config::{ 3 | BuilderConfig, DatabaseConfig, InfluxDbConfig, KabuConfig, NodeType, RemoteNodeConfig, SignerConfig, WebserverConfig, 4 | }; 5 | 6 | mod config_loader; 7 | mod kabu_config; 8 | -------------------------------------------------------------------------------- /crates/evm/utils/src/error_handler.rs: -------------------------------------------------------------------------------- 1 | use axum::http::StatusCode; 2 | 3 | /// Utility function for mapping any error into a `500 Internal Server Error` 4 | /// response. 5 | pub fn internal_error(err: E) -> (StatusCode, String) 6 | where 7 | E: std::error::Error, 8 | { 9 | (StatusCode::INTERNAL_SERVER_ERROR, err.to_string()) 10 | } 11 | -------------------------------------------------------------------------------- /crates/evm/utils/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub use evm::*; 2 | pub use nweth::NWETH; 3 | pub use revm_balances::BalanceCheater; 4 | 5 | mod evm; 6 | pub mod evm_env; 7 | pub mod evm_tx_env; 8 | 9 | pub mod remv_db_direct_access; 10 | 11 | pub mod error_handler; 12 | pub mod geth_state_update; 13 | mod nweth; 14 | pub mod reth_types; 15 | mod revm_balances; 16 | -------------------------------------------------------------------------------- /crates/defi/pools/src/state_readers/mod.rs: -------------------------------------------------------------------------------- 1 | pub use erc20::ERC20StateReader; 2 | pub use uniswapv2::UniswapV2EVMStateReader; 3 | pub use uniswapv3::UniswapV3EvmStateReader; 4 | pub use uniswapv3_quoter::{UniswapV3QuoterV2Encoder, UniswapV3QuoterV2StateReader}; 5 | 6 | mod uniswapv2; 7 | mod uniswapv3; 8 | 9 | mod erc20; 10 | pub mod uniswapv3_quoter; 11 | -------------------------------------------------------------------------------- /crates/node/debug-provider/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub use anvilprovider::AnvilProviderExt; 2 | pub use debugprovider::{AnvilDebugProvider, AnvilDebugProviderFactory, AnvilDebugProviderType, DebugProviderExt}; 3 | pub use httpcached::HttpCachedTransport; 4 | 5 | mod anvilprovider; 6 | mod archiveprovider; 7 | mod cachefolder; 8 | mod debugprovider; 9 | mod httpcached; 10 | -------------------------------------------------------------------------------- /crates/defi/pools/src/protocols/mod.rs: -------------------------------------------------------------------------------- 1 | pub use curve::{CurveCommonContract, CurveContract, CurveProtocol}; 2 | pub use helper::*; 3 | pub use sushiswap::SushiswapProtocol; 4 | pub use uniswapv2::UniswapV2Protocol; 5 | pub use uniswapv3::UniswapV3Protocol; 6 | 7 | mod curve; 8 | mod helper; 9 | mod protocol; 10 | mod sushiswap; 11 | mod uniswapv2; 12 | mod uniswapv3; 13 | -------------------------------------------------------------------------------- /crates/defi/uniswap-v3-math/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "kabu-defi-uniswap-v3-math" 3 | version.workspace = true 4 | edition.workspace = true 5 | rust-version.workspace = true 6 | license.workspace = true 7 | homepage.workspace = true 8 | repository.workspace = true 9 | 10 | [dependencies] 11 | alloy.workspace = true 12 | eyre.workspace = true 13 | thiserror.workspace = true 14 | -------------------------------------------------------------------------------- /crates/types/swap/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod calculation_result; 2 | pub mod swap; 3 | pub mod swap_encoder; 4 | pub mod swap_error; 5 | pub mod swap_line; 6 | pub mod swap_step; 7 | pub mod tips; 8 | 9 | pub use calculation_result::*; 10 | pub use swap::*; 11 | pub use swap_encoder::*; 12 | pub use swap_error::*; 13 | pub use swap_line::*; 14 | pub use swap_step::*; 15 | pub use tips::*; 16 | -------------------------------------------------------------------------------- /crates/types/events/src/health_event.rs: -------------------------------------------------------------------------------- 1 | use crate::Message; 2 | use alloy_primitives::TxHash; 3 | use kabu_types_swap::{EstimationError, SwapError}; 4 | 5 | #[derive(Clone, Debug)] 6 | pub enum HealthEvent { 7 | PoolSwapError(SwapError), 8 | SwapLineEstimationError(EstimationError), 9 | MonitorTx(TxHash), 10 | } 11 | 12 | pub type MessageHealthEvent = Message; 13 | -------------------------------------------------------------------------------- /migrations/00000000000000_diesel_initial_setup/down.sql: -------------------------------------------------------------------------------- 1 | -- This file was automatically created by Diesel to setup helper functions 2 | -- and other internal bookkeeping. This file is safe to edit, any future 3 | -- changes will be added to existing projects as new migrations. 4 | 5 | DROP FUNCTION IF EXISTS diesel_manage_updated_at(_tbl regclass); 6 | DROP FUNCTION IF EXISTS diesel_set_updated_at(); 7 | -------------------------------------------------------------------------------- /crates/defi/health-monitor/src/lib.rs: -------------------------------------------------------------------------------- 1 | mod pool_health_monitor; 2 | mod state_health_monitor; 3 | mod stuffing_tx_monitor; 4 | 5 | mod metrics_recorder_actor; 6 | 7 | pub use metrics_recorder_actor::MetricsRecorderActor; 8 | pub use pool_health_monitor::PoolHealthMonitorComponent; 9 | pub use state_health_monitor::StateHealthMonitorActor; 10 | pub use stuffing_tx_monitor::StuffingTxMonitorActor; 11 | -------------------------------------------------------------------------------- /crates/execution/multicaller/src/pool_abi_encoder/pools/mod.rs: -------------------------------------------------------------------------------- 1 | pub use curve::CurveProtocolAbiEncoder; 2 | pub use maverick::MaverickProtocolAbiEncoder; 3 | pub use pancake3::PancakeV3ProtocolAbiEncoder; 4 | pub use uniswapv2::UniswapV2ProtocolAbiEncoder; 5 | pub use uniswapv3::UniswapV3ProtocolAbiEncoder; 6 | mod curve; 7 | mod maverick; 8 | mod pancake3; 9 | mod uniswapv2; 10 | mod uniswapv3; 11 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | # See https://pre-commit.com for more information 2 | repos: 3 | - repo: local 4 | hooks: 5 | - id: make-fmt 6 | name: make-fmt 7 | language: system 8 | entry: make fmt 9 | pass_filenames: false 10 | - id: make-taplo 11 | name: make-taplo 12 | language: system 13 | entry: make taplo 14 | pass_filenames: false -------------------------------------------------------------------------------- /book.toml: -------------------------------------------------------------------------------- 1 | [book] 2 | authors = ["Kabu Contributors"] 3 | language = "en" 4 | src = "book" 5 | title = "Kabu - MEV Bot Documentation" 6 | 7 | [build] 8 | build-dir = "target/book" 9 | 10 | [rust] 11 | edition = "2021" 12 | 13 | [preprocessor] 14 | [preprocessor.mermaid] 15 | command = "mdbook-mermaid" 16 | 17 | [output] 18 | [output.html] 19 | additional-js = ["book/js/mermaid.min.js", "book/js/mermaid-init.js"] 20 | -------------------------------------------------------------------------------- /crates/defi/uniswap-v3-math/src/tick.rs: -------------------------------------------------------------------------------- 1 | use alloy::primitives::U256; 2 | 3 | pub struct Tick { 4 | pub liquidity_gross: u128, 5 | pub liquidity_net: i128, 6 | pub fee_growth_outside_0_x_128: U256, 7 | pub fee_growth_outside_1_x_128: U256, 8 | pub tick_cumulative_outside: U256, 9 | pub seconds_per_liquidity_outside_x_128: U256, 10 | pub seconds_outside: u32, 11 | pub initialized: bool, 12 | } 13 | -------------------------------------------------------------------------------- /crates/evm/db/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub use alloydb::AlloyDB; 2 | pub use database_helpers::DatabaseHelpers; 3 | pub use database_loom::DatabaseKabuExt; 4 | pub use error::KabuDBError; 5 | pub use kabu_db::KabuDB; 6 | pub type KabuDBType = KabuDB; 7 | 8 | mod alloydb; 9 | mod database_helpers; 10 | mod database_loom; 11 | mod error; 12 | pub mod fast_cache_db; 13 | pub mod fast_hasher; 14 | mod in_memory_db; 15 | mod kabu_db; 16 | mod kabu_db_helper; 17 | -------------------------------------------------------------------------------- /crates/core/node/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Kabu MEV Bot Node Implementation 2 | 3 | pub mod builder; 4 | pub mod context; 5 | pub mod defaults; 6 | pub mod node; 7 | pub mod signer; 8 | pub mod traits; 9 | 10 | pub use builder::{KabuHandle, NodeBuilder}; 11 | pub use context::{KabuContext, NodeConfig}; 12 | pub use defaults::*; 13 | pub use node::{ComponentSet, ComponentSetBuilder, KabuEthereumNode}; 14 | pub use traits::{ComponentBuilder, DisabledComponent}; 15 | -------------------------------------------------------------------------------- /crates/rpc/state/src/app_state.rs: -------------------------------------------------------------------------------- 1 | use kabu_core_blockchain::{Blockchain, BlockchainState}; 2 | use kabu_storage_db::DbPool; 3 | use reth_ethereum_primitives::EthPrimitives; 4 | use revm::{DatabaseCommit, DatabaseRef}; 5 | 6 | #[derive(Clone)] 7 | pub struct AppState { 8 | pub db: DbPool, 9 | pub bc: Blockchain, 10 | pub state: BlockchainState, 11 | } 12 | -------------------------------------------------------------------------------- /crates/rpc/state/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "kabu-rpc-state" 3 | edition.workspace = true 4 | exclude.workspace = true 5 | homepage.workspace = true 6 | license.workspace = true 7 | repository.workspace = true 8 | rust-version.workspace = true 9 | version.workspace = true 10 | 11 | [dependencies] 12 | kabu-core-blockchain.workspace = true 13 | kabu-storage-db.workspace = true 14 | reth-ethereum-primitives.workspace = true 15 | revm.workspace = true 16 | -------------------------------------------------------------------------------- /crates/defi/abi/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "kabu-defi-abi" 3 | version.workspace = true 4 | edition.workspace = true 5 | rust-version.workspace = true 6 | license.workspace = true 7 | homepage.workspace = true 8 | repository.workspace = true 9 | 10 | [dependencies] 11 | alloy.workspace = true 12 | 13 | #alloy-contract.workspace = true 14 | #alloy-primitives.workspace = true 15 | #alloy-sol-macro.workspace = true 16 | #alloy-sol-types.workspace = true 17 | -------------------------------------------------------------------------------- /crates/rpc/handler/src/dto/block.rs: -------------------------------------------------------------------------------- 1 | use serde::Serialize; 2 | use utoipa::ToSchema; 3 | 4 | #[derive(Debug, Serialize)] 5 | #[serde(tag = "type", rename_all = "SCREAMING_SNAKE_CASE")] 6 | pub enum WebSocketMessage { 7 | BlockHeader(BlockHeader), 8 | } 9 | 10 | #[derive(Debug, Serialize, ToSchema)] 11 | pub struct BlockHeader { 12 | pub number: u64, 13 | pub timestamp: u64, 14 | pub base_fee_per_gas: Option, 15 | pub next_block_base_fee: u64, 16 | } 17 | -------------------------------------------------------------------------------- /crates/defi/abi/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub use abi_helpers::AbiEncoderHelper; 2 | pub use erc20::IERC20; 3 | pub use multicaller::IMultiCaller; 4 | pub use weth::IWETH; 5 | 6 | mod abi_helpers; 7 | 8 | pub mod balancer; 9 | pub mod curve; 10 | mod erc20; 11 | pub mod lido; 12 | pub mod maverick; 13 | pub mod multicaller; 14 | pub mod uniswap2; 15 | pub mod uniswap3; 16 | pub mod uniswap4; 17 | pub mod uniswap_periphery; 18 | mod weth; 19 | 20 | pub mod maverick2; 21 | 22 | pub mod pancake; 23 | -------------------------------------------------------------------------------- /crates/metrics/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "kabu-metrics" 3 | version.workspace = true 4 | edition.workspace = true 5 | rust-version.workspace = true 6 | license.workspace = true 7 | homepage.workspace = true 8 | repository.workspace = true 9 | 10 | [dependencies] 11 | eyre.workspace = true 12 | influxdb.workspace = true 13 | kabu-core-components = { workspace = true } 14 | tokio.workspace = true 15 | tracing.workspace = true 16 | 17 | # reth 18 | reth-tasks.workspace = true 19 | -------------------------------------------------------------------------------- /crates/types/events/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub use best_tx_compose::*; 2 | pub use defi_events::*; 3 | pub use health_event::*; 4 | pub use message::Message; 5 | pub use node::*; 6 | pub use state_update_event::*; 7 | pub use swap_compose::*; 8 | pub use tasks::LoomTask; 9 | pub use tx_compose::*; 10 | 11 | mod best_tx_compose; 12 | mod defi_events; 13 | mod health_event; 14 | mod message; 15 | mod node; 16 | mod swap_compose; 17 | 18 | mod state_update_event; 19 | mod tasks; 20 | mod tx_compose; 21 | -------------------------------------------------------------------------------- /book/SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Summary 2 | 3 | - [Introduction](introduction.md) 4 | - [Getting started](getting_started.md) 5 | - [Examples](examples.md) 6 | - [Architecture](architecture.md) 7 | - [Node System](architecture/node_system.md) 8 | - [Components](architecture/components.md) 9 | - [State management](architecture/state_management.md) 10 | - [Tips & tricks](tips_and_tricks.md) 11 | - [Custom messages](tips_and_tricks/custom_messages.md) 12 | - [Address book](tips_and_tricks/address_book.md) 13 | -------------------------------------------------------------------------------- /bin/kabu/src/arguments.rs: -------------------------------------------------------------------------------- 1 | use clap::{Parser, Subcommand}; 2 | 3 | #[derive(Debug, Subcommand)] 4 | pub enum Command { 5 | Node(KabuArgs), 6 | Remote(KabuArgs), 7 | } 8 | 9 | #[derive(Parser, Debug)] 10 | #[command(name="Kabu", version, about, long_about = None)] 11 | pub struct AppArgs { 12 | #[command(subcommand)] 13 | pub command: Command, 14 | } 15 | 16 | #[derive(Parser, Debug)] 17 | pub struct KabuArgs { 18 | #[arg(long, default_value = "config.toml")] 19 | pub kabu_config: String, 20 | } 21 | -------------------------------------------------------------------------------- /bin/keys/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "keys" 3 | version.workspace = true 4 | edition.workspace = true 5 | rust-version.workspace = true 6 | license.workspace = true 7 | homepage.workspace = true 8 | repository.workspace = true 9 | exclude.workspace = true 10 | 11 | [dependencies] 12 | kabu-types-entities.workspace = true 13 | 14 | aes-gcm.workspace = true 15 | clap.workspace = true 16 | eyre.workspace = true 17 | hex.workspace = true 18 | pbkdf2.workspace = true 19 | rand.workspace = true 20 | sha2.workspace = true 21 | -------------------------------------------------------------------------------- /crates/defi/abi/src/uniswap_periphery/ticklens.rs: -------------------------------------------------------------------------------- 1 | use alloy::sol; 2 | 3 | sol! { 4 | #[derive(Debug, PartialEq, Eq)] 5 | interface ITickLens { 6 | struct PopulatedTick { 7 | int24 tick; 8 | int128 liquidityNet; 9 | uint128 liquidityGross; 10 | } 11 | 12 | function getPopulatedTicksInWord(address pool, int16 tickBitmapIndex) 13 | external 14 | view 15 | returns (PopulatedTick[] memory populatedTicks); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /crates/storage/db/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "kabu-storage-db" 3 | edition.workspace = true 4 | exclude.workspace = true 5 | homepage.workspace = true 6 | license.workspace = true 7 | repository.workspace = true 8 | rust-version.workspace = true 9 | version.workspace = true 10 | 11 | [dependencies] 12 | bb8.workspace = true 13 | diesel.workspace = true 14 | diesel-async.workspace = true 15 | diesel_migrations = { version = "2.2", features = ["postgres"] } 16 | thiserror.workspace = true 17 | tracing.workspace = true 18 | -------------------------------------------------------------------------------- /crates/evm/db/src/error.rs: -------------------------------------------------------------------------------- 1 | use revm::database::DBErrorMarker; 2 | use std::error::Error; 3 | use std::fmt::{Debug, Display, Formatter}; 4 | 5 | #[derive(Clone, Default, Debug)] 6 | pub enum KabuDBError { 7 | #[default] 8 | Nonimportant, 9 | TransportError, 10 | NoDB, 11 | DatabaseError(String), 12 | } 13 | 14 | impl DBErrorMarker for KabuDBError {} 15 | 16 | impl Display for KabuDBError { 17 | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { 18 | write!(f, "{self:?}") 19 | } 20 | } 21 | 22 | impl Error for KabuDBError {} 23 | -------------------------------------------------------------------------------- /crates/defi/abi/src/lido/wsteth.rs: -------------------------------------------------------------------------------- 1 | use alloy::sol; 2 | 3 | sol! { 4 | #[derive(Debug, PartialEq, Eq)] 5 | interface IWStEth { 6 | function getWstETHByStETH(uint256 stETHAmount) external view returns (uint256); 7 | function getStETHByWstETH(uint256 wstETHAmount) external view returns (uint256); 8 | function stEthPerToken() returns (uint256); 9 | function tokensPerStEth() returns (uint256); 10 | function wrap(uint256 stETHAmount) returns (uint256); 11 | function unwrap(uint256 wstETHAmount) returns (uint256); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /crates/defi/pools/src/lib.rs: -------------------------------------------------------------------------------- 1 | extern crate core; 2 | 3 | pub use curvepool::{CurvePool, CurvePoolAbiEncoder}; 4 | pub use kabu_types_market::PoolsLoadingConfig; 5 | pub use loaders::*; 6 | pub use maverickpool::MaverickPool; 7 | pub use pancakev3pool::PancakeV3Pool; 8 | pub use uniswapv2pool::UniswapV2Pool; 9 | pub use uniswapv3pool::{Slot0, UniswapV3Pool}; 10 | 11 | pub mod db_reader; 12 | mod maverickpool; 13 | pub mod state_readers; 14 | mod uniswapv2pool; 15 | mod uniswapv3pool; 16 | 17 | mod curvepool; 18 | pub mod protocols; 19 | 20 | mod loaders; 21 | mod pancakev3pool; 22 | mod virtual_impl; 23 | -------------------------------------------------------------------------------- /book/tips_and_tricks/address_book.md: -------------------------------------------------------------------------------- 1 | # Address book 2 | The address book contain ofter used addresses to have a convenient way to access them. It is less error-prone and easier to read. 3 | 4 | ## Address types 5 | Right now you will find `TokenAddress`, `FactoryAddress`, `PeripheryAddress` and other more specific address clusters for different protocols like `UniswapV2PoolAddress`. 6 | 7 | ## Example 8 | Just import is using the `kabu` or the dedicated `defi-address-book` crate. 9 | 10 | ```rust,ignore 11 | use kabu::eth::address_book::TokenAddress; 12 | 13 | let weth_address = TokenAddress::WETH; 14 | ``` -------------------------------------------------------------------------------- /crates/types/swap/src/calculation_result.rs: -------------------------------------------------------------------------------- 1 | use alloy_primitives::U256; 2 | use std::fmt::{Display, Formatter}; 3 | 4 | #[derive(Debug, Clone)] 5 | pub struct CalculationResult { 6 | pub amount_in: U256, 7 | pub amount_out: U256, 8 | } 9 | 10 | impl CalculationResult { 11 | pub fn new(amount_in: U256, amount_out: U256) -> Self { 12 | Self { amount_in, amount_out } 13 | } 14 | } 15 | 16 | impl Display for CalculationResult { 17 | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { 18 | write!(f, "(amount_in={}, amount_out={})", self.amount_in, self.amount_out) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /crates/node/exex/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "kabu-node-exex" 3 | edition.workspace = true 4 | exclude.workspace = true 5 | homepage.workspace = true 6 | license.workspace = true 7 | repository.workspace = true 8 | rust-version.workspace = true 9 | version.workspace = true 10 | 11 | [dependencies] 12 | kabu-core-blockchain.workspace = true 13 | kabu-types-blockchain.workspace = true 14 | kabu-types-events.workspace = true 15 | 16 | eyre.workspace = true 17 | tokio.workspace = true 18 | tracing.workspace = true 19 | 20 | # reth 21 | reth-ethereum-primitives.workspace = true 22 | reth-transaction-pool.workspace = true 23 | -------------------------------------------------------------------------------- /crates/core/components/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "kabu-core-components" 3 | version.workspace = true 4 | edition.workspace = true 5 | rust-version.workspace = true 6 | license.workspace = true 7 | homepage.workspace = true 8 | repository.workspace = true 9 | 10 | [dependencies] 11 | kabu-evm-db = { workspace = true } 12 | kabu-types-entities = { workspace = true } 13 | kabu-types-events = { workspace = true } 14 | 15 | eyre = { workspace = true } 16 | tokio = { workspace = true } 17 | tracing = { workspace = true } 18 | 19 | # reth 20 | reth-ethereum-primitives = { workspace = true } 21 | reth-tasks = { workspace = true } 22 | -------------------------------------------------------------------------------- /crates/defi/abi/src/uniswap2/router.rs: -------------------------------------------------------------------------------- 1 | use alloy::sol; 2 | 3 | sol! { 4 | 5 | #[sol(abi=true,rpc)] 6 | #[derive(Debug, PartialEq, Eq)] 7 | interface IUniswapV2Router { 8 | function getAmountOut(uint amountIn, uint reserveIn, uint reserveOut) 9 | public 10 | pure 11 | virtual 12 | override 13 | returns (uint amountOut); 14 | 15 | function getAmountIn(uint amountOut, uint reserveIn, uint reserveOut) 16 | public 17 | pure 18 | virtual 19 | override 20 | returns (uint amountIn); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /crates/rpc/handler/src/dto/pagination.rs: -------------------------------------------------------------------------------- 1 | use serde::Deserialize; 2 | use utoipa::IntoParams; 3 | 4 | const fn _page_default() -> usize { 5 | 1 6 | } 7 | const fn _limit_default() -> usize { 8 | 20 9 | } 10 | 11 | #[derive(Debug, Deserialize, IntoParams)] 12 | pub struct Pagination { 13 | #[serde(default = "_page_default")] 14 | pub page: usize, 15 | #[serde(default = "_limit_default")] 16 | pub limit: usize, 17 | } 18 | 19 | impl Pagination { 20 | pub fn start(&self) -> usize { 21 | if self.page == 0 { 22 | return 0; 23 | } 24 | (self.page - 1) * self.limit 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /crates/defi/market/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub use history_pool_loader_component::{HistoryPoolLoaderComponent, HistoryPoolLoaderComponentBuilder}; 2 | pub use new_pool_actor::NewPoolLoaderComponent; 3 | pub use pool_loader_actor::{PoolLoaderActor, fetch_and_add_pool_by_pool_id, fetch_state_and_add_pool}; 4 | pub use protocol_pool_loader_component::{ProtocolPoolLoaderComponent, ProtocolPoolLoaderComponentBuilder}; 5 | pub use required_pools_actor::RequiredPoolLoaderActor; 6 | 7 | mod history_pool_loader_component; 8 | mod logs_parser; 9 | mod new_pool_actor; 10 | mod pool_loader_actor; 11 | mod protocol_pool_loader_component; 12 | mod required_pools_actor; 13 | -------------------------------------------------------------------------------- /crates/evm/db/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "kabu-evm-db" 3 | version.workspace = true 4 | edition.workspace = true 5 | rust-version.workspace = true 6 | license.workspace = true 7 | homepage.workspace = true 8 | repository.workspace = true 9 | 10 | [dependencies] 11 | alloy.workspace = true 12 | eyre.workspace = true 13 | rand.workspace = true 14 | revm.workspace = true 15 | tokio.workspace = true 16 | tracing.workspace = true 17 | 18 | 19 | [dev-dependencies] 20 | criterion.workspace = true 21 | dotenvy.workspace = true 22 | tracing.workspace = true 23 | url.workspace = true 24 | 25 | [[bench]] 26 | harness = false 27 | name = "benchmark" 28 | -------------------------------------------------------------------------------- /crates/types/entities/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![allow(unused_assignments, unused_variables, dead_code, unused_must_use)] 2 | 3 | extern crate core; 4 | 5 | pub use account_nonce_balance::{AccountNonceAndBalanceState, AccountNonceAndBalances}; 6 | pub use block_history::{BlockHistory, BlockHistoryEntry, BlockHistoryManager, BlockHistoryState}; 7 | pub use datafetcher::{DataFetcher, FetchState}; 8 | pub use keystore::KeyStore; 9 | pub use signers::{LoomTxSigner, TxSignerEth, TxSigners}; 10 | 11 | mod block_history; 12 | 13 | pub mod account_nonce_balance; 14 | pub mod private; 15 | pub mod strategy_config; 16 | 17 | mod datafetcher; 18 | mod keystore; 19 | mod signers; 20 | -------------------------------------------------------------------------------- /crates/strategy/backrun/src/mod.rs: -------------------------------------------------------------------------------- 1 | pub use arb_actor::StateChangeArbActor; 2 | pub use backrun_config::{BackrunConfig, BackrunConfigSection}; 3 | pub use block_state_change_processor::BlockStateChangeProcessorActor; 4 | pub use pending_tx_state_change_processor::PendingTxStateChangeProcessorActor; 5 | pub use state_change_arb_searcher::StateChangeArbSearcherActor; 6 | pub use swap_calculator::SwapCalculator; 7 | 8 | mod block_state_change_processor; 9 | mod pending_tx_state_change_processor; 10 | mod state_change_arb_searcher; 11 | 12 | mod affected_pools_code; 13 | mod affected_pools_state; 14 | mod arb_actor; 15 | mod backrun_config; 16 | mod swap_calculator; 17 | -------------------------------------------------------------------------------- /crates/core/router/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "kabu-core-router" 3 | edition.workspace = true 4 | exclude.workspace = true 5 | homepage.workspace = true 6 | license.workspace = true 7 | repository.workspace = true 8 | rust-version.workspace = true 9 | version.workspace = true 10 | 11 | [dependencies] 12 | kabu-core-components.workspace = true 13 | kabu-types-entities.workspace = true 14 | kabu-types-events.workspace = true 15 | 16 | eyre.workspace = true 17 | tokio.workspace = true 18 | tracing.workspace = true 19 | 20 | revm.workspace = true 21 | 22 | # reth 23 | reth-ethereum-primitives.workspace = true 24 | reth-node-types.workspace = true 25 | reth-tasks.workspace = true 26 | -------------------------------------------------------------------------------- /crates/execution/multicaller/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code)] 2 | pub use deploy::{DEFAULT_VIRTUAL_ADDRESS, MulticallerDeployer}; 3 | pub use multicaller_encoder::MulticallerEncoder; 4 | pub use multicaller_encoder::MulticallerSwapEncoder; 5 | pub use opcodes_encoder::{OpcodesEncoder, OpcodesEncoderV2}; 6 | pub use pool_abi_encoder::ProtocolABIEncoderV2; 7 | pub use swapline_encoder::SwapLineEncoder; 8 | pub use swapstep_encoder::SwapStepEncoder; 9 | 10 | mod deploy; 11 | mod multicaller_encoder; 12 | mod opcodes_encoder; 13 | mod opcodes_helpers; 14 | pub mod pool_abi_encoder; 15 | pub mod pool_opcodes_encoder; 16 | mod swap_encoder; 17 | mod swapline_encoder; 18 | mod swapstep_encoder; 19 | -------------------------------------------------------------------------------- /crates/types/market/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod market; 2 | pub mod market_state; 3 | pub mod mock_pool; 4 | pub mod pool; 5 | pub mod pool_config; 6 | pub mod pool_error; 7 | pub mod pool_id; 8 | pub mod pool_loader; 9 | pub mod required_state; 10 | pub mod swap_direction; 11 | pub mod swap_path; 12 | pub mod swap_path_builder; 13 | pub mod token; 14 | 15 | pub use market::*; 16 | pub use market_state::*; 17 | pub use mock_pool::*; 18 | pub use pool::*; 19 | pub use pool_config::*; 20 | pub use pool_error::*; 21 | pub use pool_id::*; 22 | pub use pool_loader::*; 23 | pub use required_state::*; 24 | pub use swap_direction::*; 25 | pub use swap_path::*; 26 | pub use swap_path_builder::*; 27 | pub use token::*; 28 | -------------------------------------------------------------------------------- /crates/strategy/backrun/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub use arb_actor::StateChangeArbComponent; 2 | pub use backrun_config::{BackrunConfig, BackrunConfigSection}; 3 | pub use block_state_change_processor::BlockStateChangeProcessorComponent; 4 | pub use pending_tx_state_change_processor::PendingTxStateChangeProcessorComponent; 5 | pub use state_change_arb_searcher::StateChangeArbSearcherComponent; 6 | pub use swap_calculator::SwapCalculator; 7 | 8 | mod block_state_change_processor; 9 | mod pending_tx_state_change_processor; 10 | mod state_change_arb_searcher; 11 | 12 | mod affected_pools_code; 13 | mod affected_pools_logs; 14 | mod affected_pools_state; 15 | mod arb_actor; 16 | mod backrun_config; 17 | mod swap_calculator; 18 | -------------------------------------------------------------------------------- /crates/defi/abi/src/weth.rs: -------------------------------------------------------------------------------- 1 | use alloy::sol; 2 | 3 | sol! { 4 | 5 | #[sol(abi = true, rpc)] 6 | #[derive(Debug, PartialEq, Eq)] 7 | interface IWETH { 8 | event Approval(address indexed src, address indexed guy, uint256 wad); 9 | event Transfer(address indexed src, address indexed dst, uint256 wad); 10 | event Deposit(address indexed dst, uint256 wad); 11 | event Withdrawal(address indexed src, uint256 wad); 12 | 13 | function deposit() public payable; 14 | function withdraw(uint wad) public; 15 | function totalSupply() public view returns (uint); 16 | function balanceOf(address account) external view returns (uint256); 17 | } 18 | 19 | 20 | } 21 | -------------------------------------------------------------------------------- /crates/types/blockchain/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub use accountnoncetx::AccountNonceAndTransactions; 2 | pub use chain_parameters::ChainParameters; 3 | pub use fetchstate::FetchState; 4 | pub use mempool::Mempool; 5 | pub use mempool_tx::MempoolTx; 6 | pub use opcodes::*; 7 | pub use state_update::{ 8 | GethStateUpdate, GethStateUpdateVec, TRACING_CALL_OPTS, TRACING_OPTS, debug_log_geth_state_update, debug_trace_block, 9 | debug_trace_call_diff, debug_trace_call_post_state, debug_trace_call_pre_state, debug_trace_transaction, get_touched_addresses, 10 | }; 11 | mod accountnoncetx; 12 | mod chain_parameters; 13 | mod fetchstate; 14 | mod mempool; 15 | mod mempool_tx; 16 | mod new_block; 17 | mod opcodes; 18 | mod state_update; 19 | -------------------------------------------------------------------------------- /crates/types/swap/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "kabu-types-swap" 3 | version.workspace = true 4 | edition.workspace = true 5 | rust-version.workspace = true 6 | license.workspace = true 7 | homepage.workspace = true 8 | repository.workspace = true 9 | 10 | [dependencies] 11 | kabu-evm-db.workspace = true 12 | kabu-evm-utils.workspace = true 13 | kabu-types-market.workspace = true 14 | 15 | eyre.workspace = true 16 | lazy_static.workspace = true 17 | rand.workspace = true 18 | tracing.workspace = true 19 | 20 | alloy-primitives.workspace = true 21 | alloy-sol-types.workspace = true 22 | 23 | alloy-evm.workspace = true 24 | revm.workspace = true 25 | 26 | [dev-dependencies] 27 | kabu-defi-address-book.workspace = true 28 | -------------------------------------------------------------------------------- /crates/core/config/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "kabu-core-config" 3 | version.workspace = true 4 | edition.workspace = true 5 | rust-version.workspace = true 6 | license.workspace = true 7 | homepage.workspace = true 8 | repository.workspace = true 9 | 10 | [dependencies] 11 | # Core dependencies 12 | alloy-primitives = { workspace = true, features = ["serde"] } 13 | eyre.workspace = true 14 | serde = { workspace = true, features = ["derive"] } 15 | toml.workspace = true 16 | 17 | # For config loading with env var support 18 | async-trait.workspace = true 19 | dotenvy = "0.15" 20 | regex = "1.10" 21 | thiserror.workspace = true 22 | tokio.workspace = true 23 | 24 | # For display derive 25 | strum_macros.workspace = true 26 | -------------------------------------------------------------------------------- /crates/core/mempool/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "kabu-core-mempool" 3 | edition.workspace = true 4 | exclude.workspace = true 5 | homepage.workspace = true 6 | license.workspace = true 7 | repository.workspace = true 8 | rust-version.workspace = true 9 | version.workspace = true 10 | 11 | [dependencies] 12 | eyre.workspace = true 13 | influxdb.workspace = true 14 | kabu-core-blockchain.workspace = true 15 | kabu-core-components = { path = "../components" } 16 | kabu-types-blockchain.workspace = true 17 | kabu-types-events.workspace = true 18 | tokio.workspace = true 19 | tracing.workspace = true 20 | 21 | 22 | # reth 23 | reth-ethereum-primitives.workspace = true 24 | reth-node-types.workspace = true 25 | reth-tasks.workspace = true 26 | -------------------------------------------------------------------------------- /crates/node/json-rpc/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "kabu-node-json-rpc" 3 | edition.workspace = true 4 | exclude.workspace = true 5 | homepage.workspace = true 6 | license.workspace = true 7 | repository.workspace = true 8 | rust-version.workspace = true 9 | version.workspace = true 10 | 11 | [dependencies] 12 | kabu-core-components = { workspace = true } 13 | kabu-node-debug-provider.workspace = true 14 | kabu-types-events.workspace = true 15 | 16 | eyre.workspace = true 17 | reth-ethereum-primitives.workspace = true 18 | reth-tasks.workspace = true 19 | tokio.workspace = true 20 | tracing.workspace = true 21 | 22 | # alloy 23 | alloy-network.workspace = true 24 | alloy-provider.workspace = true 25 | alloy-rpc-types.workspace = true 26 | -------------------------------------------------------------------------------- /crates/types/events/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "kabu-types-events" 3 | version.workspace = true 4 | edition.workspace = true 5 | rust-version.workspace = true 6 | license.workspace = true 7 | homepage.workspace = true 8 | repository.workspace = true 9 | 10 | [dependencies] 11 | kabu-types-blockchain.workspace = true 12 | kabu-types-entities.workspace = true 13 | kabu-types-market.workspace = true 14 | kabu-types-swap.workspace = true 15 | 16 | alloy-consensus.workspace = true 17 | alloy-primitives.workspace = true 18 | alloy-rpc-types.workspace = true 19 | revm.workspace = true 20 | 21 | # reth 22 | reth-ethereum-primitives.workspace = true 23 | reth-node-types.workspace = true 24 | 25 | chrono.workspace = true 26 | eyre.workspace = true 27 | -------------------------------------------------------------------------------- /crates/broadcast/broadcaster/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "kabu-broadcast-broadcaster" 3 | edition.workspace = true 4 | exclude.workspace = true 5 | homepage.workspace = true 6 | license.workspace = true 7 | repository.workspace = true 8 | rust-version.workspace = true 9 | version.workspace = true 10 | 11 | [dependencies] 12 | kabu-core-components = { workspace = true } 13 | kabu-types-events.workspace = true 14 | 15 | eyre.workspace = true 16 | tokio.workspace = true 17 | tracing.workspace = true 18 | url.workspace = true 19 | 20 | # alloy 21 | alloy-primitives.workspace = true 22 | alloy-provider.workspace = true 23 | alloy-rpc-types-mev.workspace = true 24 | alloy-signer-local.workspace = true 25 | 26 | # reth 27 | reth-tasks.workspace = true 28 | -------------------------------------------------------------------------------- /crates/defi/price/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "kabu-defi-price" 3 | edition.workspace = true 4 | exclude.workspace = true 5 | homepage.workspace = true 6 | license.workspace = true 7 | repository.workspace = true 8 | rust-version.workspace = true 9 | version.workspace = true 10 | 11 | [dependencies] 12 | kabu-core-blockchain.workspace = true 13 | kabu-core-components = { workspace = true } 14 | kabu-defi-address-book.workspace = true 15 | kabu-defi-pools.workspace = true 16 | kabu-types-market.workspace = true 17 | 18 | eyre.workspace = true 19 | tokio.workspace = true 20 | tracing.workspace = true 21 | 22 | # alloy 23 | alloy-network.workspace = true 24 | alloy-primitives.workspace = true 25 | alloy-provider.workspace = true 26 | 27 | # reth 28 | reth-tasks.workspace = true 29 | -------------------------------------------------------------------------------- /crates/defi/pools/src/virtual_impl/tick_provider.rs: -------------------------------------------------------------------------------- 1 | use crate::db_reader::UniswapV3DbReader; 2 | use alloy::primitives::{Address, U256}; 3 | use kabu_defi_uniswap_v3_math::tick_provider::TickProvider; 4 | use revm::DatabaseRef; 5 | 6 | pub struct TickProviderEVMDB { 7 | pub db: DB, 8 | pub pool_address: Address, 9 | } 10 | 11 | impl TickProviderEVMDB 12 | where 13 | DB: DatabaseRef, 14 | { 15 | pub fn new(db: DB, pool_address: Address) -> Self { 16 | TickProviderEVMDB { db, pool_address } 17 | } 18 | } 19 | 20 | impl TickProvider for TickProviderEVMDB 21 | where 22 | DB: DatabaseRef, 23 | { 24 | fn get_tick(&self, tick: i16) -> eyre::Result { 25 | UniswapV3DbReader::tick_bitmap(&self.db, self.pool_address, tick) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /taplo.toml: -------------------------------------------------------------------------------- 1 | include = ["**/Cargo.toml", "config-example.toml", "deny.toml", "rust-toolchain.toml", "rustfmt.toml"] 2 | 3 | [formatting] 4 | align_comments = false 5 | allowed_blank_lines = 2 6 | array_trailing_comma = true 7 | column_width = 140 8 | compact_arrays = true 9 | reorder_arrays = true 10 | reorder_keys = true 11 | 12 | [[rule]] 13 | # Do not reorder to keep name and version on top 14 | keys = ["package"] 15 | formatting = { reorder_keys = false } 16 | 17 | [[rule]] 18 | # Keep relay order in config-example.toml 19 | keys = ["actors"] 20 | formatting = { reorder_arrays = false } 21 | 22 | [[rule]] 23 | keys = ["package", "build-dependencies", "dependencies", "dev-dependencies"] 24 | 25 | [[rule]] 26 | keys = ["workspace.package", "workspace.dependencies", "profile.test", "profile.release", "profile.maxperf"] 27 | -------------------------------------------------------------------------------- /crates/types/entities/src/strategy_config.rs: -------------------------------------------------------------------------------- 1 | use alloy_primitives::Address; 2 | use serde::de::DeserializeOwned; 3 | use std::path::PathBuf; 4 | use thiserror::Error; 5 | use tokio::fs; 6 | 7 | #[derive(Debug, Error)] 8 | pub enum LoadConfigError { 9 | #[error("IO error: {0}")] 10 | IoError(#[from] std::io::Error), 11 | #[error("TOML error: {0}")] 12 | TomlError(#[from] toml::de::Error), 13 | } 14 | 15 | pub trait StrategyConfig { 16 | /// If None is returned, the strategy will use a random signer in the swap router. 17 | fn eoa(&self) -> Option
; 18 | } 19 | 20 | pub async fn load_from_file(file_path: PathBuf) -> Result { 21 | let contents = fs::read_to_string(file_path).await?; 22 | let config: C = toml::from_str(&contents)?; 23 | Ok(config) 24 | } 25 | -------------------------------------------------------------------------------- /crates/node/debug-provider/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "kabu-node-debug-provider" 3 | version.workspace = true 4 | edition.workspace = true 5 | rust-version.workspace = true 6 | license.workspace = true 7 | homepage.workspace = true 8 | repository.workspace = true 9 | 10 | [dependencies] 11 | async-trait.workspace = true 12 | eyre.workspace = true 13 | futures.workspace = true 14 | k256.workspace = true 15 | reqwest.workspace = true 16 | serde_json.workspace = true 17 | tokio.workspace = true 18 | tower.workspace = true 19 | tracing.workspace = true 20 | url.workspace = true 21 | 22 | alloy.workspace = true 23 | 24 | 25 | [dev-dependencies] 26 | dotenvy.workspace = true 27 | env_logger.workspace = true 28 | kabu-defi-abi.workspace = true 29 | url.workspace = true 30 | 31 | alloy-provider.workspace = true 32 | alloy-rpc-client.workspace = true 33 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to kabu 2 | We welcome contributions to kabu! Please read the following guidelines before submitting a pull request. 3 | 4 | ## Run those command before submitting a PR 5 | Before open a PR, please make sure that all tests are passing and the code is properly formatted. 6 | 7 | ### Run all tests 8 | ```bash 9 | make test 10 | make swap-test-all 11 | ``` 12 | 13 | ### Format code 14 | ```bash 15 | make clippy 16 | make fmt 17 | make taplo 18 | ``` 19 | 20 | ## Optional: Install pre-commit hooks 21 | See https://pre-commit.com for a detailed guide on how to install pre-commit hooks. 22 | Then run in the root of the repository: 23 | ```bash 24 | pre-commit install 25 | ``` 26 | 27 | ## Install tools 28 | To install the tools required to run the tests and format the code, run: 29 | ```bash 30 | cargo install taplo-cli --locked 31 | ``` -------------------------------------------------------------------------------- /crates/core/blockchain/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "kabu-core-blockchain" 3 | version.workspace = true 4 | edition.workspace = true 5 | rust-version.workspace = true 6 | license.workspace = true 7 | homepage.workspace = true 8 | repository.workspace = true 9 | 10 | [dependencies] 11 | kabu-defi-address-book.workspace = true 12 | kabu-evm-db.workspace = true 13 | kabu-types-blockchain.workspace = true 14 | kabu-types-entities.workspace = true 15 | kabu-types-events.workspace = true 16 | kabu-types-market.workspace = true 17 | 18 | 19 | eyre.workspace = true 20 | influxdb.workspace = true 21 | revm.workspace = true 22 | tokio.workspace = true 23 | tracing.workspace = true 24 | 25 | # alloy 26 | alloy.workspace = true 27 | alloy-chains.workspace = true 28 | 29 | # reth 30 | reth-ethereum-primitives.workspace = true 31 | reth-node-types.workspace = true 32 | -------------------------------------------------------------------------------- /crates/defi/pools/src/protocols/sushiswap.rs: -------------------------------------------------------------------------------- 1 | use crate::protocols::helper::get_uniswap2pool_address; 2 | use crate::protocols::protocol::Protocol; 3 | use alloy::primitives::{Address, B256}; 4 | use kabu_defi_address_book::FactoryAddress; 5 | 6 | pub struct SushiswapProtocol {} 7 | 8 | impl SushiswapProtocol { 9 | pub fn get_pool_address_for_tokens(token0: Address, token1: Address) -> Address { 10 | let init_code: B256 = "e18a34eb0e04b04f7a0ac29a6e80748dca96319b42c54d679cb821dca90c6303".parse().unwrap(); 11 | get_uniswap2pool_address(token0, token1, FactoryAddress::SUSHISWAP_V2, init_code) 12 | } 13 | } 14 | 15 | impl Protocol for SushiswapProtocol { 16 | fn get_pool_address_vec_for_tokens(token0: Address, token1: Address) -> Vec
{ 17 | vec![SushiswapProtocol::get_pool_address_for_tokens(token0, token1)] 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /crates/evm/utils/src/geth_state_update.rs: -------------------------------------------------------------------------------- 1 | use alloy::primitives::{Address, B256, U256}; 2 | use alloy::rpc::types::trace::geth::AccountState; 3 | use kabu_types_blockchain::GethStateUpdate; 4 | 5 | pub fn account_state_with_nonce_and_balance(nonce: u64, balance: U256) -> AccountState { 6 | AccountState { balance: Some(balance), code: None, nonce: Some(nonce), storage: Default::default() } 7 | } 8 | pub fn account_state_add_storage(account_state: AccountState, key: B256, value: B256) -> AccountState { 9 | let mut account_state = account_state; 10 | account_state.storage.insert(key, value); 11 | account_state 12 | } 13 | 14 | pub fn geth_state_update_add_account(update: GethStateUpdate, address: Address, account_state: AccountState) -> GethStateUpdate { 15 | let mut update = update; 16 | update.insert(address, account_state); 17 | update 18 | } 19 | -------------------------------------------------------------------------------- /crates/types/blockchain/src/accountnoncetx.rs: -------------------------------------------------------------------------------- 1 | use alloy_primitives::TxHash; 2 | 3 | #[derive(Debug, Clone, Default)] 4 | pub struct AccountNonceAndTransactions { 5 | pub nonce: Option, 6 | pub txs: Vec, 7 | } 8 | 9 | impl AccountNonceAndTransactions { 10 | pub fn new() -> Self { 11 | Self::default() 12 | } 13 | 14 | pub fn add_tx_hash(&mut self, tx_hash: TxHash) -> &mut Self { 15 | self.txs.push(tx_hash); 16 | self 17 | } 18 | 19 | pub fn set_nonce(&mut self, nonce: Option) -> &mut Self { 20 | if let Some(cur_nonce) = self.nonce 21 | && let Some(some_nonce) = nonce 22 | { 23 | if cur_nonce < some_nonce { 24 | self.nonce = Some(some_nonce); 25 | } 26 | return self; 27 | } 28 | self.nonce = nonce; 29 | self 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /crates/rpc/handler/src/dto/quote.rs: -------------------------------------------------------------------------------- 1 | use crate::dto::pool::PoolProtocol; 2 | use alloy_primitives::{Address, U256}; 3 | use serde::{Deserialize, Serialize}; 4 | use utoipa::PartialSchema; 5 | use utoipa::{IntoParams, ToSchema}; 6 | 7 | #[derive(Debug, Deserialize, IntoParams)] 8 | pub struct Filter { 9 | pub protocol: Option, 10 | } 11 | 12 | #[allow(unused)] 13 | #[derive(Debug, Deserialize, ToSchema)] 14 | pub struct QuoteRequest { 15 | #[schema(schema_with = String::schema)] 16 | pub token_address_from: Address, 17 | #[schema(schema_with = String::schema)] 18 | pub token_address_to: Address, 19 | #[schema(schema_with = String::schema)] 20 | pub amount_in: U256, 21 | } 22 | 23 | #[derive(Debug, Serialize, ToSchema)] 24 | pub struct QuoteResponse { 25 | #[schema(schema_with = String::schema)] 26 | pub out_amount: U256, 27 | pub gas_used: u64, 28 | } 29 | -------------------------------------------------------------------------------- /crates/strategy/backrun/src/backrun_config.rs: -------------------------------------------------------------------------------- 1 | use alloy_primitives::Address; 2 | use kabu_types_entities::strategy_config::StrategyConfig; 3 | use serde::Deserialize; 4 | 5 | #[derive(Clone, Deserialize, Debug)] 6 | pub struct BackrunConfigSection { 7 | pub backrun_strategy: BackrunConfig, 8 | } 9 | 10 | #[derive(Clone, Deserialize, Debug)] 11 | pub struct BackrunConfig { 12 | eoa: Option
, 13 | smart: bool, 14 | } 15 | 16 | impl StrategyConfig for BackrunConfig { 17 | fn eoa(&self) -> Option
{ 18 | self.eoa 19 | } 20 | } 21 | 22 | impl BackrunConfig { 23 | pub fn smart(&self) -> bool { 24 | self.smart 25 | } 26 | 27 | pub fn new_dumb() -> Self { 28 | Self { eoa: None, smart: false } 29 | } 30 | } 31 | 32 | impl Default for BackrunConfig { 33 | fn default() -> Self { 34 | Self { eoa: None, smart: true } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /crates/defi/abi/src/erc20.rs: -------------------------------------------------------------------------------- 1 | use alloy::sol; 2 | 3 | sol! { 4 | #[sol(abi=true,rpc)] 5 | #[derive(Debug, PartialEq, Eq)] 6 | interface IERC20 { 7 | event Transfer(address indexed from, address indexed to, uint256 value); 8 | event Approval(address indexed owner, address indexed spender, uint256 value); 9 | 10 | function decimals() external view returns (uint256); 11 | function totalSupply() external view returns (uint256); 12 | function balanceOf(address account) external view returns (uint256); 13 | function transfer(address to, uint256 amount) external returns (bool); 14 | function allowance(address owner, address spender) external view returns (uint256); 15 | function approve(address spender, uint256 amount) external returns (bool); 16 | function transferFrom(address from, address to, uint256 amount) external returns (bool); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /deny.toml: -------------------------------------------------------------------------------- 1 | [licenses] 2 | allow = [ 3 | # permissive licenses 4 | "0BSD", 5 | "Apache-2.0", 6 | "BSD-2-Clause", 7 | "BSD-3-Clause", 8 | "BSL-1.0", 9 | "CDLA-Permissive-2.0", 10 | "ISC", 11 | "MIT", 12 | "Unicode-3.0", 13 | "Unlicense", 14 | "Zlib", 15 | # permissive license with unclear patent grant 16 | "CC0-1.0", 17 | # weak/limited copyleft licenses 18 | "MPL-2.0", 19 | ] 20 | 21 | # Clearify licence: https://github.com/briansmith/ring/issues/902 22 | # Plan to remove OpenSSL: https://github.com/briansmith/ring/issues/1827 23 | [[licenses.clarify]] 24 | expression = "ISC AND MIT AND OpenSSL" 25 | license-files = [{ path = "LICENSE", hash = 0xbd0eed23 }] 26 | name = "ring" 27 | 28 | [advisories] 29 | ignore = [ 30 | # Temporary accept those until all crates are updated 31 | "RUSTSEC-2024-0436", 32 | "RUSTSEC-2025-0055", 33 | ] 34 | yanked = "warn" 35 | 36 | [bans] 37 | multiple-versions = "allow" 38 | 39 | [sources] 40 | allow-git = ["https://github.com/paradigmxyz/reth"] 41 | -------------------------------------------------------------------------------- /crates/defi/preloader/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "kabu-defi-preloader" 3 | edition.workspace = true 4 | exclude.workspace = true 5 | homepage.workspace = true 6 | license.workspace = true 7 | repository.workspace = true 8 | rust-version.workspace = true 9 | version.workspace = true 10 | 11 | [dependencies] 12 | kabu-core-blockchain.workspace = true 13 | kabu-core-components = { workspace = true } 14 | kabu-defi-address-book.workspace = true 15 | kabu-evm-utils.workspace = true 16 | kabu-types-blockchain.workspace = true 17 | kabu-types-entities.workspace = true 18 | kabu-types-market.workspace = true 19 | 20 | eyre.workspace = true 21 | tokio.workspace = true 22 | tracing.workspace = true 23 | 24 | #alloy 25 | alloy-eips.workspace = true 26 | alloy-network.workspace = true 27 | alloy-primitives.workspace = true 28 | alloy-provider.workspace = true 29 | alloy-rpc-types-trace.workspace = true 30 | 31 | #revm 32 | revm.workspace = true 33 | 34 | # reth 35 | reth-node-types.workspace = true 36 | reth-tasks.workspace = true 37 | -------------------------------------------------------------------------------- /crates/types/blockchain/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "kabu-types-blockchain" 3 | version.workspace = true 4 | edition.workspace = true 5 | rust-version.workspace = true 6 | license.workspace = true 7 | homepage.workspace = true 8 | repository.workspace = true 9 | 10 | [dependencies] 11 | kabu-node-debug-provider.workspace = true 12 | 13 | chrono.workspace = true 14 | eyre.workspace = true 15 | lazy_static.workspace = true 16 | tokio.workspace = true 17 | tracing.workspace = true 18 | 19 | # alloy 20 | alloy-consensus.workspace = true 21 | alloy-eips.workspace = true 22 | alloy-primitives.workspace = true 23 | alloy-provider.workspace = true 24 | alloy-rpc-types.workspace = true 25 | alloy-rpc-types-eth.workspace = true 26 | alloy-rpc-types-trace.workspace = true 27 | 28 | # reth 29 | reth-node-types.workspace = true 30 | reth-primitives-traits.workspace = true 31 | 32 | [dev-dependencies] 33 | dotenvy.workspace = true 34 | env_logger.workspace = true 35 | serde_json.workspace = true 36 | url.workspace = true 37 | 38 | alloy-rpc-client.workspace = true 39 | -------------------------------------------------------------------------------- /crates/evm/utils/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "kabu-evm-utils" 3 | version.workspace = true 4 | edition.workspace = true 5 | rust-version.workspace = true 6 | license.workspace = true 7 | homepage.workspace = true 8 | repository.workspace = true 9 | 10 | [dependencies] 11 | kabu-defi-abi.workspace = true 12 | kabu-defi-address-book.workspace = true 13 | kabu-evm-db.workspace = true 14 | kabu-node-debug-provider.workspace = true 15 | kabu-types-blockchain.workspace = true 16 | 17 | # misc 18 | axum.workspace = true 19 | either.workspace = true 20 | eyre.workspace = true 21 | lazy_static.workspace = true 22 | thiserror.workspace = true 23 | tracing.workspace = true 24 | 25 | # revm 26 | revm.workspace = true 27 | 28 | # alloy 29 | alloy.workspace = true 30 | alloy-eips.workspace = true 31 | alloy-evm.workspace = true 32 | alloy-primitives.workspace = true 33 | alloy-rpc-types.workspace = true 34 | alloy-rpc-types-eth.workspace = true 35 | alloy-rpc-types-trace.workspace = true 36 | 37 | # reth 38 | reth-db.workspace = true 39 | reth-primitives.workspace = true 40 | -------------------------------------------------------------------------------- /crates/node/reth-api/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "kabu-node-reth-api" 3 | edition.workspace = true 4 | exclude.workspace = true 5 | homepage.workspace = true 6 | license.workspace = true 7 | repository.workspace = true 8 | rust-version.workspace = true 9 | version.workspace = true 10 | 11 | [dependencies] 12 | async-trait.workspace = true 13 | eyre.workspace = true 14 | futures-util.workspace = true 15 | tokio.workspace = true 16 | tracing.workspace = true 17 | 18 | # alloy 19 | alloy-network.workspace = true 20 | alloy-provider.workspace = true 21 | alloy-pubsub.workspace = true 22 | alloy-transport.workspace = true 23 | 24 | # reth 25 | reth-chain-state = { workspace = true, features = ["serde"] } 26 | reth-ethereum-primitives.workspace = true 27 | reth-execution-types.workspace = true 28 | reth-node-types.workspace = true 29 | reth-primitives.workspace = true 30 | reth-primitives-traits.workspace = true 31 | reth-provider.workspace = true 32 | reth-storage-api.workspace = true 33 | reth-storage-rpc-provider.workspace = true 34 | reth-tasks.workspace = true 35 | -------------------------------------------------------------------------------- /crates/core/node/src/traits.rs: -------------------------------------------------------------------------------- 1 | //! Component builder trait 2 | 3 | use eyre::Result; 4 | use kabu_core_components::Component; 5 | 6 | /// Trait for building components from context 7 | pub trait ComponentBuilder: Sized + Send + Sync + 'static { 8 | /// The component type that will be created 9 | type Component: Component; 10 | 11 | /// Build the component from the given context 12 | fn build(self, ctx: &Ctx) -> Result; 13 | } 14 | 15 | /// Marker struct for disabled components 16 | pub struct DisabledComponent; 17 | 18 | impl Component for DisabledComponent { 19 | fn spawn(self, _executor: reth_tasks::TaskExecutor) -> Result<()> { 20 | // No-op for disabled components 21 | Ok(()) 22 | } 23 | 24 | fn name(&self) -> &'static str { 25 | "Disabled" 26 | } 27 | } 28 | 29 | impl ComponentBuilder for DisabledComponent { 30 | type Component = DisabledComponent; 31 | 32 | fn build(self, _ctx: &Ctx) -> Result { 33 | Ok(DisabledComponent) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /crates/execution/estimator/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "kabu-execution-estimator" 3 | edition.workspace = true 4 | exclude.workspace = true 5 | homepage.workspace = true 6 | license.workspace = true 7 | repository.workspace = true 8 | rust-version.workspace = true 9 | version.workspace = true 10 | 11 | [dependencies] 12 | kabu-core-components = { workspace = true } 13 | kabu-evm-db.workspace = true 14 | kabu-evm-utils.workspace = true 15 | kabu-node-debug-provider.workspace = true 16 | kabu-types-events.workspace = true 17 | kabu-types-swap.workspace = true 18 | 19 | chrono.workspace = true 20 | eyre.workspace = true 21 | influxdb.workspace = true 22 | tokio.workspace = true 23 | tracing.workspace = true 24 | 25 | # alloy 26 | alloy-consensus.workspace = true 27 | alloy-eips.workspace = true 28 | alloy-evm.workspace = true 29 | alloy-network.workspace = true 30 | alloy-primitives.workspace = true 31 | alloy-provider.workspace = true 32 | alloy-rpc-types.workspace = true 33 | 34 | #revm 35 | revm.workspace = true 36 | 37 | # reth 38 | reth-tasks.workspace = true 39 | -------------------------------------------------------------------------------- /crates/types/events/src/defi_events.rs: -------------------------------------------------------------------------------- 1 | use alloy_primitives::{BlockHash, BlockNumber, TxHash}; 2 | use kabu_types_market::PoolId; 3 | 4 | #[derive(Clone, Debug)] 5 | pub enum MarketEvents { 6 | BlockHeaderUpdate { block_number: BlockNumber, block_hash: BlockHash, timestamp: u64, base_fee: u64, next_base_fee: u64 }, 7 | BlockTxUpdate { block_number: BlockNumber, block_hash: BlockHash }, 8 | BlockStateUpdate { block_hash: BlockHash }, 9 | NewPoolLoaded { pool_id: PoolId, swap_path_idx_vec: Vec }, 10 | } 11 | 12 | #[derive(Clone, Debug)] 13 | pub enum MempoolEvents { 14 | /// The transaction has a valid nonce and provides enough gas to pay for the base fee of the next block. 15 | MempoolActualTxUpdate { 16 | tx_hash: TxHash, 17 | }, 18 | /// The transaction has been added to the mempool without any validation. 19 | MempoolTxUpdate { 20 | tx_hash: TxHash, 21 | }, 22 | MempoolStateUpdate { 23 | tx_hash: TxHash, 24 | }, 25 | MempoolLogUpdate { 26 | tx_hash: TxHash, 27 | }, 28 | } 29 | -------------------------------------------------------------------------------- /crates/types/events/src/message.rs: -------------------------------------------------------------------------------- 1 | use std::fmt::Debug; 2 | use std::ops::Deref; 3 | 4 | use chrono::Utc; 5 | 6 | #[derive(Clone, Debug)] 7 | pub struct Message { 8 | pub inner: T, 9 | pub source: Option, 10 | pub time: Option>, 11 | } 12 | 13 | impl Message { 14 | pub fn new(t: T) -> Self { 15 | Message { inner: t, source: None, time: None } 16 | } 17 | 18 | pub fn new_with_time(t: T) -> Self { 19 | Message { inner: t, source: None, time: Some(Utc::now()) } 20 | } 21 | pub fn new_with_source(t: T, source: String) -> Self { 22 | Message { inner: t, source: Some(source), time: Some(Utc::now()) } 23 | } 24 | 25 | pub fn source(&self) -> String { 26 | self.source.clone().unwrap_or("unknown".to_string()) 27 | } 28 | 29 | pub fn inner(&self) -> &T { 30 | &self.inner 31 | } 32 | } 33 | 34 | impl Deref for Message { 35 | type Target = T; 36 | 37 | fn deref(&self) -> &Self::Target { 38 | &self.inner 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /crates/types/market/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "kabu-types-market" 3 | version.workspace = true 4 | edition.workspace = true 5 | rust-version.workspace = true 6 | license.workspace = true 7 | homepage.workspace = true 8 | repository.workspace = true 9 | 10 | [dependencies] 11 | kabu-defi-address-book.workspace = true 12 | kabu-evm-db.workspace = true 13 | kabu-node-debug-provider.workspace = true 14 | kabu-types-blockchain.workspace = true 15 | 16 | eyre.workspace = true 17 | serde.workspace = true 18 | strum.workspace = true 19 | strum_macros.workspace = true 20 | thiserror.workspace = true 21 | tokio.workspace = true 22 | tokio-stream.workspace = true 23 | tracing.workspace = true 24 | 25 | alloy-evm.workspace = true 26 | alloy-network.workspace = true 27 | alloy-primitives.workspace = true 28 | alloy-provider.workspace = true 29 | alloy-rpc-types.workspace = true 30 | alloy-rpc-types-trace.workspace = true 31 | alloy-sol-types.workspace = true 32 | 33 | reth-ethereum-primitives.workspace = true 34 | revm.workspace = true 35 | 36 | [dev-dependencies] 37 | serde_json.workspace = true 38 | -------------------------------------------------------------------------------- /crates/node/node-config/src/lib.rs: -------------------------------------------------------------------------------- 1 | #[derive(Debug, Clone)] 2 | pub struct NodeBlockComponentConfig { 3 | pub block_header: bool, 4 | pub block_with_tx: bool, 5 | pub block_logs: bool, 6 | pub block_state_update: bool, 7 | } 8 | 9 | impl NodeBlockComponentConfig { 10 | pub fn all_disabled() -> Self { 11 | Self { block_header: false, block_with_tx: false, block_logs: false, block_state_update: false } 12 | } 13 | 14 | pub fn all_enabled() -> Self { 15 | Self { block_header: true, block_with_tx: true, block_logs: true, block_state_update: true } 16 | } 17 | 18 | pub fn with_block_header(mut self) -> Self { 19 | self.block_header = true; 20 | self 21 | } 22 | 23 | pub fn with_block_with_tx(mut self) -> Self { 24 | self.block_with_tx = true; 25 | self 26 | } 27 | 28 | pub fn with_block_logs(mut self) -> Self { 29 | self.block_logs = true; 30 | self 31 | } 32 | 33 | pub fn with_block_state_update(mut self) -> Self { 34 | self.block_state_update = true; 35 | self 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 cakevm 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /crates/defi/market/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "kabu-defi-market" 3 | edition.workspace = true 4 | exclude.workspace = true 5 | homepage.workspace = true 6 | license.workspace = true 7 | repository.workspace = true 8 | rust-version.workspace = true 9 | version.workspace = true 10 | 11 | [dependencies] 12 | kabu-core-blockchain.workspace = true 13 | kabu-core-components = { workspace = true } 14 | kabu-evm-db.workspace = true 15 | kabu-node-debug-provider.workspace = true 16 | kabu-types-blockchain.workspace = true 17 | kabu-types-events.workspace = true 18 | kabu-types-market.workspace = true 19 | 20 | eyre.workspace = true 21 | reth-chain-state.workspace = true 22 | reth-ethereum-primitives.workspace = true 23 | reth-execution-types.workspace = true 24 | reth-node-types.workspace = true 25 | reth-tasks.workspace = true 26 | 27 | # futures 28 | futures-util.workspace = true 29 | tokio.workspace = true 30 | tracing.workspace = true 31 | 32 | 33 | # alloy 34 | alloy-network.workspace = true 35 | alloy-primitives.workspace = true 36 | alloy-provider.workspace = true 37 | alloy-rpc-types.workspace = true 38 | 39 | #revm 40 | revm.workspace = true 41 | -------------------------------------------------------------------------------- /crates/execution/multicaller/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "kabu-execution-multicaller" 3 | version.workspace = true 4 | edition.workspace = true 5 | rust-version.workspace = true 6 | license.workspace = true 7 | homepage.workspace = true 8 | repository.workspace = true 9 | 10 | [dependencies] 11 | kabu-defi-abi.workspace = true 12 | kabu-defi-address-book.workspace = true 13 | kabu-node-debug-provider.workspace = true 14 | kabu-types-blockchain.workspace = true 15 | kabu-types-market.workspace = true 16 | kabu-types-swap.workspace = true 17 | 18 | eyre.workspace = true 19 | k256.workspace = true 20 | lazy_static.workspace = true 21 | tokio.workspace = true 22 | tracing.workspace = true 23 | 24 | # alloy 25 | alloy-eips.workspace = true 26 | alloy-network.workspace = true 27 | alloy-primitives.workspace = true 28 | alloy-provider.workspace = true 29 | alloy-rpc-types.workspace = true 30 | alloy-rpc-types-trace.workspace = true 31 | alloy-signer-local.workspace = true 32 | alloy-sol-types.workspace = true 33 | 34 | 35 | [dev-dependencies] 36 | dotenvy.workspace = true 37 | env_logger.workspace = true 38 | kabu-defi-pools.workspace = true 39 | tokio.workspace = true 40 | -------------------------------------------------------------------------------- /testing/backtest-runner/test_21035613.toml: -------------------------------------------------------------------------------- 1 | # WETH -> GOAT -> GOAT -> WETH ARB CASE 2 | # replay tx with loom for 0x28243575d2f32061fb0044d6338e73907d3e5324aa3644cb849035e398ec375e 3 | [modules] 4 | signer = true 5 | flashbots = true 6 | 7 | [settings] 8 | block = 21035613 9 | coinbase = "0x1dd35b4da6534230ff53048f7477f17f7f4e7a70" 10 | multicaller = "0x3dd35b4da6534230ff53048f7477f17f7f4e7a70" 11 | 12 | [pools] 13 | # in order of swaps 14 | goat_weth_uni3 = { address = "0x8682fc63dc2525fd2e5ed4e28e207a2fd9f36dab", class = "uniswap3" } 15 | goat_weth_uni2 = { address = "0x1084a95c69bdc3325c1c42f86676b6eb66dce053", class = "uniswap2" } 16 | 17 | 18 | [txs] 19 | # block 21035614 20 | tx_1 = { hash = "0xccd80581ece52527eebba671b7a2bb9867195481244a92bca31177a75f58227a", send = "mempool" } 21 | 22 | 23 | [tokens] 24 | weth = { address = "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", symbol = "WETH", decimals = 18, basic = true, middle = false } 25 | goat = { address = "0x5200b34e6a519f289f5258de4554ebd3db12e822", symbol = "GOAT", decimals = 9, basic = false, middle = false } 26 | 27 | 28 | [assertions] 29 | swaps_encoded = 1 30 | swaps_ok = 1 31 | best_profit_eth = 0.0032 32 | -------------------------------------------------------------------------------- /crates/defi/abi/src/curve/common.rs: -------------------------------------------------------------------------------- 1 | use alloy::sol; 2 | 3 | sol! { 4 | #[sol(abi = true, rpc)] 5 | #[derive(Debug, PartialEq, Eq)] 6 | interface ICurveFactory { 7 | function pool_list(uint256) external view returns (address); 8 | function pool_count() external view returns (uint256); 9 | } 10 | } 11 | 12 | sol! { 13 | #[sol(abi = true, rpc)] 14 | #[derive(Debug, PartialEq, Eq)] 15 | interface ICurveAddressProvider { 16 | function get_address(uint256) external view returns (address); 17 | } 18 | } 19 | 20 | sol! { 21 | #[sol(abi = true, rpc)] 22 | #[derive(Debug, PartialEq, Eq)] 23 | interface ICurveCommon { 24 | function coins(uint256) external view returns (bytes); 25 | function balances(uint256) external view returns (uint256); 26 | function get_balances() external view returns (bytes); 27 | } 28 | } 29 | 30 | sol! { 31 | #[sol(abi = true, rpc)] 32 | #[derive(Debug, PartialEq, Eq)] 33 | interface ICurveCommonI128 { 34 | function coins(int128) external view returns (bytes); 35 | function balances(int128) external view returns (uint256); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /testing/backtest-runner/test_18567709.toml: -------------------------------------------------------------------------------- 1 | #WETH PEPE ARB CASE WITH TWO STUFFING 2 | #ALL POOLS PRELOADED 3 | [modules] 4 | signer = true 5 | flashbots = true 6 | 7 | 8 | [settings] 9 | block = 18567709 10 | coinbase = "0x1dd35b4da6534230ff53048f7477f17f7f4e7a70" 11 | multicaller = "0x3dd35b4da6534230ff53048f7477f17f7f4e7a70" 12 | skip_default = false 13 | 14 | [pools] 15 | weth_pepe_uni3 = { address = "0xa84181f223a042949e9040e42b44c50021802db6", class = "uniswap3" } 16 | weth_pepe_uni2 = { address = "0xaa9b647f42858f2db441f0aa75843a8e7fd5aff2", class = "uniswap2" } 17 | 18 | [txs] 19 | 20 | tx_1 = { hash = "0x037c66ae5e0e893c4f47ef47d21f0afc18fdad334f92e898cae1f2a3da92f9b3", send = "mempool" } 21 | tx_2 = { hash = "0x054a3f0c4ff3cf582c167669ed845f50b39f92007683c03b2ea53c522749d215", send = "mempool" } 22 | 23 | [tokens] 24 | weth = { address = "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", symbol = "WETH", decimals = 18, basic = true, middle = false } 25 | pepe = { address = "0x6982508145454Ce325dDbE47a25d4ec3d2311933", symbol = "PEPE", decimals = 18, basic = false, middle = false } 26 | 27 | 28 | [assertions] 29 | swaps_encoded = 1 30 | swaps_ok = 1 31 | best_profit_eth = 0.029 -------------------------------------------------------------------------------- /crates/evm/utils/src/remv_db_direct_access.rs: -------------------------------------------------------------------------------- 1 | use alloy::primitives::{Address, U256, keccak256}; 2 | use eyre::{Result, eyre}; 3 | use revm::DatabaseRef; 4 | use tracing::debug; 5 | 6 | pub fn calc_hashmap_cell, U1: Into>(offset: U0, cell: U1) -> U256 { 7 | let offset: U256 = offset.into(); 8 | let cell: U256 = cell.into(); 9 | let mut buf: Vec = Vec::new(); 10 | buf.extend(cell.to_be_bytes_vec()); 11 | buf.extend(offset.to_be_bytes_vec()); 12 | debug!("Reading cell : {} {} {:?}", offset, cell, buf); 13 | 14 | keccak256(buf).into() 15 | } 16 | 17 | pub fn try_read_cell(db: &DB, account: &Address, cell: &U256) -> Result { 18 | db.storage_ref(*account, *cell).map_err(|_| eyre!("READ_CELL_FAILED")) 19 | } 20 | 21 | pub fn try_read_hashmap_cell(db: &DB, account: &Address, hashmap_offset: &U256, item: &U256) -> Result { 22 | let mut buf = item.to_be_bytes::<32>().to_vec(); 23 | buf.append(&mut hashmap_offset.to_be_bytes::<32>().to_vec()); 24 | let cell: U256 = keccak256(buf.as_slice()).into(); 25 | db.storage_ref(*account, cell).map_err(|_| eyre!("READ_HASHMAP_CELL_ERROR")) 26 | } 27 | -------------------------------------------------------------------------------- /book/js/mermaid-init.js: -------------------------------------------------------------------------------- 1 | (() => { 2 | const darkThemes = ['ayu', 'navy', 'coal']; 3 | const lightThemes = ['light', 'rust']; 4 | 5 | const classList = document.getElementsByTagName('html')[0].classList; 6 | 7 | let lastThemeWasLight = true; 8 | for (const cssClass of classList) { 9 | if (darkThemes.includes(cssClass)) { 10 | lastThemeWasLight = false; 11 | break; 12 | } 13 | } 14 | 15 | const theme = lastThemeWasLight ? 'default' : 'dark'; 16 | mermaid.initialize({ startOnLoad: true, theme }); 17 | 18 | // Simplest way to make mermaid re-render the diagrams in the new theme is via refreshing the page 19 | 20 | for (const darkTheme of darkThemes) { 21 | document.getElementById(darkTheme).addEventListener('click', () => { 22 | if (lastThemeWasLight) { 23 | window.location.reload(); 24 | } 25 | }); 26 | } 27 | 28 | for (const lightTheme of lightThemes) { 29 | document.getElementById(lightTheme).addEventListener('click', () => { 30 | if (!lastThemeWasLight) { 31 | window.location.reload(); 32 | } 33 | }); 34 | } 35 | })(); 36 | -------------------------------------------------------------------------------- /crates/rpc/handler/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "kabu-rpc-handler" 3 | edition.workspace = true 4 | exclude.workspace = true 5 | homepage.workspace = true 6 | license.workspace = true 7 | repository.workspace = true 8 | rust-version.workspace = true 9 | version.workspace = true 10 | 11 | [dependencies] 12 | kabu-core-blockchain.workspace = true 13 | kabu-core-components = { workspace = true } 14 | kabu-evm-db.workspace = true 15 | kabu-evm-utils.workspace = true 16 | kabu-rpc-state.workspace = true 17 | kabu-storage-db.workspace = true 18 | kabu-types-blockchain.workspace = true 19 | kabu-types-events.workspace = true 20 | kabu-types-market.workspace = true 21 | 22 | hex.workspace = true 23 | serde.workspace = true 24 | serde_json.workspace = true 25 | 26 | alloy-primitives.workspace = true 27 | revm.workspace = true 28 | 29 | # async 30 | tokio.workspace = true 31 | tokio-util.workspace = true 32 | 33 | # tracing + error handling 34 | eyre.workspace = true 35 | tracing.workspace = true 36 | 37 | # reth 38 | reth-ethereum-primitives.workspace = true 39 | reth-tasks.workspace = true 40 | 41 | # web 42 | axum.workspace = true 43 | tower-http.workspace = true 44 | utoipa.workspace = true 45 | utoipa-swagger-ui.workspace = true 46 | -------------------------------------------------------------------------------- /crates/types/blockchain/src/chain_parameters.rs: -------------------------------------------------------------------------------- 1 | use alloy_eips::eip1559::BaseFeeParams; 2 | use alloy_rpc_types_eth::Header; 3 | 4 | #[derive(Clone, Debug)] 5 | pub struct ChainParameters { 6 | pub chain_id: u64, 7 | pub base_fee_params: BaseFeeParams, 8 | } 9 | 10 | impl ChainParameters { 11 | pub fn ethereum() -> ChainParameters { 12 | ChainParameters { chain_id: 1, base_fee_params: BaseFeeParams::ethereum() } 13 | } 14 | 15 | pub fn calc_next_block_base_fee(&self, gas_used: u64, gas_limit: u64, base_fee: u64) -> u64 { 16 | self.base_fee_params.next_block_base_fee(gas_used, gas_limit, base_fee) 17 | } 18 | 19 | pub fn calc_next_block_base_fee_from_header(&self, header: &Header) -> u64 { 20 | self.base_fee_params.next_block_base_fee(header.gas_used, header.gas_limit, header.base_fee_per_gas.unwrap_or_default()) 21 | } 22 | } 23 | 24 | impl Default for ChainParameters { 25 | fn default() -> Self { 26 | Self::ethereum() 27 | } 28 | } 29 | impl From for ChainParameters { 30 | fn from(chain_id: u64) -> Self { 31 | match chain_id { 32 | 1 => ChainParameters::ethereum(), 33 | _ => unimplemented!(), 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /crates/core/components/src/kabu_node.rs: -------------------------------------------------------------------------------- 1 | use crate::Component; 2 | use eyre::Result; 3 | use reth_tasks::TaskExecutor; 4 | 5 | // Note: This file should only contain trait definitions and framework code 6 | // Concrete implementations should be in the bin/kabu crate or other implementation crates 7 | 8 | /// Simple placeholder component for testing the framework 9 | pub struct PlaceholderComponent { 10 | name: &'static str, 11 | } 12 | 13 | impl PlaceholderComponent { 14 | pub fn new(name: &'static str) -> Self { 15 | Self { name } 16 | } 17 | } 18 | 19 | impl Component for PlaceholderComponent { 20 | fn spawn(self, executor: TaskExecutor) -> Result<()> { 21 | let name = self.name; 22 | executor.spawn_critical(name, async move { 23 | tracing::info!("{} placeholder component running", name); 24 | // Placeholder components just log and stay alive 25 | loop { 26 | tokio::time::sleep(std::time::Duration::from_secs(60)).await; 27 | tracing::debug!("{} placeholder component heartbeat", name); 28 | } 29 | }); 30 | Ok(()) 31 | } 32 | 33 | fn name(&self) -> &'static str { 34 | self.name 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /book/tips_and_tricks.md: -------------------------------------------------------------------------------- 1 | # Tips & Tricks 2 | 3 | This section contains various tips and tricks for working with the Kabu framework effectively. 4 | 5 | ## Component Development 6 | 7 | When developing components, keep these tips in mind: 8 | 9 | 1. **Use the Builder Pattern**: Components should provide builder methods for configuration 10 | 2. **Handle Errors Gracefully**: Use proper error handling and logging 11 | 3. **Test in Isolation**: Write unit tests for component logic separately from the messaging infrastructure 12 | 13 | ## Performance Optimization 14 | 15 | 1. **Channel Sizing**: Size channels based on expected throughput 16 | 2. **Parallel Processing**: Use tokio's concurrency features effectively 17 | 3. **State Management**: Minimize lock contention with Arc> 18 | 19 | ## Debugging 20 | 21 | 1. **Enable Tracing**: Use `RUST_LOG=debug` to see detailed logs 22 | 2. **Monitor Channels**: Track channel depths and message flow 23 | 3. **Component Lifecycle**: Log component startup and shutdown 24 | 25 | For more specific tips, see the subsections: 26 | - [Custom Messages](tips_and_tricks/custom_messages.md) - How to extend the messaging system 27 | - [Address Book](tips_and_tricks/address_book.md) - Managing addresses and configurations -------------------------------------------------------------------------------- /crates/rpc/handler/src/dto/flashbots.rs: -------------------------------------------------------------------------------- 1 | use alloy_primitives::{B256, Bytes, U64}; 2 | use serde::{Deserialize, Serialize}; 3 | use utoipa::PartialSchema; 4 | use utoipa::ToSchema; 5 | 6 | #[derive(Debug, Deserialize, ToSchema)] 7 | #[serde(rename_all = "camelCase")] 8 | pub struct BundleParam { 9 | #[serde(rename = "txs")] 10 | #[schema(schema_with = String::schema)] 11 | pub transactions: Vec, 12 | 13 | #[serde(rename = "blockNumber")] 14 | #[schema(schema_with = String::schema)] 15 | pub target_block: Option, 16 | // dropped the rest of the fields 17 | } 18 | 19 | #[derive(Debug, Deserialize, ToSchema)] 20 | pub struct BundleRequest { 21 | #[allow(dead_code)] 22 | pub jsonrpc: String, 23 | #[allow(dead_code)] 24 | pub id: u64, 25 | #[allow(dead_code)] 26 | pub method: String, 27 | pub params: Vec, 28 | } 29 | 30 | #[derive(Serialize, ToSchema)] 31 | #[serde(rename_all = "camelCase")] 32 | pub struct BundleResponse { 33 | #[schema(schema_with = String::schema)] 34 | pub bundle_hash: Option, 35 | } 36 | 37 | #[derive(Serialize, ToSchema)] 38 | pub struct SendBundleResponse { 39 | pub jsonrpc: String, 40 | pub id: u64, 41 | pub result: BundleResponse, 42 | } 43 | -------------------------------------------------------------------------------- /crates/broadcast/accounts/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "kabu-broadcast-accounts" 3 | edition.workspace = true 4 | exclude.workspace = true 5 | homepage.workspace = true 6 | license.workspace = true 7 | repository.workspace = true 8 | rust-version.workspace = true 9 | version.workspace = true 10 | 11 | [dependencies] 12 | kabu-core-blockchain.workspace = true 13 | kabu-core-components = { workspace = true } 14 | kabu-types-entities.workspace = true 15 | kabu-types-events.workspace = true 16 | kabu-types-swap.workspace = true 17 | 18 | eyre.workspace = true 19 | tokio.workspace = true 20 | tracing.workspace = true 21 | 22 | #alloy 23 | alloy-consensus.workspace = true 24 | alloy-eips.workspace = true 25 | alloy-network.workspace = true 26 | alloy-primitives.workspace = true 27 | alloy-provider.workspace = true 28 | 29 | # reth 30 | reth-chain-state.workspace = true 31 | reth-ethereum-primitives.workspace = true 32 | reth-node-types.workspace = true 33 | reth-primitives.workspace = true 34 | reth-primitives-traits.workspace = true 35 | reth-provider.workspace = true 36 | reth-revm.workspace = true 37 | reth-rpc-eth-types.workspace = true 38 | reth-tasks.workspace = true 39 | 40 | # futures 41 | futures-util.workspace = true 42 | 43 | # revm 44 | revm.workspace = true 45 | -------------------------------------------------------------------------------- /crates/defi/health-monitor/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "kabu-defi-health-monitor" 3 | edition.workspace = true 4 | exclude.workspace = true 5 | homepage.workspace = true 6 | license.workspace = true 7 | repository.workspace = true 8 | rust-version.workspace = true 9 | version.workspace = true 10 | 11 | [dependencies] 12 | kabu-core-blockchain.workspace = true 13 | kabu-core-components = { workspace = true } 14 | kabu-defi-address-book.workspace = true 15 | kabu-evm-db.workspace = true 16 | kabu-evm-utils.workspace = true 17 | kabu-types-blockchain.workspace = true 18 | kabu-types-events.workspace = true 19 | kabu-types-market.workspace = true 20 | kabu-types-swap.workspace = true 21 | 22 | chrono.workspace = true 23 | eyre.workspace = true 24 | influxdb.workspace = true 25 | lazy_static.workspace = true 26 | tikv-jemalloc-ctl.workspace = true 27 | tokio.workspace = true 28 | tracing.workspace = true 29 | 30 | #alloy 31 | alloy-consensus.workspace = true 32 | alloy-eips.workspace = true 33 | alloy-network.workspace = true 34 | alloy-primitives.workspace = true 35 | alloy-provider.workspace = true 36 | alloy-rpc-types.workspace = true 37 | 38 | #revm 39 | revm.workspace = true 40 | 41 | # reth 42 | reth-node-types.workspace = true 43 | reth-tasks.workspace = true 44 | -------------------------------------------------------------------------------- /crates/evm/db/src/database_loom.rs: -------------------------------------------------------------------------------- 1 | use crate::error::KabuDBError; 2 | use crate::fast_cache_db::FastDbAccount; 3 | use alloy::primitives::map::HashMap; 4 | use alloy::primitives::{Address, U256}; 5 | use revm::DatabaseRef; 6 | use revm::state::AccountInfo; 7 | 8 | pub trait DatabaseKabuExt { 9 | fn with_ext_db(&mut self, db: impl DatabaseRef + Send + Sync + 'static); 10 | fn is_account(&self, address: &Address) -> bool; 11 | fn is_slot(&self, address: &Address, slot: &U256) -> bool; 12 | fn contracts_len(&self) -> usize; 13 | fn accounts_len(&self) -> usize; 14 | fn storage_len(&self) -> usize; 15 | 16 | fn load_account(&mut self, address: Address) -> eyre::Result<&mut FastDbAccount>; 17 | 18 | fn load_cached_account(&mut self, address: Address) -> eyre::Result<&mut FastDbAccount>; 19 | 20 | fn insert_contract(&mut self, account: &mut AccountInfo); 21 | 22 | fn insert_account_info(&mut self, address: Address, info: AccountInfo); 23 | 24 | fn insert_account_storage(&mut self, address: Address, slot: U256, value: U256) -> eyre::Result<()>; 25 | 26 | fn replace_account_storage(&mut self, address: Address, storage: HashMap) -> eyre::Result<()>; 27 | 28 | fn maintain(self) -> Self; 29 | } 30 | -------------------------------------------------------------------------------- /crates/strategy/backrun/src/swap_calculator.rs: -------------------------------------------------------------------------------- 1 | use alloy_evm::EvmEnv; 2 | use alloy_primitives::U256; 3 | use alloy_primitives::utils::parse_units; 4 | use kabu_evm_db::KabuDBError; 5 | use kabu_types_swap::{SwapError, SwapErrorKind, SwapLine}; 6 | use lazy_static::lazy_static; 7 | use revm::DatabaseRef; 8 | 9 | lazy_static! { 10 | static ref START_OPTIMIZE_INPUT: U256 = parse_units("0.01", "ether").unwrap().get_absolute(); 11 | } 12 | 13 | pub struct SwapCalculator {} 14 | 15 | impl SwapCalculator { 16 | #[inline] 17 | #[allow(clippy::result_large_err)] 18 | pub fn calculate<'a, DB: DatabaseRef>( 19 | path: &'a mut SwapLine, 20 | db: &DB, 21 | evm_env: EvmEnv, 22 | ) -> eyre::Result<&'a mut SwapLine, SwapError> { 23 | let first_token = path.get_first_token().unwrap(); 24 | if let Some(amount_in) = first_token.calc_token_value_from_eth(*START_OPTIMIZE_INPUT) { 25 | //trace!("calculate : {} amount in : {}",first_token.get_symbol(), first_token.to_float(amount_in) ); 26 | path.optimize_with_in_amount(db, evm_env, amount_in) 27 | } else { 28 | Err(path.to_error(SwapErrorKind::CalculationError("PRICE_NOT_SET".to_string()))) 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /crates/defi/pools/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "kabu-defi-pools" 3 | version.workspace = true 4 | edition.workspace = true 5 | rust-version.workspace = true 6 | license.workspace = true 7 | homepage.workspace = true 8 | repository.workspace = true 9 | 10 | [dependencies] 11 | kabu-defi-abi.workspace = true 12 | kabu-defi-address-book.workspace = true 13 | kabu-defi-uniswap-v3-math.workspace = true 14 | kabu-evm-db.workspace = true 15 | kabu-evm-utils.workspace = true 16 | kabu-types-blockchain.workspace = true 17 | kabu-types-entities.workspace = true 18 | kabu-types-market.workspace = true 19 | 20 | async-stream.workspace = true 21 | eyre.workspace = true 22 | futures.workspace = true 23 | lazy_static.workspace = true 24 | revm.workspace = true 25 | tokio-stream.workspace = true 26 | tracing.workspace = true 27 | 28 | 29 | # alloy 30 | alloy.workspace = true 31 | alloy-evm.workspace = true 32 | 33 | # reth 34 | reth-ethereum-primitives.workspace = true 35 | reth-node-types.workspace = true 36 | 37 | [features] 38 | debug-calculation = [] 39 | 40 | 41 | [dev-dependencies] 42 | kabu-node-debug-provider.workspace = true 43 | 44 | alloy-rpc-client.workspace = true 45 | dotenvy.workspace = true 46 | env_logger.workspace = true 47 | rand.workspace = true 48 | tokio.workspace = true 49 | -------------------------------------------------------------------------------- /crates/defi/market/src/logs_parser.rs: -------------------------------------------------------------------------------- 1 | use alloy_network::Network; 2 | use alloy_primitives::Log; 3 | use alloy_provider::Provider; 4 | use eyre::Result; 5 | use std::collections::HashMap; 6 | use tokio::sync::broadcast; 7 | use tracing::error; 8 | 9 | use kabu_types_events::LoomTask; 10 | use kabu_types_market::PoolLoaders; 11 | 12 | pub async fn process_log_entries( 13 | log_entries: Vec, 14 | pool_loaders: &PoolLoaders, 15 | tasks_tx: broadcast::Sender, 16 | ) -> Result<()> 17 | where 18 | N: Network, 19 | P: Provider + Send + Sync + Clone + 'static, 20 | { 21 | let mut pool_to_fetch = Vec::new(); 22 | let mut processed_pools = HashMap::new(); 23 | 24 | for log_entry in log_entries.into_iter() { 25 | if let Some((pool_id, pool_class)) = pool_loaders.determine_pool_class(&log_entry) { 26 | // was this pool already processed? 27 | if processed_pools.insert(log_entry.address, true).is_some() { 28 | continue; 29 | } 30 | 31 | pool_to_fetch.push((pool_id, pool_class)); 32 | } 33 | } 34 | 35 | if let Err(e) = tasks_tx.send(LoomTask::FetchAndAddPools(pool_to_fetch)) { 36 | error!("Failed to send task: {}", e); 37 | } 38 | Ok(()) 39 | } 40 | -------------------------------------------------------------------------------- /crates/strategy/merger/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "kabu-strategy-merger" 3 | edition.workspace = true 4 | exclude.workspace = true 5 | homepage.workspace = true 6 | license.workspace = true 7 | repository.workspace = true 8 | rust-version.workspace = true 9 | version.workspace = true 10 | 11 | [dependencies] 12 | kabu-core-components = { workspace = true } 13 | kabu-evm-db.workspace = true 14 | kabu-evm-utils.workspace = true 15 | kabu-node-debug-provider.workspace = true 16 | kabu-types-blockchain.workspace = true 17 | kabu-types-entities.workspace = true 18 | kabu-types-events.workspace = true 19 | kabu-types-market.workspace = true 20 | kabu-types-swap.workspace = true 21 | 22 | chrono.workspace = true 23 | eyre.workspace = true 24 | lazy_static.workspace = true 25 | revm.workspace = true 26 | tokio.workspace = true 27 | tracing.workspace = true 28 | 29 | # alloy 30 | alloy-consensus.workspace = true 31 | alloy-eips.workspace = true 32 | alloy-network.workspace = true 33 | alloy-primitives.workspace = true 34 | alloy-provider.workspace = true 35 | alloy-rpc-types.workspace = true 36 | alloy-rpc-types-trace.workspace = true 37 | 38 | # reth 39 | reth-node-types.workspace = true 40 | reth-primitives-traits.workspace = true 41 | reth-provider.workspace = true 42 | reth-tasks.workspace = true 43 | -------------------------------------------------------------------------------- /testing/backtest-runner/test_21063544.toml: -------------------------------------------------------------------------------- 1 | # WETH -> SATO -> SATO -> WETH ARB CASE 2 | # replay tx with loom for 0x3de3021d19af3ae7a14d149ecc88e5943ee6c282b814ff86e9b86e159cf749ed 3 | [modules] 4 | signer = true 5 | flashbots = true 6 | 7 | [settings] 8 | block = 21063544 9 | coinbase = "0x1dd35b4da6534230ff53048f7477f17f7f4e7a70" 10 | multicaller = "0x3dd35b4da6534230ff53048f7477f17f7f4e7a70" 11 | skip_default = false 12 | # DISABLED: Not finding expected arbitrage opportunities (expects 1, finds 0) - needs investigation 13 | disabled = true 14 | 15 | [pools] 16 | # in order of swaps 17 | sato_weth_uni2 = { address = "0x1e79d6529f271876d202cbb216b856165d862353", class = "uniswap2" } 18 | sato_weth_uni3 = { address = "0xf65c9da67e5cfdb097b7e9010e2f509a4b0d4cb2", class = "uniswap3" } 19 | 20 | 21 | [txs] 22 | # block 21063545 23 | tx_1 = { hash = "0xa3d5a476ce4fb9b087e0bca72f1718c76c6ed08df19a24adcdd6b076cadeb3db", send = "mempool" } 24 | 25 | 26 | [tokens] 27 | weth = { address = "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", symbol = "WETH", decimals = 18, basic = true, middle = false } 28 | sato = { address = "0x5De758BbA013e58dAe2693aEA3f0B12B31a3023d", symbol = "SATO", decimals = 18, basic = false, middle = false } 29 | 30 | 31 | [assertions] 32 | swaps_encoded = 1 33 | swaps_ok = 1 34 | best_profit_eth = 0.005 35 | -------------------------------------------------------------------------------- /crates/node/debug-provider/src/cachefolder.rs: -------------------------------------------------------------------------------- 1 | use std::path::Path; 2 | 3 | use alloy::primitives::B256; 4 | use eyre::Result; 5 | use tokio::fs; 6 | use tokio::io::{AsyncReadExt, AsyncWriteExt}; 7 | 8 | #[derive(Clone)] 9 | pub struct CacheFolder { 10 | path: String, 11 | } 12 | 13 | impl CacheFolder { 14 | pub async fn new(path: &str) -> Self { 15 | if !Path::new(path).exists() { 16 | fs::create_dir_all(path).await.unwrap(); 17 | } 18 | CacheFolder { path: path.to_string() } 19 | } 20 | 21 | pub async fn write(&self, method: String, index: B256, data: String) -> Result<()> { 22 | let file_path = format!("{}/{}_{}.json", self.path, method.to_lowercase(), index.to_string().strip_prefix("0x").unwrap()); 23 | let mut file = fs::File::create(file_path).await?; 24 | file.write_all(data.as_bytes()).await?; 25 | Ok(()) 26 | } 27 | 28 | pub async fn read(&self, method: String, index: B256) -> Result { 29 | let file_path = format!("{}/{}_{}.json", self.path, method.to_lowercase(), index.to_string().strip_prefix("0x").unwrap()); 30 | let mut file = fs::File::open(file_path).await?; 31 | let mut content = String::new(); 32 | file.read_to_string(&mut content).await?; 33 | Ok(content) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /testing/backtest-runner/test_20927846.toml: -------------------------------------------------------------------------------- 1 | # CHAD_0x5c CHAD_0x7d WETH ARB CASE 2 | [modules] 3 | signer = true 4 | flashbots = true 5 | 6 | [settings] 7 | block = 20927847 8 | coinbase = "0x1dd35b4da6534230ff53048f7477f17f7f4e7a70" 9 | multicaller = "0x3dd35b4da6534230ff53048f7477f17f7f4e7a70" 10 | skip_default = false 11 | 12 | [pools] 13 | chad_0x7d_weth_uni2 = { address = "0xff554b495ba09cb31c1833ed133e732aabaf7aca", class = "uniswap2" } 14 | chad_0x5c_chad_0x7d = { address = "0xd6a74e770fa71967b97186d9d59ce633627bffaa", class = "uniswap2" } 15 | chad_0x5c_weth_uni3 = { address = "0xf532da4460c965379593d38f89afae437fb54700", class = "uniswap3" } 16 | 17 | 18 | [txs] 19 | # block 20927848 20 | tx_1 = { hash = "0x98ce09e2d71ab3f4e47c8c437386c1b4413bf58242acb8d21c2e61a8123ed04e", send = "mempool" } 21 | 22 | 23 | [tokens] 24 | weth = { address = "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", symbol = "WETH", decimals = 18, basic = true, middle = false } 25 | chad_0x5c = { address = "0x5c888fa2e6f9f0880321683d1efa12e936fd5051", symbol = "CHAD_0x5c", decimals = 18, basic = false, middle = false } 26 | chad_0x7d = { address = "0x7d7ef607A2B7aB675194D85eB03b276a93F6b71d", symbol = "CHAD_0x7d", decimals = 18, basic = false, middle = false } 27 | 28 | [assertions] 29 | swaps_encoded = 1 30 | swaps_ok = 1 31 | best_profit_eth = 0.003239 32 | -------------------------------------------------------------------------------- /migrations/00000000000000_diesel_initial_setup/up.sql: -------------------------------------------------------------------------------- 1 | -- This file was automatically created by Diesel to setup helper functions 2 | -- and other internal bookkeeping. This file is safe to edit, any future 3 | -- changes will be added to existing projects as new migrations. 4 | 5 | 6 | 7 | 8 | -- Sets up a trigger for the given table to automatically set a column called 9 | -- `updated_at` whenever the row is modified (unless `updated_at` was included 10 | -- in the modified columns) 11 | -- 12 | -- # Example 13 | -- 14 | -- ```sql 15 | -- CREATE TABLE users (id SERIAL PRIMARY KEY, updated_at TIMESTAMP NOT NULL DEFAULT NOW()); 16 | -- 17 | -- SELECT diesel_manage_updated_at('users'); 18 | -- ``` 19 | CREATE OR REPLACE FUNCTION diesel_manage_updated_at(_tbl regclass) RETURNS VOID AS $$ 20 | BEGIN 21 | EXECUTE format('CREATE TRIGGER set_updated_at BEFORE UPDATE ON %s 22 | FOR EACH ROW EXECUTE PROCEDURE diesel_set_updated_at()', _tbl); 23 | END; 24 | $$ LANGUAGE plpgsql; 25 | 26 | CREATE OR REPLACE FUNCTION diesel_set_updated_at() RETURNS trigger AS $$ 27 | BEGIN 28 | IF ( 29 | NEW IS DISTINCT FROM OLD AND 30 | NEW.updated_at IS NOT DISTINCT FROM OLD.updated_at 31 | ) THEN 32 | NEW.updated_at := current_timestamp; 33 | END IF; 34 | RETURN NEW; 35 | END; 36 | $$ LANGUAGE plpgsql; 37 | -------------------------------------------------------------------------------- /crates/core/mempool/src/mempool_builder.rs: -------------------------------------------------------------------------------- 1 | use crate::MempoolComponent; 2 | use eyre::Result; 3 | use kabu_core_blockchain::AppState; 4 | use kabu_core_components::{BuilderContext, PoolBuilder}; 5 | use reth_node_types::NodePrimitives; 6 | use tokio::sync::broadcast; 7 | 8 | /// Builder for the mempool component 9 | #[derive(Clone, Default)] 10 | pub struct MempoolBuilder; 11 | 12 | impl MempoolBuilder { 13 | pub fn new() -> Self { 14 | Self 15 | } 16 | } 17 | 18 | impl PoolBuilder> for MempoolBuilder 19 | where 20 | NP: NodePrimitives + Default + 'static, 21 | { 22 | type Pool = MempoolComponent; 23 | 24 | async fn build_pool(self, ctx: &BuilderContext>) -> Result { 25 | let state = &ctx.state; 26 | 27 | // For now, create dummy channels 28 | let (_mempool_tx, mempool_rx) = broadcast::channel(1000); 29 | let (_market_events_tx, market_events_rx) = broadcast::channel(1000); 30 | let (mempool_events_tx, _) = broadcast::channel(1000); 31 | 32 | Ok(MempoolComponent::new( 33 | state.chain_parameters.clone(), 34 | state.mempool.clone(), 35 | mempool_rx, 36 | market_events_rx, 37 | mempool_events_tx, 38 | None, // influxdb_tx 39 | )) 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /crates/types/blockchain/src/mempool_tx.rs: -------------------------------------------------------------------------------- 1 | use crate::{FetchState, GethStateUpdate}; 2 | use alloy_primitives::{BlockNumber, TxHash}; 3 | use alloy_rpc_types_eth::Log; 4 | use chrono::{DateTime, Utc}; 5 | use reth_node_types::NodePrimitives; 6 | 7 | #[derive(Clone, Debug)] 8 | pub struct MempoolTx { 9 | pub source: String, 10 | pub tx_hash: TxHash, 11 | pub time: DateTime, 12 | pub tx: Option, 13 | pub logs: Option>, 14 | pub mined: Option, 15 | pub failed: Option, 16 | pub state_update: Option, 17 | pub pre_state: Option>, 18 | } 19 | 20 | impl MempoolTx { 21 | pub fn new() -> MempoolTx { 22 | MempoolTx { ..MempoolTx::default() } 23 | } 24 | pub fn new_with_hash(tx_hash: TxHash) -> MempoolTx { 25 | MempoolTx { tx_hash, ..MempoolTx::default() } 26 | } 27 | } 28 | 29 | impl Default for MempoolTx { 30 | fn default() -> Self { 31 | MempoolTx { 32 | source: "unknown".to_string(), 33 | tx_hash: TxHash::default(), 34 | time: Utc::now(), 35 | tx: None, 36 | state_update: None, 37 | logs: None, 38 | mined: None, 39 | failed: None, 40 | pre_state: None, 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /crates/core/blockchain/src/app_state.rs: -------------------------------------------------------------------------------- 1 | use alloy::primitives::ChainId; 2 | use kabu_types_blockchain::ChainParameters; 3 | use kabu_types_blockchain::Mempool; 4 | use kabu_types_entities::AccountNonceAndBalanceState; 5 | use kabu_types_market::Market; 6 | use reth_ethereum_primitives::EthPrimitives; 7 | use reth_node_types::NodePrimitives; 8 | use std::sync::Arc; 9 | use tokio::sync::RwLock; 10 | 11 | #[derive(Clone)] 12 | pub struct AppState { 13 | pub chain_id: ChainId, 14 | pub chain_parameters: ChainParameters, 15 | pub market: Arc>, 16 | pub mempool: Arc>>, 17 | pub account_nonce_and_balance: Arc>, 18 | } 19 | 20 | impl AppState { 21 | pub fn new(chain_id: ChainId) -> Self { 22 | let chain_parameters = ChainParameters::ethereum(); 23 | 24 | Self { 25 | chain_id, 26 | chain_parameters, 27 | market: Arc::new(RwLock::new(Market::default())), 28 | mempool: Arc::new(RwLock::new(Default::default())), 29 | account_nonce_and_balance: Arc::new(RwLock::new(AccountNonceAndBalanceState::new())), 30 | } 31 | } 32 | 33 | pub fn with_chain_parameters(mut self, chain_parameters: ChainParameters) -> Self { 34 | self.chain_parameters = chain_parameters; 35 | self 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /crates/defi/abi/src/lido/steth.rs: -------------------------------------------------------------------------------- 1 | use alloy::sol; 2 | 3 | sol! { 4 | #[derive(Debug, PartialEq, Eq)] 5 | interface IStEth { 6 | event TransferShares( 7 | address indexed from, 8 | address indexed to, 9 | uint256 sharesValue 10 | ); 11 | event SharesBurnt( 12 | address indexed account, 13 | uint256 preRebaseTokenAmount, 14 | uint256 postRebaseTokenAmount, 15 | uint256 sharesAmount 16 | ); 17 | 18 | function totalSupply() external view returns (uint256); 19 | function getTotalPooledEther() external view returns (uint256); 20 | function balanceOf(address _account) external view returns (uint256); 21 | function getTotalShares() external view returns (uint256); 22 | function sharesOf(address _account) external view returns (uint256); 23 | function getSharesByPooledEth(uint256 _ethAmount) public view returns (uint256); 24 | function getPooledEthByShares(uint256 _sharesAmount) public view returns (uint256); 25 | 26 | function submit(address _referral) external payable returns (uint256); 27 | function transferShares(address _recipient, uint256 _sharesAmount) external returns (uint256); 28 | function transferSharesFrom(address _sender, address _recipient, uint256 _sharesAmount) external returns (uint256); 29 | 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /testing/backtest-runner/test_19109955.toml: -------------------------------------------------------------------------------- 1 | # WETH SYM ARB CASE 2 | [modules] 3 | signer = true 4 | flashbots = true 5 | 6 | [settings] 7 | block = 19109955 8 | coinbase = "0x1dd35b4da6534230ff53048f7477f17f7f4e7a70" 9 | multicaller = "0x3dd35b4da6534230ff53048f7477f17f7f4e7a70" 10 | skip_default = false 11 | 12 | [pools] 13 | usdt_flc_uni3 = { address = "0xebce363564fa8b55d85aaf681156087116b148db", class = "uniswap3" } 14 | flc_dai_uni3 = { address = "0x2b2a82d50e6e9d5b95ca644b989f9b143ea9ede2", class = "uniswap3" } 15 | dai_usdt_uin3 = { address = "0x48da0965ab2d2cbf1c17c09cfb5cbe67ad5b1406", class = "uniswap3" } 16 | 17 | weth_syn_uni3 = { address = "0x2dd35b4da6534230ff53048f7477f17f7f4e7a70", class = "uniswap3" } 18 | weth_syn_sushi = { address = "0x4a86c01d67965f8cb3d0aaa2c655705e64097c31", class = "uniswap2" } 19 | 20 | 21 | [txs] 22 | tx_1 = { hash = "0xf9fb98fe76dc5f4e836cdc3d80cd7902150a8609c617064f1447c3980fd6776b", send = "mempool" } 23 | tx_2 = { hash = "0x1ec982c2d4eb5475192b26f7208b797328eab88f8e5be053f797f74bcb87a20c", send = "mempool" } 24 | 25 | [tokens] 26 | weth = { address = "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", symbol = "WETH", decimals = 18, basic = true, middle = false } 27 | usdt = { address = "0xdAC17F958D2ee523a2206206994597C13D831ec7", symbol = "USDT", decimals = 6, basic = true, middle = false } 28 | 29 | 30 | [assertions] 31 | swaps_encoded = 1 32 | swaps_ok = 1 33 | best_profit_eth = 0.0043 34 | -------------------------------------------------------------------------------- /crates/defi/pools/src/protocols/uniswapv2.rs: -------------------------------------------------------------------------------- 1 | use alloy::primitives::{Address, B256, Bytes, b256}; 2 | use alloy::sol_types::SolCall; 3 | use kabu_defi_abi::uniswap2::IUniswapV2Pair; 4 | use kabu_defi_address_book::FactoryAddress; 5 | 6 | use crate::protocols::helper::get_uniswap2pool_address; 7 | use crate::protocols::match_abi; 8 | use crate::protocols::protocol::Protocol; 9 | 10 | pub struct UniswapV2Protocol {} 11 | 12 | impl UniswapV2Protocol { 13 | pub fn is_code(code: &Bytes) -> bool { 14 | match_abi( 15 | code, 16 | vec![ 17 | IUniswapV2Pair::swapCall::SELECTOR, 18 | IUniswapV2Pair::mintCall::SELECTOR, 19 | IUniswapV2Pair::syncCall::SELECTOR, 20 | IUniswapV2Pair::token0Call::SELECTOR, 21 | IUniswapV2Pair::factoryCall::SELECTOR, 22 | ], 23 | ) 24 | } 25 | 26 | pub fn get_pool_address_for_tokens(token0: Address, token1: Address) -> Address { 27 | let init_code: B256 = b256!("96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f"); 28 | get_uniswap2pool_address(token0, token1, FactoryAddress::UNISWAP_V2, init_code) 29 | } 30 | } 31 | 32 | impl Protocol for UniswapV2Protocol { 33 | fn get_pool_address_vec_for_tokens(token0: Address, token1: Address) -> Vec
{ 34 | vec![UniswapV2Protocol::get_pool_address_for_tokens(token0, token1)] 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /crates/rpc/handler/src/openapi.rs: -------------------------------------------------------------------------------- 1 | use crate::dto::block::BlockHeader; 2 | use crate::dto::pool::MarketStats; 3 | use crate::dto::pool::Pool; 4 | use crate::dto::pool::PoolClass; 5 | use crate::dto::pool::PoolDetailsResponse; 6 | use crate::dto::pool::PoolProtocol; 7 | use crate::dto::pool::PoolResponse; 8 | use crate::dto::quote::QuoteRequest; 9 | use crate::dto::quote::QuoteResponse; 10 | use crate::handler::blocks::__path_latest_block; 11 | use crate::handler::pools::__path_market_stats; 12 | use crate::handler::pools::__path_pool; 13 | use crate::handler::pools::__path_pool_quote; 14 | use crate::handler::pools::__path_pools; 15 | use utoipa::OpenApi; 16 | 17 | #[derive(OpenApi)] 18 | #[openapi( 19 | paths(latest_block), 20 | tags( 21 | (name = "block", description = "Blockchain") 22 | ), 23 | components(schemas(BlockHeader)) 24 | )] 25 | pub struct BlockApi; 26 | 27 | #[derive(OpenApi)] 28 | #[openapi( 29 | paths(pool, pools, pool_quote, market_stats), 30 | tags( 31 | (name = "market", description = "Market") 32 | ), 33 | components(schemas(PoolResponse, PoolDetailsResponse, Pool, PoolClass, PoolProtocol, MarketStats, QuoteRequest, QuoteResponse)) 34 | )] 35 | pub struct MarketApi; 36 | 37 | #[derive(OpenApi)] 38 | #[openapi( 39 | nest( 40 | (path = "/api/v1/block/", api = BlockApi), 41 | (path = "/api/v1/markets", api = MarketApi) 42 | ) 43 | )] 44 | pub struct ApiDoc; 45 | -------------------------------------------------------------------------------- /testing/backtest-runner/test_19101578.toml: -------------------------------------------------------------------------------- 1 | #WETH N ARB CASE WITH MULTIPLE STUFFING 2 | [modules] 3 | signer = true 4 | flashbots = true 5 | 6 | 7 | [settings] 8 | block = 19101578 9 | coinbase = "0x1dd35b4da6534230ff53048f7477f17f7f4e7a70" 10 | multicaller = "0x3dd35b4da6534230ff53048f7477f17f7f4e7a70" 11 | 12 | [pools] 13 | weth_n_uni3 = { address = "0x90e7a93e0a6514cb0c84fc7acc1cb5c0793352d2", class = "uniswap3" } 14 | weth_n_uni2 = { address = "0x177a9b475f55b6b7b25204e2562a39308bba2a54", class = "uniswap2" } 15 | 16 | [txs] 17 | 18 | tx_1 = { hash = "0x57593a2ca17101536d5b0a98afa17d5bb24eff8370b4d43859f45c27043184a1", send = "mempool" } 19 | tx_2 = { hash = "0xa77549d2a9fe1e7fcf54619af4f79fd36cdb76f287dfd1926f5d4dca92d7147e", send = "mempool" } 20 | tx_3 = { hash = "0xc8fa479a462b43545fe7dd05b375b6ff57c9d961c76e8955e44b9f604b7e60a4", send = "mempool" } 21 | tx_4 = { hash = "0x46081e7e9feed67e378cf743fb56355ce510441f6dad16f69f47e5dbb13ddd50", send = "mempool" } 22 | tx_5 = { hash = "0x0def9bd26edcd94ad3d9a7269de062d2bf34682f25c2fdcae91360241fd82351", send = "mempool" } 23 | tx_6 = { hash = "0x505ef4f817f97da840ca09a811d2d6a185bbb889f5afb9817ad74dc86b5419f7", send = "mempool" } 24 | 25 | [tokens] 26 | weth = { address = "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", symbol = "WETH", decimals = 18, basic = true, middle = false } 27 | 28 | [assertions] 29 | swaps_encoded = 1 30 | swaps_ok = 1 31 | #best_profit_eth = 0.23 32 | # TODO: Fix merger 33 | best_profit_eth = 0.09 -------------------------------------------------------------------------------- /crates/core/block-history/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "kabu-core-block-history" 3 | edition.workspace = true 4 | exclude.workspace = true 5 | homepage.workspace = true 6 | license.workspace = true 7 | repository.workspace = true 8 | rust-version.workspace = true 9 | version.workspace = true 10 | 11 | [dependencies] 12 | kabu-core-blockchain.workspace = true 13 | kabu-core-components = { workspace = true } 14 | kabu-evm-db.workspace = true 15 | kabu-node-debug-provider.workspace = true 16 | kabu-types-blockchain.workspace = true 17 | kabu-types-entities.workspace = true 18 | kabu-types-events.workspace = true 19 | kabu-types-market.workspace = true 20 | 21 | eyre.workspace = true 22 | futures-util.workspace = true 23 | tokio.workspace = true 24 | tracing.workspace = true 25 | 26 | alloy-network.workspace = true 27 | alloy-primitives.workspace = true 28 | alloy-provider.workspace = true 29 | 30 | reth-chain-state.workspace = true 31 | reth-ethereum-primitives.workspace = true 32 | reth-execution-types.workspace = true 33 | reth-node-types.workspace = true 34 | reth-primitives-traits.workspace = true 35 | reth-rpc-convert.workspace = true 36 | reth-tasks.workspace = true 37 | revm.workspace = true 38 | 39 | [dev-dependencies] 40 | kabu-evm-db.workspace = true 41 | kabu-evm-utils.workspace = true 42 | 43 | alloy-eips.workspace = true 44 | alloy-node-bindings.workspace = true 45 | alloy-rpc-client.workspace = true 46 | env_logger.workspace = true 47 | reth-storage-api.workspace = true 48 | -------------------------------------------------------------------------------- /crates/core/components/src/lib.rs: -------------------------------------------------------------------------------- 1 | mod builder; 2 | mod kabu_builder; 3 | mod kabu_node; 4 | mod node; 5 | mod traits; 6 | 7 | pub use builder::{BuilderContext, Components, ComponentsBuilder, MevComponentChannels, MevComponents, MevComponentsBuilder}; 8 | pub use kabu_builder::{KabuBuilder, KabuBuiltNode, KabuHandle}; 9 | pub use kabu_node::PlaceholderComponent; 10 | pub use node::{DefaultKabuNodeComponentsBuilder, KabuComponentsSet, KabuNode, KabuNodeComponentsBuilder, KabuNodeTypes, WithKabuNode}; 11 | pub use traits::{ 12 | BroadcasterBuilder, ConsensusBuilder, EstimatorBuilder, ExecutorBuilder, HealthMonitorBuilder as HealthMonitorBuilderTrait, 13 | InitializerBuilder, MarketBuilder, MergerBuilder, MevNodeComponentsBuilder, MonitoringBuilder, NetworkBuilder, NodeComponentsBuilder, 14 | PayloadBuilder, PoolBuilder, SignerBuilder as SignerBuilderTrait, StrategyBuilder, WebServerBuilder, 15 | }; 16 | 17 | use eyre::Result; 18 | use reth_tasks::TaskExecutor; 19 | 20 | /// A component is a long-running task that processes data 21 | pub trait Component: Send + 'static { 22 | /// Spawn the component's tasks using the provided executor 23 | fn spawn(self, executor: TaskExecutor) -> Result<()> 24 | where 25 | Self: Sized; 26 | 27 | /// Name of the component for logging 28 | fn name(&self) -> &'static str; 29 | 30 | /// Check if component is ready to start (optional validation) 31 | fn check_readiness(&self) -> Result<()> { 32 | Ok(()) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /crates/defi/abi/src/errors.rs: -------------------------------------------------------------------------------- 1 | use alloy_sol_types::{sol, SolCall, SolInterface}; 2 | 3 | sol!{ 4 | #[derive(Debug, PartialEq, Eq)] 5 | interface IAbiErrors { 6 | error AllowanceExpired(uint256 deadline); 7 | error InsufficientAllowance(uint256 amount); 8 | error ExcessiveInvalidation(); 9 | error ContractLocked(); 10 | error InsufficientToken(); 11 | error InsufficientETH(); 12 | error InvalidBips(); 13 | error InvalidSpender(); 14 | error V3InvalidSwap(); 15 | error V3TooLittleReceived(); 16 | error V3TooMuchRequested(); 17 | error V3InvalidAmountOut(); 18 | error V3InvalidCaller(); 19 | error V2TooLittleReceived(); 20 | error V2TooMuchRequested(); 21 | error V2InvalidPath(); 22 | error ExecutionFailed(uint256 commandIndex, bytes message); 23 | error ETHNotAccepted(); 24 | error UnsafeCast(); 25 | error TransactionDeadlinePassed(); 26 | error FromAddressIsNotOwner(); 27 | error LengthMismatch(); 28 | error UnableToClaim(); 29 | error InvalidCommandType(uint256 commandType); 30 | error BuyPunkFailed(); 31 | error InvalidOwnerERC721(); 32 | error InvalidOwnerERC1155(); 33 | error BalanceTooLow(); 34 | error InvalidReserves(); 35 | error InvalidPath(); 36 | error Error(string); 37 | error Fallback(bytes); 38 | error Unknown(bytes); 39 | } 40 | } 41 | 42 | -------------------------------------------------------------------------------- /book/introduction.md: -------------------------------------------------------------------------------- 1 | # Kabu Documentation 2 | _Documentation for the Kabu MEV bot_ 3 | 4 |
5 | 6 | Kabu Logo 7 | 8 | [![Telegram Chat][tg-badge]][tg-url] 9 | 10 |
11 | 12 | ## What is Kabu? 13 | 14 | Kabu is a backrunning bot, currently under heavy development. It continues the journey of [loom](https://github.com/dexloom/loom). Since then many breaking changes have been made to revm, reth and alloy. The goal here is to make everything work again and modernize the codebase. Currently, Kabu is a work in progress and not yet ready for production use. 15 | 16 | ## Who is Kabu for? 17 | 18 | For everyone that does not like to reinvent the wheel all the time. Have foundation to work with, extend it, rewrite it or use it as playground to learn about MEV and backrunning. 19 | 20 | ## Kabu is opinionated 21 | 22 | - Kabu will only support exex and json-rpc 23 | - We reuse as much as possible from reth, alloy and revm 24 | - We keep as close as possible to the architecture of reth 25 | 26 | ## Why "Kabu"? 27 | 28 | In Japanese, *kabu* (株) means "stock" — both in the financial sense and as a metaphor for growth. 29 | 30 | ## High level architecture 31 | The kabu framework is using the alloy type system and has a deep integration with reth to receive events. 32 | 33 |
34 | 35 | ![High level architecture](images/high_level_architecture.svg) 36 | 37 |
38 | 39 | 40 | [tg-badge]: https://img.shields.io/badge/telegram-kabu-2C5E3D?style=plastic&logo=telegram 41 | [tg-url]: https://t.me/joinkabu -------------------------------------------------------------------------------- /testing/backtest-runner/test_20935488.toml: -------------------------------------------------------------------------------- 1 | # ITO KOBOSU WETH ARB CASE 2 | # replay tx with loom for 0xdc89197a90ba2c9f9a285cc7335b8f0917413d85bebd1764110d786a4753bde2 3 | [modules] 4 | signer = true 5 | flashbots = true 6 | 7 | 8 | [settings] 9 | block = 20935488 10 | coinbase = "0x1dd35b4da6534230ff53048f7477f17f7f4e7a70" 11 | multicaller = "0x3dd35b4da6534230ff53048f7477f17f7f4e7a70" 12 | skip_default = false 13 | # DISABLED: KABOSU/ITO tokens fail with "UniswapV2: TRANSFER_FAILED" - need special transfer method implementation 14 | disabled = true 15 | 16 | [pools] 17 | ito_weth_uni2 = { address = "0x322bba387c825180ebfb62bd8e6969ebe5b5e52d", class = "uniswap2" } 18 | kobosu_ito_uni2 = { address = "0xf382839b955ab57cc1e041f2c987a909c9a48af1", class = "uniswap2" } 19 | kobosu_weth_uni3 = { address = "0x49af5fb5de94c93ee83ad488fe8cab30b0ef35f2", class = "uniswap3" } 20 | 21 | [txs] 22 | # block 20935489 23 | tx_1 = { hash = "0xf9da422be59beb73438586da11601400b66d7b4246d665477f0ebc84646df8d9", send = "mempool" } 24 | 25 | 26 | [tokens] 27 | weth = { address = "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", symbol = "WETH", decimals = 18, basic = true, middle = false } 28 | ito = { address = "0x465dbC39F46f9D43C581a5d90A43e4a0F2A6fF2d", symbol = "ITO", decimals = 9, basic = false, middle = false } 29 | kabosu = { address = "0xceb67a66c2c8a90980da3a50a3f96c07525a26cb", symbol = "KABOSU", decimals = 9, basic = false, middle = false } 30 | 31 | [assertions] 32 | swaps_encoded = 1 33 | # TODO: Fix simulation - getting "UniswapV2: TRANSFER_FAILED" for KABOSU/ITO tokens 34 | swaps_ok = 1 35 | best_profit_eth = 0.0086 -------------------------------------------------------------------------------- /crates/types/entities/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "kabu-types-entities" 3 | version.workspace = true 4 | edition.workspace = true 5 | rust-version.workspace = true 6 | license.workspace = true 7 | homepage.workspace = true 8 | repository.workspace = true 9 | 10 | [dependencies] 11 | kabu-defi-address-book.workspace = true 12 | kabu-evm-db.workspace = true 13 | kabu-evm-utils.workspace = true 14 | kabu-node-debug-provider.workspace = true 15 | kabu-types-blockchain.workspace = true 16 | kabu-types-market.workspace = true 17 | 18 | aes.workspace = true 19 | eyre.workspace = true 20 | hex.workspace = true 21 | indexmap.workspace = true 22 | lazy_static.workspace = true 23 | rand.workspace = true 24 | serde.workspace = true 25 | sha2.workspace = true 26 | thiserror.workspace = true 27 | tokio.workspace = true 28 | toml.workspace = true 29 | tracing.workspace = true 30 | 31 | # alloy 32 | alloy-consensus.workspace = true 33 | alloy-network.workspace = true 34 | alloy-primitives.workspace = true 35 | alloy-provider.workspace = true 36 | alloy-rpc-types.workspace = true 37 | alloy-signer-local.workspace = true 38 | 39 | # reth 40 | reth-ethereum-primitives.workspace = true 41 | reth-node-types.workspace = true 42 | reth-primitives-traits.workspace = true 43 | reth-rpc-convert.workspace = true 44 | 45 | [build-dependencies] 46 | hex.workspace = true 47 | rand.workspace = true 48 | 49 | [dev-dependencies] 50 | alloy-node-bindings.workspace = true 51 | alloy-rpc-client.workspace = true 52 | criterion = { version = "0.5.1", features = ["async_tokio"] } 53 | 54 | [[bench]] 55 | harness = false 56 | name = "benchmark" 57 | -------------------------------------------------------------------------------- /testing/backtest-runner/test_18498188.toml: -------------------------------------------------------------------------------- 1 | #WETH GROK AND WBTC ARB CASE WITH NEW WBTC CRV POOL 2 | # 0x67857131Ae32f72739a2c8d0bd0e812793D8BB24 UNI3 CRV-WBTC Pool created 3 | [modules] 4 | signer = true 5 | flashbots = true 6 | price = true 7 | 8 | 9 | [settings] 10 | block = 18498188 11 | coinbase = "0x1dd35b4da6534230ff53048f7477f17f7f4e7a70" 12 | multicaller = "0x3dd35b4da6534230ff53048f7477f17f7f4e7a70" 13 | skip_default = false 14 | 15 | [pools] 16 | weth_wbtc_uni3 = { address = "0xCBCdF9626bC03E24f779434178A73a0B4bad62eD", class = "uniswap3" } 17 | weth_wbtc_uni2 = { address = "0xbb2b8038a1640196fbe3e38816f3e67cba72d940", class = "uniswap2" } 18 | weth_crv_uni3 = { address = "0x919Fa96e88d67499339577Fa202345436bcDaf79", class = "uniswap3" } 19 | weth_crv_uni2 = { address = "0x3da1313ae46132a397d90d95b1424a9a7e3e0fce", class = "uniswap2" } 20 | 21 | [txs] 22 | 23 | tx_1 = { hash = "0x26177953373b45fa2abac4dee9634c7db65a9e0aaf64b99c7095f51d229f24b7", send = "mempool" } 24 | tx_2 = { hash = "0x1114432ef38437dde663aeb868f553e0ec0ca973120e472687957223efeda331", send = "mempool" } 25 | 26 | [tokens] 27 | weth = { address = "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", symbol = "WETH", decimals = 18, basic = true, middle = false } 28 | wbtc = { address = "0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599", symbol = "WBTC", decimals = 8, basic = true, middle = false } 29 | 30 | [assertions] 31 | #swaps_encoded = 14 32 | #swaps_ok = 11 33 | #best_profit_eth = 181.37 34 | #swaps_encoded = 10 35 | #swaps_ok = 7 36 | #best_profit_eth = 172.78 37 | # TODO: Fix new pool arb timings 38 | swaps_encoded = 1 39 | swaps_ok = 1 40 | best_profit_eth = 8.88 -------------------------------------------------------------------------------- /testing/backtest-runner/src/flashbots_mock.rs: -------------------------------------------------------------------------------- 1 | use alloy_primitives::{B256, Bytes, U64}; 2 | use serde::{Deserialize, Serialize}; 3 | use wiremock::{ 4 | Mock, MockServer, ResponseTemplate, 5 | matchers::{method, path}, 6 | }; 7 | 8 | #[derive(Debug, Deserialize)] 9 | pub struct BundleRequest { 10 | #[allow(dead_code)] 11 | pub jsonrpc: String, 12 | #[allow(dead_code)] 13 | pub id: u64, 14 | #[allow(dead_code)] 15 | pub method: String, 16 | pub params: Vec, 17 | } 18 | 19 | #[derive(Debug, Deserialize)] 20 | #[serde(rename_all = "camelCase")] 21 | pub struct BundleParam { 22 | #[serde(rename = "txs")] 23 | pub transactions: Vec, 24 | 25 | #[allow(dead_code)] 26 | #[serde(rename = "blockNumber")] 27 | pub target_block: Option, 28 | // dropped the rest of the fields 29 | } 30 | 31 | #[derive(Serialize)] 32 | #[serde(rename_all = "camelCase")] 33 | pub struct BundleResponse { 34 | pub bundle_hash: Option, 35 | } 36 | 37 | #[derive(Serialize)] 38 | pub struct SendBundleResponse { 39 | pub jsonrpc: String, 40 | pub id: u64, 41 | pub result: BundleResponse, 42 | } 43 | 44 | pub async fn mount_flashbots_mock(mock_server: &MockServer) { 45 | let bundle_resp = SendBundleResponse { jsonrpc: "2.0".to_string(), id: 1, result: BundleResponse { bundle_hash: Some(B256::ZERO) } }; 46 | 47 | Mock::given(method("POST")) 48 | .and(path("/")) 49 | .respond_with(ResponseTemplate::new(200).set_body_json(&bundle_resp).append_header("content-type", "application/json")) 50 | .mount(mock_server) 51 | .await; 52 | } 53 | -------------------------------------------------------------------------------- /crates/rpc/handler/src/handler/blocks.rs: -------------------------------------------------------------------------------- 1 | use crate::dto::block::BlockHeader; 2 | use axum::Json; 3 | use axum::extract::State; 4 | use axum::http::StatusCode; 5 | use kabu_rpc_state::AppState; 6 | use revm::{DatabaseCommit, DatabaseRef}; 7 | 8 | /// Get latest block 9 | /// 10 | /// Get the latest block header 11 | #[utoipa::path( 12 | get, 13 | path = "latest_block", 14 | tag = "block", 15 | tags = [], 16 | responses( 17 | (status = 200, description = "Todo item created successfully", body = BlockHeader), 18 | ) 19 | )] 20 | pub async fn latest_block( 21 | State(app_state): State>, 22 | ) -> Result, (StatusCode, String)> { 23 | { 24 | // Get latest block from block history 25 | let block_history_guard = app_state.state.block_history(); 26 | let block_history = block_history_guard.read().await; 27 | let latest_block_number = block_history.latest_block_number; 28 | 29 | if let Some(block_hash) = block_history.get_block_hash_for_block_number(latest_block_number) 30 | && let Some(entry) = block_history.get_block_history_entry(&block_hash) 31 | { 32 | let ret = BlockHeader { 33 | number: entry.header.number, 34 | timestamp: entry.header.timestamp, 35 | base_fee_per_gas: entry.header.base_fee_per_gas, 36 | next_block_base_fee: 0, 37 | }; 38 | return Ok(Json(ret)); 39 | } 40 | 41 | Err((StatusCode::INTERNAL_SERVER_ERROR, "No block header found".to_string())) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /book/architecture/state_management.md: -------------------------------------------------------------------------------- 1 | # State management 2 | kabu provides multiple ways to fetch the state and keep it up to date. The state can be fetched using different methods as described in the following section. 3 | 4 | ## Receiving new state 5 | kabu is allow to fetch the state using three different methods: 6 | - **WS/IPC based**: Subscribing to new events using WebSocket or IPC. For each new event a `debug_trace_block` call is made to get the state diff. 7 | - **Direct DB**: Subscribing to new events like before using WebSocket or IPC, but fetching the state diff directly from the DB. 8 | - **ExEx**: Subscribing to new ExEx events and reading the execution outcome from reth. 9 | 10 |
11 | 12 | ![Receiving new state](../images/receive_new_state.svg) 13 | 14 |
15 | 16 | 17 | ## Adding new state to the DB 18 | kabu keeps all required state in-memory and optionally fetches missing state from an external database provider. The `KabuDB` is split in three parts to be efficient cloneable. The first part is mutable where every new or changed state will be added. 19 | 20 | With each new block a background task will be spawned that merges all state to the inner read-only `KabuDB`. This inner `KabuDB` lives inside an `Arc`. The motivation is here to not wait for the merge and save costs for not cloning the whole state all the time. 21 | 22 | The third part in a `DatabaseRef` to an external database provider. This is used to fetch missing state that was not prefetched. Both parts are optional e.g. for testing if the prefetched state is working correct. 23 | 24 |
25 | 26 | ![Receiving new state](../images/kabu_db.svg) 27 | 28 |
-------------------------------------------------------------------------------- /crates/strategy/backrun/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "kabu-strategy-backrun" 3 | edition.workspace = true 4 | exclude.workspace = true 5 | homepage.workspace = true 6 | license.workspace = true 7 | repository.workspace = true 8 | rust-version.workspace = true 9 | version.workspace = true 10 | 11 | [dependencies] 12 | kabu-core-components = { workspace = true } 13 | kabu-defi-abi.workspace = true 14 | kabu-defi-address-book.workspace = true 15 | kabu-defi-pools.workspace = true 16 | kabu-evm-db.workspace = true 17 | kabu-node-debug-provider.workspace = true 18 | kabu-types-blockchain.workspace = true 19 | kabu-types-entities.workspace = true 20 | kabu-types-events.workspace = true 21 | kabu-types-market.workspace = true 22 | kabu-types-swap.workspace = true 23 | 24 | chrono.workspace = true 25 | eyre.workspace = true 26 | influxdb.workspace = true 27 | lazy_static.workspace = true 28 | num_cpus.workspace = true 29 | rayon.workspace = true 30 | reth-node-types.workspace = true 31 | reth-primitives-traits.workspace = true 32 | reth-tasks.workspace = true 33 | revm.workspace = true 34 | serde.workspace = true 35 | tokio.workspace = true 36 | tracing.workspace = true 37 | 38 | # alloy 39 | alloy-consensus.workspace = true 40 | alloy-eips.workspace = true 41 | alloy-evm.workspace = true 42 | alloy-network.workspace = true 43 | alloy-primitives.workspace = true 44 | alloy-provider.workspace = true 45 | alloy-rpc-types.workspace = true 46 | alloy-rpc-types-trace.workspace = true 47 | alloy-sol-types.workspace = true 48 | 49 | [[bench]] 50 | harness = false 51 | name = "swap_calculation_bench" 52 | 53 | [dev-dependencies] 54 | criterion = { version = "0.5.1", features = ["async_tokio"] } 55 | -------------------------------------------------------------------------------- /crates/defi/uniswap-v3-math/src/error.rs: -------------------------------------------------------------------------------- 1 | use alloy::primitives::ruint::ParseError; 2 | use thiserror::Error; 3 | 4 | // TODO: make these errors better, some errors in univ3 libs are just require(condition) without a message. 5 | #[derive(Error, Debug)] 6 | pub enum UniswapV3MathError { 7 | #[error("Denominator is 0")] 8 | DenominatorIsZero, 9 | #[error("Result is U256::MAX")] 10 | ResultIsU256MAX, 11 | #[error("Sqrt price is 0")] 12 | SqrtPriceIsZero, 13 | #[error("Sqrt price is less than or equal to quotient")] 14 | SqrtPriceIsLteQuotient, 15 | #[error("Can not get most significant bit or least significant bit on zero value")] 16 | ZeroValue, 17 | #[error("Liquidity is 0")] 18 | LiquidityIsZero, 19 | //TODO: Update this, shield your eyes for now 20 | #[error("require((product = amount * sqrtPX96) / amount == sqrtPX96 && numerator1 > product);")] 21 | ProductDivAmount, 22 | #[error("Denominator is less than or equal to prod_1")] 23 | DenominatorIsLteProdOne, 24 | #[error("Liquidity Sub")] 25 | LiquiditySub, 26 | #[error("Liquidity Add")] 27 | LiquidityAdd, 28 | #[error("The given tick must be less than, or equal to, the maximum tick")] 29 | T, 30 | #[error("Second inequality must be < because the price can never reach the price at the max tick")] 31 | R, 32 | #[error("Overflow when casting to U160")] 33 | SafeCastToU160Overflow, 34 | #[error("Tick spacing error")] 35 | TickSpacingError, 36 | #[error("Middleware error when getting next_initialized_tick_within_one_word")] 37 | MiddlewareError(String), 38 | #[error("Parse error")] 39 | ParseError(#[from] ParseError), 40 | } 41 | -------------------------------------------------------------------------------- /crates/types/events/src/node.rs: -------------------------------------------------------------------------------- 1 | use crate::Message; 2 | use alloy_consensus::BlockHeader; 3 | use alloy_primitives::TxHash; 4 | use kabu_types_blockchain::{GethStateUpdateVec, MempoolTx}; 5 | use reth_ethereum_primitives::EthPrimitives; 6 | use reth_node_types::NodePrimitives; 7 | 8 | #[derive(Clone, Debug)] 9 | pub struct NodeMempoolDataUpdate { 10 | pub tx_hash: TxHash, 11 | pub mempool_tx: MempoolTx, 12 | } 13 | 14 | #[derive(Clone, Debug)] 15 | pub struct BlockUpdate { 16 | pub block: N::Block, 17 | } 18 | 19 | #[derive(Clone, Debug)] 20 | pub struct BlockStateUpdate { 21 | pub block_header: N::BlockHeader, 22 | pub state_update: GethStateUpdateVec, 23 | } 24 | 25 | #[derive(Clone, Debug)] 26 | pub struct BlockHeaderEventData { 27 | pub header: N::BlockHeader, 28 | pub next_block_number: u64, 29 | pub next_block_timestamp: u64, 30 | } 31 | 32 | pub type MessageMempoolDataUpdate = Message>; 33 | 34 | pub type MessageBlockHeader = Message>; 35 | pub type MessageBlock = Message>; 36 | pub type MessageBlockStateUpdate = Message>; 37 | 38 | impl BlockHeaderEventData 39 | where 40 | N::BlockHeader: BlockHeader, 41 | { 42 | pub fn new(header: N::BlockHeader) -> Self { 43 | let next_block_number = header.number() + 1; 44 | let next_block_timestamp = header.timestamp() + 12; 45 | Self { header, next_block_number, next_block_timestamp } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /crates/execution/multicaller/src/pool_abi_encoder/mod.rs: -------------------------------------------------------------------------------- 1 | use alloy_primitives::{Address, Bytes, U256}; 2 | use kabu_types_market::Pool; 3 | 4 | pub use abi_encoder::*; 5 | mod abi_encoder; 6 | 7 | mod pools; 8 | 9 | pub trait ProtocolAbiSwapEncoderTrait: Send + Sync + 'static { 10 | fn encode_swap_in_amount_provided( 11 | &self, 12 | pool: &dyn Pool, 13 | token_from_address: Address, 14 | token_to_address: Address, 15 | amount: U256, 16 | recipient: Address, 17 | payload: Bytes, 18 | ) -> eyre::Result; 19 | 20 | fn encode_swap_out_amount_provided( 21 | &self, 22 | pool: &dyn Pool, 23 | token_from_address: Address, 24 | token_to_address: Address, 25 | amount: U256, 26 | recipient: Address, 27 | payload: Bytes, 28 | ) -> eyre::Result; 29 | 30 | fn swap_in_amount_offset(&self, pool: &dyn Pool, token_from_address: Address, token_to_address: Address) -> Option; 31 | 32 | fn swap_out_amount_offset(&self, pool: &dyn Pool, token_from_address: Address, token_to_address: Address) -> Option; 33 | 34 | fn swap_out_amount_return_offset(&self, pool: &dyn Pool, token_from_address: Address, token_to_address: Address) -> Option; 35 | 36 | fn swap_in_amount_return_offset(&self, pool: &dyn Pool, token_from_address: Address, token_to_address: Address) -> Option; 37 | 38 | fn swap_out_amount_return_script(&self, pool: &dyn Pool, token_from_address: Address, token_to_address: Address) -> Option; 39 | 40 | fn swap_in_amount_return_script(&self, pool: &dyn Pool, token_from_address: Address, token_to_address: Address) -> Option; 41 | } 42 | -------------------------------------------------------------------------------- /testing/backtest-runner/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "kabu-backtest-runner" 3 | version.workspace = true 4 | edition.workspace = true 5 | rust-version.workspace = true 6 | license.workspace = true 7 | homepage.workspace = true 8 | repository.workspace = true 9 | exclude.workspace = true 10 | 11 | [dependencies] 12 | kabu = { workspace = true, features = ["full-json-rpc", "strategy-full"] } 13 | 14 | chrono.workspace = true 15 | clap.workspace = true 16 | dotenvy.workspace = true 17 | eyre.workspace = true 18 | serde.workspace = true 19 | serde_json.workspace = true 20 | tokio.workspace = true 21 | toml.workspace = true 22 | tracing.workspace = true 23 | tracing-subscriber.workspace = true 24 | wiremock.workspace = true 25 | 26 | # alloy 27 | alloy-consensus.workspace = true 28 | alloy-evm.workspace = true 29 | alloy-network.workspace = true 30 | alloy-primitives.workspace = true 31 | alloy-provider.workspace = true 32 | alloy-rpc-types.workspace = true 33 | alloy-rpc-types-eth.workspace = true 34 | 35 | # reth 36 | reth.workspace = true 37 | reth-ethereum-primitives.workspace = true 38 | reth-execution-types.workspace = true 39 | reth-node-ethereum.workspace = true 40 | reth-primitives.workspace = true 41 | reth-primitives-traits.workspace = true 42 | reth-storage-rpc-provider.workspace = true 43 | reth-tasks.workspace = true 44 | 45 | # revm 46 | revm.workspace = true 47 | 48 | kabu-core-components.workspace = true 49 | kabu-core-node.workspace = true 50 | 51 | [dev-dependencies] 52 | chrono.workspace = true 53 | criterion = { version = "0.5.1", features = ["async_tokio"] } 54 | num_cpus.workspace = true 55 | rand.workspace = true 56 | rayon.workspace = true 57 | 58 | [[bench]] 59 | harness = false 60 | name = "benchmark" 61 | -------------------------------------------------------------------------------- /crates/types/market/src/swap_direction.rs: -------------------------------------------------------------------------------- 1 | use crate::PoolId; 2 | use alloy_primitives::Address; 3 | use std::fmt::Debug; 4 | use std::hash::{DefaultHasher, Hash, Hasher}; 5 | 6 | #[derive(Clone, Debug)] 7 | pub struct SwapDirection { 8 | token_from: Address, 9 | token_to: Address, 10 | } 11 | 12 | impl SwapDirection { 13 | #[inline] 14 | pub fn new(token_from: Address, token_to: Address) -> Self { 15 | Self { token_from, token_to } 16 | } 17 | 18 | #[inline] 19 | pub fn from(&self) -> &Address { 20 | &self.token_from 21 | } 22 | #[inline] 23 | pub fn to(&self) -> &Address { 24 | &self.token_to 25 | } 26 | 27 | #[inline] 28 | pub fn get_hash(&self) -> u64 { 29 | let mut hasher = DefaultHasher::new(); 30 | self.hash(&mut hasher); 31 | hasher.finish() 32 | } 33 | 34 | #[inline] 35 | pub fn get_hash_with_pool(&self, pool_id: &PoolId) -> u64 { 36 | let mut hasher = DefaultHasher::new(); 37 | self.hash(&mut hasher); 38 | pool_id.hash(&mut hasher); 39 | hasher.finish() 40 | } 41 | } 42 | 43 | impl Hash for SwapDirection { 44 | fn hash(&self, state: &mut H) { 45 | self.token_from.hash(state); 46 | self.token_to.hash(state); 47 | } 48 | } 49 | 50 | impl PartialEq for SwapDirection { 51 | fn eq(&self, other: &Self) -> bool { 52 | self.token_from.eq(&other.token_from) && self.token_to.eq(&other.token_to) 53 | } 54 | } 55 | 56 | impl Eq for SwapDirection {} 57 | 58 | impl From<(Address, Address)> for SwapDirection { 59 | fn from(value: (Address, Address)) -> Self { 60 | Self { token_from: value.0, token_to: value.1 } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /crates/types/swap/src/swap_encoder.rs: -------------------------------------------------------------------------------- 1 | use crate::Swap; 2 | use crate::tips::Tips; 3 | use alloy_primitives::{Address, BlockNumber, Bytes, U256}; 4 | use eyre::Result; 5 | use std::ops::Deref; 6 | use std::sync::Arc; 7 | 8 | pub trait SwapEncoder { 9 | /// Encodes Swap 10 | /// 11 | /// - next_block_number - number of the next block 12 | /// - next_block_gas_price - base_fee + priority fee for transaction 13 | /// - sender_address - EOA of of the transaction 14 | /// - sender_eth_balance - balance of EOA 15 | /// 16 | /// returns (to. value, call_data) for transaction 17 | #[allow(clippy::too_many_arguments)] 18 | fn encode( 19 | &self, 20 | swap: Swap, 21 | tips_pct: Option, 22 | next_block_number: Option, 23 | gas_cost: Option, 24 | sender_address: Option
, 25 | sender_eth_balance: Option, 26 | ) -> Result<(Address, Option, Bytes, Vec)> 27 | where 28 | Self: Sized; 29 | 30 | fn set_address(&mut self, address: Address); 31 | 32 | fn address(&self) -> Address; 33 | } 34 | 35 | #[derive(Clone)] 36 | pub struct SwapEncoderWrapper { 37 | pub inner: Arc, 38 | } 39 | 40 | impl SwapEncoderWrapper { 41 | pub fn new(encoder: Arc) -> Self { 42 | SwapEncoderWrapper { inner: encoder } 43 | } 44 | } 45 | 46 | impl From for SwapEncoderWrapper { 47 | fn from(pool: T) -> Self { 48 | Self { inner: Arc::new(pool) } 49 | } 50 | } 51 | 52 | impl Deref for SwapEncoderWrapper { 53 | type Target = dyn SwapEncoder; 54 | fn deref(&self) -> &Self::Target { 55 | self.inner.deref() 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /crates/types/market/src/pool_config.rs: -------------------------------------------------------------------------------- 1 | use crate::PoolClass; 2 | use std::collections::HashMap; 3 | use strum::IntoEnumIterator; 4 | 5 | #[derive(Clone)] 6 | pub struct PoolsLoadingConfig { 7 | threads: Option, 8 | is_enabled: HashMap, 9 | } 10 | 11 | impl PoolsLoadingConfig { 12 | pub fn new() -> Self { 13 | let mut is_enabled = HashMap::new(); 14 | for pool_class in PoolClass::iter() { 15 | is_enabled.insert(pool_class, true); 16 | } 17 | 18 | Self { threads: None, is_enabled } 19 | } 20 | 21 | pub fn disable_all(self) -> Self { 22 | let mut is_enabled = HashMap::new(); 23 | for pool_class in PoolClass::iter() { 24 | is_enabled.insert(pool_class, false); 25 | } 26 | 27 | Self { is_enabled, ..self } 28 | } 29 | 30 | pub fn enable(self, pool_class: PoolClass) -> Self { 31 | let mut is_enabled = self.is_enabled; 32 | is_enabled.insert(pool_class, true); 33 | 34 | Self { is_enabled, ..self } 35 | } 36 | 37 | pub fn disable(self, pool_class: PoolClass) -> Self { 38 | let mut is_enabled = self.is_enabled; 39 | is_enabled.insert(pool_class, true); 40 | 41 | Self { is_enabled, ..self } 42 | } 43 | 44 | pub fn is_enabled(&self, pool_class: PoolClass) -> bool { 45 | self.is_enabled.get(&pool_class).is_some_and(|s| *s) 46 | } 47 | 48 | pub fn with_threads(self, threads: usize) -> Self { 49 | Self { threads: Some(threads), ..self } 50 | } 51 | 52 | pub fn threads(&self) -> Option { 53 | self.threads 54 | } 55 | } 56 | 57 | impl Default for PoolsLoadingConfig { 58 | fn default() -> Self { 59 | PoolsLoadingConfig::new() 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /crates/defi/pools/src/state_readers/erc20.rs: -------------------------------------------------------------------------------- 1 | use alloy::primitives::{Address, U256}; 2 | use alloy::sol_types::{SolCall, SolInterface}; 3 | use alloy_evm::EvmEnv; 4 | use eyre::Result; 5 | use kabu_defi_abi::IERC20; 6 | use kabu_evm_db::KabuDBError; 7 | use kabu_evm_utils::evm_call; 8 | use kabu_types_market::PoolError; 9 | use revm::DatabaseRef; 10 | 11 | pub struct ERC20StateReader {} 12 | 13 | #[allow(dead_code)] 14 | impl ERC20StateReader { 15 | pub fn balance_of + ?Sized>( 16 | db: &DB, 17 | evm_env: &EvmEnv, 18 | erc20_token: Address, 19 | account: Address, 20 | ) -> Result { 21 | let input = IERC20::IERC20Calls::balanceOf(IERC20::balanceOfCall { account }).abi_encode(); 22 | let (call_data_result, _, _) = evm_call(db, evm_env.clone(), erc20_token, input)?; 23 | 24 | let call_return = IERC20::balanceOfCall::abi_decode_returns(&call_data_result) 25 | .map_err(|e| PoolError::AbiDecodingError { method: "balanceOf", source: e })?; 26 | Ok(call_return) 27 | } 28 | 29 | pub fn allowance + ?Sized>( 30 | db: &DB, 31 | evm_env: &EvmEnv, 32 | erc20_token: Address, 33 | owner: Address, 34 | spender: Address, 35 | ) -> Result { 36 | let input = IERC20::IERC20Calls::allowance(IERC20::allowanceCall { owner, spender }).abi_encode(); 37 | let (call_data_result, _, _) = evm_call(db, evm_env.clone(), erc20_token, input)?; 38 | 39 | let call_return = IERC20::allowanceCall::abi_decode_returns(&call_data_result) 40 | .map_err(|e| PoolError::AbiDecodingError { method: "allowance", source: e })?; 41 | Ok(call_return) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /testing/backtest-runner/README.md: -------------------------------------------------------------------------------- 1 | # ARB CASE TESTER WITH ANVIL 2 | 3 | Each test case is described in config file that contains preloading 4 | information and expected calculation results. 5 | 6 | ```toml 7 | # WETH GROK AND WBTC ARB CASE WITH NEW WBTC CRV POOL 8 | # 0x67857131Ae32f72739a2c8d0bd0e812793D8BB24 UNI3 CRV-WBTC Pool created 9 | [modules] 10 | signer = false 11 | price = true 12 | 13 | 14 | [settings] 15 | block = 18498188 16 | coinbase = "0x1dd35b4da6534230ff53048f7477f17f7f4e7a70" 17 | multicaller = "0x3dd35b4da6534230ff53048f7477f17f7f4e7a70" 18 | skip_default = false 19 | 20 | [pools] 21 | weth_wbtc_uni3 = { address = "0xCBCdF9626bC03E24f779434178A73a0B4bad62eD", class = "uniswap3" } 22 | weth_wbtc_uni2 = { address = "0xbb2b8038a1640196fbe3e38816f3e67cba72d940", class = "uniswap2" } 23 | weth_crv_uni3 = { address = "0x919Fa96e88d67499339577Fa202345436bcDaf79", class = "uniswap3" } 24 | weth_crv_uni2 = { address = "0x3da1313ae46132a397d90d95b1424a9a7e3e0fce", class = "uniswap2" } 25 | 26 | [txs] 27 | 28 | tx_1 = { hash = "0x26177953373b45fa2abac4dee9634c7db65a9e0aaf64b99c7095f51d229f24b7", send = "mempool" } 29 | tx_2 = { hash = "0x1114432ef38437dde663aeb868f553e0ec0ca973120e472687957223efeda331", send = "mempool" } 30 | 31 | [tokens] 32 | weth = { address = "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", symbol = "WETH", decimals = 18, basic = true, middle = false } 33 | wbtc = { address = "0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599", symbol = "WBTC", decimals = 8, basic = true, middle = false } 34 | 35 | [results] 36 | swaps_encoded = 14 37 | swaps_ok = 11 38 | best_profit_eth = 181.37 39 | ``` 40 | 41 | Run current available tests 42 | 43 | ```shell 44 | make swap-test-all 45 | ``` 46 | 47 | Run specific 48 | 49 | ```shell 50 | make swap-test FILE= 51 | ``` 52 | 53 | -------------------------------------------------------------------------------- /crates/defi/pools/src/protocols/uniswapv3.rs: -------------------------------------------------------------------------------- 1 | use alloy::primitives::{Address, B256, Bytes, b256}; 2 | use alloy::sol_types::SolCall; 3 | use kabu_defi_abi::uniswap3::IUniswapV3Pool; 4 | use kabu_defi_address_book::FactoryAddress; 5 | 6 | use crate::protocols::helper::get_uniswap3pool_address; 7 | use crate::protocols::match_abi; 8 | use crate::protocols::protocol::Protocol; 9 | 10 | pub struct UniswapV3Protocol {} 11 | 12 | impl UniswapV3Protocol { 13 | pub fn get_pool_address_for_tokens(token0: Address, token1: Address, fee: u32) -> Address { 14 | let init_code: B256 = b256!("e34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b54"); 15 | 16 | get_uniswap3pool_address(token0, token1, fee, FactoryAddress::UNISWAP_V3, init_code) 17 | } 18 | 19 | pub fn is_code(code: &Bytes) -> bool { 20 | match_abi(code, vec![IUniswapV3Pool::swapCall::SELECTOR, IUniswapV3Pool::mintCall::SELECTOR, IUniswapV3Pool::collectCall::SELECTOR]) 21 | } 22 | } 23 | 24 | impl Protocol for UniswapV3Protocol { 25 | fn get_pool_address_vec_for_tokens(token0: Address, token1: Address) -> Vec
{ 26 | let init_code: B256 = "e34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b54".parse().unwrap(); 27 | 28 | let pair_address0 = get_uniswap3pool_address(token0, token1, 100, FactoryAddress::UNISWAP_V3, init_code); 29 | let pair_address1 = get_uniswap3pool_address(token0, token1, 500, FactoryAddress::UNISWAP_V3, init_code); 30 | let pair_address2 = get_uniswap3pool_address(token0, token1, 3000, FactoryAddress::UNISWAP_V3, init_code); 31 | let pair_address3 = get_uniswap3pool_address(token0, token1, 10000, FactoryAddress::UNISWAP_V3, init_code); 32 | 33 | vec![pair_address0, pair_address1, pair_address2, pair_address3] 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /crates/rpc/handler/src/router.rs: -------------------------------------------------------------------------------- 1 | use crate::handler::blocks::latest_block; 2 | use crate::handler::flashbots::flashbots; 3 | use crate::handler::pools::{market_stats, pool, pool_quote, pools}; 4 | use crate::handler::ws::ws_handler; 5 | //use crate::openapi::ApiDoc; 6 | use crate::openapi::ApiDoc; 7 | use axum::Router; 8 | use axum::routing::{get, post}; 9 | use kabu_rpc_state::AppState; 10 | use revm::{DatabaseCommit, DatabaseRef}; 11 | use utoipa::OpenApi; 12 | use utoipa_swagger_ui::SwaggerUi; 13 | //use utoipa::OpenApi; 14 | //use utoipa_swagger_ui::SwaggerUi; 15 | 16 | pub fn router + DatabaseCommit + Sync + Send + Clone + 'static>( 17 | app_state: AppState, 18 | ) -> Router<()> { 19 | Router::new() 20 | .nest( 21 | "/api/v1", 22 | Router::new() 23 | .nest("/block", router_block()) // rename to node 24 | .nest("/markets", router_market()) 25 | .nest("/flashbots", Router::new().route("/", post(flashbots))), 26 | ) 27 | .route("/ws", get(ws_handler)) 28 | .merge(SwaggerUi::new("/swagger-ui").url("/api-docs/openapi.json", ApiDoc::openapi())) 29 | .with_state(app_state) 30 | } 31 | 32 | pub fn router_block() -> Router> { 33 | Router::new().route("/latest_block", get(latest_block)) 34 | } 35 | 36 | pub fn router_market + DatabaseCommit + Sync + Send + Clone + 'static>() 37 | -> Router> { 38 | Router::new() 39 | .route("/pools/:address", get(pool)) 40 | .route("/pools/:address/quote", post(pool_quote)) 41 | .route("/pools", get(pools)) 42 | .route("/", get(market_stats)) 43 | } 44 | -------------------------------------------------------------------------------- /crates/types/entities/build.rs: -------------------------------------------------------------------------------- 1 | use std::env; 2 | use std::fs::File; 3 | use std::io::Write; 4 | use std::path::PathBuf; 5 | 6 | fn main() { 7 | // Tell Cargo to re-run this build script if the environment variable changes 8 | println!("cargo:rerun-if-env-changed=KEY_ENCRYPTION_PWD"); 9 | 10 | // Get the path to src/private.rs 11 | let manifest_dir = env::var("CARGO_MANIFEST_DIR").unwrap(); 12 | let dest_path = PathBuf::from(manifest_dir).join("src").join("private.rs"); 13 | 14 | // Read from environment variable or use default 15 | let key_bytes = if let Ok(key_hex) = env::var("KEY_ENCRYPTION_PWD") { 16 | // Parse hex string into bytes 17 | match hex::decode(&key_hex) { 18 | Ok(bytes) => match bytes.try_into() { 19 | Ok(arr) => arr, 20 | Err(_) => { 21 | println!("cargo:warning=KEY_ENCRYPTION_PWD must be exactly 16 bytes (32 hex characters), using default"); 22 | [0u8; 16] 23 | } 24 | }, 25 | Err(_) => { 26 | println!("cargo:warning=KEY_ENCRYPTION_PWD must be a valid hex string, using default"); 27 | [0u8; 16] 28 | } 29 | } 30 | } else { 31 | // Default key (all zeros) 32 | [0u8; 16] 33 | }; 34 | 35 | // Generate the Rust code 36 | let mut f = File::create(&dest_path).unwrap(); 37 | writeln!( 38 | f, 39 | "// This file is auto-generated by build.rs 40 | // DO NOT EDIT MANUALLY 41 | // To change the key, set the KEY_ENCRYPTION_PWD environment variable (32 hex chars) 42 | 43 | pub const KEY_ENCRYPTION_PWD: [u8; 16] = {key_bytes:?};" 44 | ) 45 | .unwrap(); 46 | 47 | println!("cargo:warning=Generated src/private.rs with KEY_ENCRYPTION_PWD"); 48 | } 49 | -------------------------------------------------------------------------------- /crates/defi/abi/src/maverick/quoter.rs: -------------------------------------------------------------------------------- 1 | use alloy::sol; 2 | 3 | sol! { 4 | 5 | #[derive(Debug, PartialEq, Eq)] 6 | struct BinInfo { 7 | uint128 id; 8 | uint8 kind; 9 | int32 lowerTick; 10 | uint128 reserveA; 11 | uint128 reserveB; 12 | uint128 mergeId; 13 | } 14 | 15 | #[derive(Debug, PartialEq, Eq)] 16 | struct BinState { 17 | uint128 reserveA; 18 | uint128 reserveB; 19 | uint128 mergeBinBalance; 20 | uint128 mergeId; 21 | uint128 totalSupply; 22 | uint8 kind; 23 | int32 lowerTick; 24 | } 25 | 26 | 27 | #[sol(abi=true,rpc)] 28 | #[derive(Debug, PartialEq, Eq)] 29 | interface IMaverickQuoter { 30 | function calculateSwap(address pool, uint128 amount, bool tokenAIn, bool exactOutput, uint256 sqrtPriceLimit) external returns (uint256 returnAmount); 31 | function calculateMultihopSwap(bytes memory path, uint256 amount, bool exactOutput) external returns (uint256 returnAmount); 32 | 33 | function getActiveBins(address pool, uint128 startBinIndex, uint128 endBinIndex) external view returns (BinInfo[] memory bins); 34 | 35 | function getBinDepth(address pool, uint128 binId) external view returns (uint256 depth); 36 | 37 | function getSqrtPrice(address pool) external view returns (uint256 sqrtPrice); 38 | 39 | function getBinsAtTick(address pool, int32 tick) external view returns (BinState[] memory bins); 40 | 41 | function activeTickLiquidity(address pool) external view returns (uint256 sqrtPrice, uint256 liquidity, uint256 reserveA, uint256 reserveB); 42 | 43 | function tickLiquidity(address pool, int32 tick) external view returns (uint256 sqrtPrice, uint256 liquidity, uint256 reserveA, uint256 reserveB); 44 | 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /crates/strategy/backrun/src/affected_pools_logs.rs: -------------------------------------------------------------------------------- 1 | use std::collections::BTreeMap; 2 | use std::sync::Arc; 3 | use tokio::sync::RwLock; 4 | 5 | use alloy_rpc_types::Log; 6 | use alloy_sol_types::SolEventInterface; 7 | use eyre::Result; 8 | 9 | use kabu_defi_abi::uniswap4::IUniswapV4PoolManagerEvents::IUniswapV4PoolManagerEventsEvents; 10 | use kabu_defi_address_book::FactoryAddress; 11 | use kabu_types_market::SwapDirection; 12 | use kabu_types_market::{Market, PoolId, PoolWrapper}; 13 | 14 | #[allow(dead_code)] 15 | pub async fn get_affected_pools_from_logs(market: Arc>, logs: &[Log]) -> Result>> { 16 | let market_guard = market.read().await; 17 | 18 | let mut affected_pools: BTreeMap> = BTreeMap::new(); 19 | 20 | for log in logs.iter() { 21 | if log.address().eq(&FactoryAddress::UNISWAP_V4_POOL_MANAGER_ADDRESS) 22 | && let Some(pool_id) = match IUniswapV4PoolManagerEventsEvents::decode_log(&log.inner) { 23 | Ok(event) => match event.data { 24 | IUniswapV4PoolManagerEventsEvents::Initialize(params) => Some(params.id), 25 | IUniswapV4PoolManagerEventsEvents::ModifyLiquidity(params) => Some(params.id), 26 | IUniswapV4PoolManagerEventsEvents::Swap(params) => Some(params.id), 27 | IUniswapV4PoolManagerEventsEvents::Donate(params) => Some(params.id), 28 | }, 29 | Err(_) => None, 30 | } 31 | && let Some(pool) = market_guard.get_pool(&PoolId::B256(pool_id)) 32 | && !affected_pools.contains_key(pool) 33 | { 34 | affected_pools.insert(pool.clone(), pool.get_swap_directions()); 35 | } 36 | } 37 | 38 | Ok(affected_pools) 39 | } 40 | -------------------------------------------------------------------------------- /config.example.toml: -------------------------------------------------------------------------------- 1 | # === Core Configuration === 2 | 3 | # At least one signer is required 4 | # You can add multiple signers for redundancy 5 | signers = [ 6 | { private_key = "${SIGNER_KEY_1}" }, 7 | #{ private_key = "${SIGNER_KEY_2}" }, 8 | ] 9 | 10 | # MEV builders/relays for bundle submission 11 | builders = [ 12 | { id = 1, name = "flashbots", url = "https://relay.flashbots.net" }, 13 | { id = 2, name = "beaverbuild", url = "https://rpc.beaverbuild.org", no_sign = true }, 14 | { id = 3, name = "titan", url = "https://rpc.titanbuilder.xyz" }, 15 | ] 16 | 17 | # Multicaller contract address for batch operations 18 | multicaller_address = "0x0000000000000000000000000000000000000000" 19 | 20 | # === Services Configuration === 21 | 22 | # Remote node connection (optional, required for remote mode) 23 | [remote_node] 24 | # Both WebSocket and HTTP URLs are required when remote_node is specified 25 | # A full node is sufficient, archive node is only required for tests 26 | ws_url = "ws://localhost:8545" # WebSocket URL for subscriptions 27 | http_url = "http://localhost:8545" # HTTP URL for RPC calls 28 | node = "reth" # Options: geth, reth 29 | 30 | # Optional: Database for persistent storage 31 | [database] 32 | url = "postgres://kabu:kabu@localhost/kabu?application_name=kabu" 33 | 34 | # Optional: Web server for monitoring 35 | [webserver] 36 | host = "0.0.0.0:3333" 37 | 38 | # Optional: InfluxDB for metrics 39 | [influxdb] 40 | url = "http://localhost:8086" 41 | database = "kabu" 42 | tags = { bot_name = "kabu" } 43 | 44 | # === Strategy Configuration === 45 | 46 | # Backrun strategy configuration 47 | [backrun_strategy] 48 | # Optional: specific signer address for this strategy 49 | # If not set, the system will randomly select from available signers 50 | # eoa = "0x..." 51 | smart = true # Enable smart routing optimizations -------------------------------------------------------------------------------- /crates/strategy/backrun/src/affected_pools_state.rs: -------------------------------------------------------------------------------- 1 | use std::collections::BTreeMap; 2 | use std::sync::Arc; 3 | use tokio::sync::RwLock; 4 | 5 | use alloy_primitives::U256; 6 | 7 | use kabu_types_blockchain::GethStateUpdateVec; 8 | use kabu_types_market::SwapDirection; 9 | use kabu_types_market::{Market, PoolId, PoolWrapper}; 10 | use tracing::debug; 11 | 12 | pub async fn get_affected_pools_from_state_update( 13 | market: Arc>, 14 | state_update: &GethStateUpdateVec, 15 | ) -> BTreeMap> { 16 | let market_guard = market.read().await; 17 | 18 | let mut affected_pools: BTreeMap> = BTreeMap::new(); 19 | 20 | for state_update_record in state_update.iter() { 21 | for (address, state_update_entry) in state_update_record.iter() { 22 | if market_guard.is_pool_manager(address) { 23 | for cell in state_update_entry.storage.keys() { 24 | let cell_u: U256 = U256::from_be_slice(cell.as_slice()); 25 | if let Some(pool_id) = market_guard.get_pool_id_for_cell(address, &cell_u) 26 | && let Some(pool) = market_guard.get_pool(pool_id) 27 | && !affected_pools.contains_key(pool) 28 | { 29 | debug!("Affected pool_managers {} pool {} ", address, pool_id); 30 | affected_pools.insert(pool.clone(), pool.get_swap_directions()); 31 | } 32 | } 33 | } else if let Some(pool) = market_guard.get_pool(&PoolId::Address(*address)) 34 | && !affected_pools.contains_key(pool) 35 | { 36 | affected_pools.insert(pool.clone(), pool.get_swap_directions()); 37 | } 38 | } 39 | } 40 | 41 | affected_pools 42 | } 43 | -------------------------------------------------------------------------------- /crates/defi/abi/src/pancake/quoter.rs: -------------------------------------------------------------------------------- 1 | use alloy::sol; 2 | 3 | sol! { 4 | 5 | #[derive(Debug, PartialEq, Eq)] 6 | interface IPancakeQuoterV2 { 7 | 8 | function quoteExactInput(bytes memory path, uint256 amountIn) 9 | external 10 | returns ( 11 | uint256 amountOut, 12 | uint160[] memory sqrtPriceX96AfterList, 13 | uint32[] memory initializedTicksCrossedList, 14 | uint256 gasEstimate 15 | ); 16 | 17 | struct QuoteExactInputSingleParams { 18 | address tokenIn; 19 | address tokenOut; 20 | uint256 amountIn; 21 | uint24 fee; 22 | uint160 sqrtPriceLimitX96; 23 | } 24 | 25 | 26 | function quoteExactInputSingle(QuoteExactInputSingleParams memory params) 27 | external 28 | returns ( 29 | uint256 amountOut, 30 | uint160 sqrtPriceX96After, 31 | uint32 initializedTicksCrossed, 32 | uint256 gasEstimate 33 | ); 34 | 35 | function quoteExactOutput(bytes memory path, uint256 amountOut) 36 | external 37 | returns ( 38 | uint256 amountIn, 39 | uint160[] memory sqrtPriceX96AfterList, 40 | uint32[] memory initializedTicksCrossedList, 41 | uint256 gasEstimate 42 | ); 43 | 44 | struct QuoteExactOutputSingleParams { 45 | address tokenIn; 46 | address tokenOut; 47 | uint256 amount; 48 | uint24 fee; 49 | uint160 sqrtPriceLimitX96; 50 | } 51 | 52 | 53 | function quoteExactOutputSingle(QuoteExactOutputSingleParams memory params) 54 | external 55 | returns ( 56 | uint256 amountIn, 57 | uint160 sqrtPriceX96After, 58 | uint32 initializedTicksCrossed, 59 | uint256 gasEstimate 60 | ); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /crates/core/config/src/config_loader.rs: -------------------------------------------------------------------------------- 1 | use async_trait::async_trait; 2 | use dotenvy::dotenv; 3 | use regex::{Captures, Regex}; 4 | use serde::de::DeserializeOwned; 5 | use std::{env, fs}; 6 | use thiserror::Error; 7 | 8 | #[derive(Debug, Error)] 9 | pub enum LoadConfigError { 10 | #[error("IO error: {0}")] 11 | IoError(#[from] std::io::Error), 12 | #[error("TOML error: {0}")] 13 | TomlError(#[from] toml::de::Error), 14 | #[error("Error loading config: {0}")] 15 | ConfigError(String), 16 | } 17 | 18 | #[async_trait] 19 | pub trait ConfigLoader { 20 | type SectionType; 21 | 22 | async fn load_from_file(file_name: String) -> Result; 23 | } 24 | 25 | pub trait ConfigLoaderSync { 26 | type SectionType; 27 | 28 | fn load_from_file_sync(file_name: String) -> Result; 29 | } 30 | 31 | pub async fn load_from_file(file_name: String) -> Result { 32 | dotenv().ok(); 33 | let contents = tokio::fs::read_to_string(file_name).await?; 34 | let contents = expand_vars(&contents); 35 | let config: T = toml::from_str(&contents)?; 36 | Ok(config) 37 | } 38 | 39 | pub fn load_from_file_sync(file_name: String) -> Result { 40 | dotenv().ok(); 41 | let contents = fs::read_to_string(file_name)?; 42 | let contents = expand_vars(&contents); 43 | let config: T = toml::from_str(&contents)?; 44 | Ok(config) 45 | } 46 | 47 | fn expand_vars(raw_config: &str) -> String { 48 | // Expands ${VAR_NAME} with environment variables 49 | let re = Regex::new(r"\$\{([a-zA-Z_][0-9a-zA-Z_]*)\}").unwrap(); 50 | re.replace_all(raw_config, |caps: &Captures| match env::var(&caps[1]) { 51 | Ok(val) => val, 52 | Err(_) => caps[0].to_string(), 53 | }) 54 | .to_string() 55 | } 56 | -------------------------------------------------------------------------------- /crates/defi/abi/src/uniswap_periphery/quoter.rs: -------------------------------------------------------------------------------- 1 | use alloy::sol; 2 | 3 | sol! { 4 | #[sol(abi = true, rpc)] 5 | #[derive(Debug, PartialEq, Eq)] 6 | interface IQuoterV2 { 7 | function quoteExactInput(bytes memory path, uint256 amountIn) 8 | external 9 | returns ( 10 | uint256 amountOut, 11 | uint160[] memory sqrtPriceX96AfterList, 12 | uint32[] memory initializedTicksCrossedList, 13 | uint256 gasEstimate 14 | ); 15 | 16 | struct QuoteExactInputSingleParams { 17 | address tokenIn; 18 | address tokenOut; 19 | uint256 amountIn; 20 | uint24 fee; 21 | uint160 sqrtPriceLimitX96; 22 | } 23 | 24 | function quoteExactInputSingle(QuoteExactInputSingleParams memory params) 25 | external 26 | returns ( 27 | uint256 amountOut, 28 | uint160 sqrtPriceX96After, 29 | uint32 initializedTicksCrossed, 30 | uint256 gasEstimate 31 | ); 32 | 33 | 34 | function quoteExactOutput(bytes memory path, uint256 amountOut) 35 | external 36 | returns ( 37 | uint256 amountIn, 38 | uint160[] memory sqrtPriceX96AfterList, 39 | uint32[] memory initializedTicksCrossedList, 40 | uint256 gasEstimate 41 | ); 42 | 43 | struct QuoteExactOutputSingleParams { 44 | address tokenIn; 45 | address tokenOut; 46 | uint256 amount; 47 | uint24 fee; 48 | uint160 sqrtPriceLimitX96; 49 | } 50 | 51 | 52 | function quoteExactOutputSingle(QuoteExactOutputSingleParams memory params) 53 | external 54 | returns ( 55 | uint256 amountIn, 56 | uint160 sqrtPriceX96After, 57 | uint32 initializedTicksCrossed, 58 | uint256 gasEstimate 59 | ); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /crates/core/node/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "kabu-core-node" 3 | version.workspace = true 4 | edition.workspace = true 5 | rust-version.workspace = true 6 | license.workspace = true 7 | homepage.workspace = true 8 | repository.workspace = true 9 | 10 | [dependencies] 11 | # Core dependencies 12 | kabu-core-blockchain = { workspace = true } 13 | kabu-core-components = { workspace = true } 14 | kabu-core-config = { workspace = true } 15 | kabu-evm-db = { workspace = true } 16 | kabu-types-blockchain = { workspace = true } 17 | kabu-types-entities = { workspace = true } 18 | kabu-types-events = { workspace = true } 19 | kabu-types-market = { workspace = true } 20 | 21 | # Component implementations 22 | kabu-broadcast-broadcaster = { workspace = true } 23 | kabu-core-block-history = { workspace = true } 24 | kabu-core-router = { workspace = true } 25 | kabu-defi-market = { workspace = true } 26 | kabu-defi-pools = { workspace = true } 27 | kabu-defi-preloader = { workspace = true } 28 | kabu-defi-price = { workspace = true } 29 | kabu-execution-estimator = { workspace = true } 30 | kabu-execution-multicaller = { workspace = true } 31 | kabu-node-debug-provider = { workspace = true } 32 | kabu-storage-db = { workspace = true } 33 | kabu-strategy-backrun = { workspace = true } 34 | kabu-strategy-merger = { workspace = true } 35 | 36 | # Optional dependencies 37 | kabu-defi-health-monitor = { workspace = true, optional = true } 38 | 39 | # External dependencies 40 | alloy-network = { workspace = true } 41 | alloy-primitives = { workspace = true } 42 | alloy-provider = { workspace = true } 43 | eyre = { workspace = true } 44 | reth = { workspace = true } 45 | reth-chain-state = { workspace = true } 46 | reth-ethereum-primitives = { workspace = true } 47 | reth-tasks = { workspace = true } 48 | tokio = { workspace = true } 49 | tracing = { workspace = true } 50 | 51 | [features] 52 | default = ["defi-health-monitor"] 53 | defi-health-monitor = ["dep:kabu-defi-health-monitor"] 54 | -------------------------------------------------------------------------------- /crates/node/exex/src/reth_exex_worker.rs: -------------------------------------------------------------------------------- 1 | use kabu_core_blockchain::Blockchain; 2 | use kabu_types_blockchain::MempoolTx; 3 | use kabu_types_events::{MessageMempoolDataUpdate, NodeMempoolDataUpdate}; 4 | use reth_ethereum_primitives::EthPrimitives; 5 | use reth_transaction_pool::{EthPooledTransaction, TransactionPool}; 6 | use tokio::select; 7 | use tracing::{debug, error, info}; 8 | 9 | pub async fn mempool_worker(mempool: Pool, bc: Blockchain) -> eyre::Result<()> 10 | where 11 | Pool: TransactionPool + Clone + 'static, 12 | { 13 | info!("Mempool worker started"); 14 | let mut tx_listener = mempool.new_transactions_listener(); 15 | 16 | let mempool_tx = bc.new_mempool_tx_channel(); 17 | 18 | // EthTxBuilder removed in new version 19 | 20 | loop { 21 | select! { 22 | tx_notification = tx_listener.recv() => { 23 | if let Some(tx_notification) = tx_notification { 24 | let tx_hash = *tx_notification.transaction.hash(); 25 | // TODO: Implement proper transaction conversion from EthPooledTransaction to alloy RPC Transaction 26 | // For now, we're skipping the transaction details in the mempool update 27 | // This allows the system to track transaction hashes without full transaction data 28 | let update_msg: MessageMempoolDataUpdate = MessageMempoolDataUpdate::new_with_source(NodeMempoolDataUpdate { tx_hash, mempool_tx: MempoolTx { tx: None, ..MempoolTx::default() } }, "exex".to_string()); 29 | if let Err(e) = mempool_tx.send(update_msg) { 30 | error!(error=?e.to_string(), "mempool_tx.send"); 31 | }else{ 32 | debug!(hash = ?tx_notification.transaction.hash(), "Received pool tx"); 33 | } 34 | } 35 | } 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /crates/defi/uniswap-v3-math/src/lib.rs: -------------------------------------------------------------------------------- 1 | use alloy::primitives::U256; 2 | pub mod bit_math; 3 | pub mod error; 4 | pub mod full_math; 5 | pub mod liquidity_math; 6 | pub mod sqrt_price_math; 7 | pub mod swap_math; 8 | pub mod tick; 9 | pub mod tick_bitmap; 10 | pub mod tick_math; 11 | pub mod tick_provider; 12 | pub mod unsafe_math; 13 | 14 | const U256_1: U256 = U256::from_limbs([1, 0, 0, 0]); 15 | const U256_2: U256 = U256::from_limbs([2, 0, 0, 0]); 16 | const U256_3: U256 = U256::from_limbs([3, 0, 0, 0]); 17 | const U256_4: U256 = U256::from_limbs([4, 0, 0, 0]); 18 | const U256_5: U256 = U256::from_limbs([5, 0, 0, 0]); 19 | const U256_6: U256 = U256::from_limbs([6, 0, 0, 0]); 20 | const U256_7: U256 = U256::from_limbs([7, 0, 0, 0]); 21 | const U256_8: U256 = U256::from_limbs([8, 0, 0, 0]); 22 | const U256_15: U256 = U256::from_limbs([15, 0, 0, 0]); 23 | const U256_16: U256 = U256::from_limbs([16, 0, 0, 0]); 24 | const U256_32: U256 = U256::from_limbs([32, 0, 0, 0]); 25 | const U256_64: U256 = U256::from_limbs([64, 0, 0, 0]); 26 | const U256_127: U256 = U256::from_limbs([127, 0, 0, 0]); 27 | const U256_128: U256 = U256::from_limbs([128, 0, 0, 0]); 28 | const U256_255: U256 = U256::from_limbs([255, 0, 0, 0]); 29 | 30 | const U256_256: U256 = U256::from_limbs([256, 0, 0, 0]); 31 | const U256_512: U256 = U256::from_limbs([512, 0, 0, 0]); 32 | const U256_1024: U256 = U256::from_limbs([1024, 0, 0, 0]); 33 | const U256_2048: U256 = U256::from_limbs([2048, 0, 0, 0]); 34 | const U256_4096: U256 = U256::from_limbs([4096, 0, 0, 0]); 35 | const U256_8192: U256 = U256::from_limbs([8192, 0, 0, 0]); 36 | const U256_16384: U256 = U256::from_limbs([16384, 0, 0, 0]); 37 | const U256_32768: U256 = U256::from_limbs([32768, 0, 0, 0]); 38 | const U256_65536: U256 = U256::from_limbs([65536, 0, 0, 0]); 39 | const U256_131072: U256 = U256::from_limbs([131072, 0, 0, 0]); 40 | const U256_262144: U256 = U256::from_limbs([262144, 0, 0, 0]); 41 | const U256_524288: U256 = U256::from_limbs([524288, 0, 0, 0]); 42 | 43 | const U256_MAX_TICK: U256 = U256::from_limbs([887272, 0, 0, 0]); 44 | -------------------------------------------------------------------------------- /testing/backtest-runner/test_20937428.toml: -------------------------------------------------------------------------------- 1 | # WETH -> USDC -> TSUKA -> ODIN -> WBTC -> WETH ARB CASE 2 | # replay tx with loom for 0x840cf4634cb8927ef1aaa00a8680f7ec7147ad27b975d733099b9a732dfd9211 3 | [modules] 4 | signer = true 5 | flashbots = true 6 | 7 | [settings] 8 | block = 20937428 9 | coinbase = "0x1dd35b4da6534230ff53048f7477f17f7f4e7a70" 10 | multicaller = "0x3dd35b4da6534230ff53048f7477f17f7f4e7a70" 11 | # DISABLED: Not finding expected arbitrage opportunities (expects 2, finds 0) - needs investigation 12 | disabled = true 13 | 14 | [pools] 15 | # in order of swaps 16 | usdc_weth_uni3 = { address = "0x88e6a0c2ddd26feeb64f039a2c41296fcb3f5640", class = "uniswap3" } 17 | usdc_tsuka_uni2 = { address = "0x67cea36eeb36ace126a3ca6e21405258130cf33c", class = "uniswap2" } 18 | tsuka_odin_uni3 = { address = "0xc0376b0cbc8e92bcc21e6b9a2f1a2351e714c6e6", class = "uniswap2" } 19 | odin_wbtc_uni3 = { address = "0xad673e520aada71cf73cd8e257a5d50c78eb01a0", class = "uniswap2" } 20 | weth_wbtc_uni2 = { address = "0x4585fe77225b41b697c938b018e2ac67ac5a20c0", class = "uniswap3" } 21 | 22 | 23 | [txs] 24 | # block 20937429 25 | tx_1 = { hash = "0x31f1e81e781e1e1f62f565945c875c6b0756a51d3f7ee5d07cf102af21253014", send = "mempool" } 26 | tx_2 = { hash = "0x2289938db5ba837b2b363c1ae9f739e96b2d576caa1a7d130f3bcdb8d78d5841", send = "mempool" } 27 | 28 | 29 | [tokens] 30 | weth = { address = "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", symbol = "WETH", decimals = 18, basic = true, middle = false } 31 | usdc = { address = "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", symbol = "USDC", decimals = 6, basic = false, middle = false } 32 | tsuka = { address = "0xc5fb36dd2fb59d3b98deff88425a3f425ee469ed", symbol = "TSUKA", decimals = 9, basic = false, middle = false } 33 | odin = { address = "0xdfc5964141c018485b4d017634660f85aa667714", symbol = "ODIN", decimals = 18, basic = false, middle = false } 34 | wbtc = { address = "0x2260fac5e5542a773aa44fbcfedf7c193bc2c599", symbol = "WBTC", decimals = 8, basic = false, middle = false } 35 | 36 | [assertions] 37 | swaps_encoded = 2 38 | swaps_ok = 2 39 | best_profit_eth = 0.00958 40 | -------------------------------------------------------------------------------- /crates/defi/abi/src/uniswap_periphery/custorm_quoter.rs: -------------------------------------------------------------------------------- 1 | use alloy::sol; 2 | 3 | sol! { 4 | #[derive(Debug, PartialEq, Eq)] 5 | interface ICustomQuoter { 6 | function quoteExactInput(bytes memory path, uint256 amountIn) 7 | external 8 | returns ( 9 | uint256 amountOut, 10 | uint160[] memory sqrtPriceX96AfterList, 11 | uint32[] memory initializedTicksCrossedList, 12 | uint256 gasEstimate 13 | ); 14 | 15 | struct QuoteExactInputSingleParams { 16 | address pool; 17 | address tokenIn; 18 | address tokenOut; 19 | uint256 amountIn; 20 | uint24 fee; 21 | uint160 sqrtPriceLimitX96; 22 | } 23 | 24 | 25 | function quoteExactInputSingle(QuoteExactInputSingleParams memory params) 26 | external 27 | returns ( 28 | uint256 amountOut, 29 | uint160 sqrtPriceX96After, 30 | uint32 initializedTicksCrossed, 31 | uint256 gasEstimate 32 | ); 33 | 34 | 35 | function quoteExactOutput(bytes memory path, uint256 amountOut) 36 | external 37 | returns ( 38 | uint256 amountIn, 39 | uint160[] memory sqrtPriceX96AfterList, 40 | uint32[] memory initializedTicksCrossedList, 41 | uint256 gasEstimate 42 | ); 43 | 44 | struct QuoteExactOutputSingleParams { 45 | address pool; 46 | address tokenIn; 47 | address tokenOut; 48 | uint256 amount; 49 | uint24 fee; 50 | uint160 sqrtPriceLimitX96; 51 | } 52 | 53 | function quoteExactOutputSingle(QuoteExactOutputSingleParams memory params) 54 | external 55 | returns ( 56 | uint256 amountIn, 57 | uint160 sqrtPriceX96After, 58 | uint32 initializedTicksCrossed, 59 | uint256 gasEstimate 60 | ); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /crates/evm/utils/src/nweth.rs: -------------------------------------------------------------------------------- 1 | use alloy::primitives::{Address, U256}; 2 | use kabu_defi_address_book::TokenAddressEth; 3 | use std::ops::{Add, Mul}; 4 | 5 | pub struct NWETH {} 6 | 7 | impl NWETH { 8 | const NWETH_EXP_U128: u128 = 10u128.pow(18); 9 | 10 | const NWETH_EXP: f64 = 10u64.pow(18) as f64; 11 | const GWEI_EXP_U128: u128 = 10u128.pow(9); 12 | const GWEI_EXP: f64 = 10u64.pow(9) as f64; 13 | const WEI_EXP_U128: u128 = 10u128.pow(18); 14 | const WEI_EXP: f64 = 10u64.pow(18) as f64; 15 | 16 | pub const ADDRESS: Address = TokenAddressEth::WETH; 17 | pub const NATIVE_ADDRESS: Address = Address::ZERO; 18 | 19 | #[inline] 20 | pub fn to_float(value: U256) -> f64 { 21 | let divider = U256::from(Self::NWETH_EXP); 22 | 23 | let ret = value.div_rem(divider); 24 | 25 | let div = u64::try_from(ret.0); 26 | let rem = u64::try_from(ret.1); 27 | 28 | if div.is_err() || rem.is_err() { 29 | 0f64 30 | } else { 31 | div.unwrap_or_default() as f64 + ((rem.unwrap_or_default() as f64) / Self::NWETH_EXP) 32 | } 33 | } 34 | 35 | #[inline] 36 | pub fn to_float_gwei(value: u128) -> f64 { 37 | let div = value / Self::GWEI_EXP_U128; 38 | let rem = value % Self::GWEI_EXP_U128; 39 | 40 | div as f64 + ((rem as f64) / Self::GWEI_EXP) 41 | } 42 | 43 | #[inline] 44 | pub fn to_float_wei(value: u128) -> f64 { 45 | let div = value / Self::WEI_EXP_U128; 46 | let rem = value % Self::WEI_EXP_U128; 47 | 48 | div as f64 + ((rem as f64) / Self::WEI_EXP) 49 | } 50 | 51 | #[inline] 52 | pub fn from_float(value: f64) -> U256 { 53 | let multiplier = U256::from(value as i64); 54 | let modulus = U256::from(((value - value.round()) * 10_i64.pow(18) as f64) as u64); 55 | multiplier.mul(U256::from(10).pow(U256::from(18))).add(modulus) 56 | } 57 | 58 | #[inline] 59 | pub fn get_exp() -> U256 { 60 | U256::from(Self::NWETH_EXP_U128) 61 | } 62 | 63 | #[inline] 64 | pub fn address() -> Address { 65 | Self::ADDRESS 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /crates/core/blockchain/src/blockchain_state.rs: -------------------------------------------------------------------------------- 1 | use kabu_evm_db::DatabaseKabuExt; 2 | use kabu_types_entities::{BlockHistory, BlockHistoryState}; 3 | use kabu_types_market::MarketState; 4 | use reth_ethereum_primitives::EthPrimitives; 5 | use reth_node_types::NodePrimitives; 6 | use revm::{Database, DatabaseCommit, DatabaseRef}; 7 | use std::sync::Arc; 8 | use tokio::sync::RwLock; 9 | 10 | #[derive(Clone)] 11 | pub struct BlockchainState { 12 | market_state: Arc>>, 13 | block_history_state: Arc>>, 14 | } 15 | 16 | impl< 17 | DB: DatabaseRef + Database + DatabaseCommit + BlockHistoryState + DatabaseKabuExt + Send + Sync + Clone + Default + 'static, 18 | NP: NodePrimitives, 19 | > Default for BlockchainState 20 | { 21 | fn default() -> Self { 22 | Self::new() 23 | } 24 | } 25 | 26 | impl< 27 | DB: DatabaseRef + Database + DatabaseCommit + BlockHistoryState + DatabaseKabuExt + Send + Sync + Clone + Default + 'static, 28 | NP: NodePrimitives, 29 | > BlockchainState 30 | { 31 | pub fn new() -> Self { 32 | BlockchainState { 33 | market_state: Arc::new(RwLock::new(MarketState::new(DB::default()))), 34 | block_history_state: Arc::new(RwLock::new(BlockHistory::::new(10))), 35 | } 36 | } 37 | 38 | pub fn new_with_market_state(market_state: MarketState) -> Self { 39 | Self { market_state: Arc::new(RwLock::new(market_state)), block_history_state: Arc::new(RwLock::new(BlockHistory::new(10))) } 40 | } 41 | 42 | pub fn with_market_state(self, market_state: MarketState) -> BlockchainState { 43 | BlockchainState { market_state: Arc::new(RwLock::new(market_state)), ..self.clone() } 44 | } 45 | } 46 | 47 | impl BlockchainState { 48 | pub fn market_state_commit(&self) -> Arc>> { 49 | self.market_state.clone() 50 | } 51 | 52 | pub fn market_state(&self) -> Arc>> { 53 | self.market_state.clone() 54 | } 55 | 56 | pub fn block_history(&self) -> Arc>> { 57 | self.block_history_state.clone() 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /crates/rpc/handler/src/handler/ws.rs: -------------------------------------------------------------------------------- 1 | use axum::extract::{ConnectInfo, State}; 2 | use axum::{ 3 | extract::ws::{Message, WebSocket, WebSocketUpgrade}, 4 | response::IntoResponse, 5 | }; 6 | 7 | use crate::dto::block::{BlockHeader, WebSocketMessage}; 8 | use kabu_rpc_state::AppState; 9 | use kabu_types_events::MarketEvents; 10 | use revm::{DatabaseCommit, DatabaseRef}; 11 | use std::net::SocketAddr; 12 | use tracing::{error, warn}; 13 | 14 | /// Handle websocket upgrade 15 | pub async fn ws_handler + DatabaseCommit + Send + Sync + Clone + 'static>( 16 | ws: WebSocketUpgrade, 17 | ConnectInfo(addr): ConnectInfo, 18 | State(app_state): State>, 19 | ) -> impl IntoResponse { 20 | ws.on_failed_upgrade(move |e| { 21 | warn!("ws upgrade error: {} with {}", e, addr); 22 | }) 23 | .on_upgrade(move |socket| on_upgrade(socket, addr, app_state)) 24 | } 25 | 26 | /// Actual websocket statemachine (one will be spawned per connection) 27 | async fn on_upgrade( 28 | mut socket: WebSocket, 29 | _who: SocketAddr, 30 | app_state: AppState, 31 | ) { 32 | let mut receiver = app_state.bc.market_events_channel().subscribe(); 33 | 34 | while let Ok(event) = receiver.recv().await { 35 | // Only process BlockHeaderUpdate events for WebSocket streaming 36 | let (block_number, timestamp, base_fee, next_base_fee) = match event { 37 | MarketEvents::BlockHeaderUpdate { block_number, timestamp, base_fee, next_base_fee, .. } => { 38 | (block_number, timestamp, base_fee, next_base_fee) 39 | } 40 | _ => continue, // Skip non-block events 41 | }; 42 | 43 | let ws_msg = WebSocketMessage::BlockHeader(BlockHeader { 44 | number: block_number, 45 | timestamp, 46 | base_fee_per_gas: Some(base_fee), 47 | next_block_base_fee: next_base_fee, 48 | }); 49 | match serde_json::to_string(&ws_msg) { 50 | Ok(json) => { 51 | let _ = socket.send(Message::Text(json.into())).await; 52 | } 53 | Err(e) => { 54 | error!("Failed to serialize block header: {}", e); 55 | } 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /crates/defi/abi/src/multicaller.rs: -------------------------------------------------------------------------------- 1 | use alloy::sol; 2 | 3 | sol! { 4 | #[derive(Debug, PartialEq, Eq)] 5 | struct DyDxAccountInfo { 6 | address owner; // The address that owns the account 7 | uint256 number; // A nonce that allows a single address to control many accounts 8 | } 9 | 10 | 11 | #[sol(abi=true, rpc)] 12 | #[derive(Debug, PartialEq, Eq)] 13 | interface IMultiCaller { 14 | function doCalls(bytes calldata data) external payable returns(uint256); 15 | function uniswapV2Call(address sender, uint amount0, uint amount1, bytes calldata data) external; 16 | function isValidSignature(bytes32, bytes calldata) external view returns (bytes4); 17 | function isValidSignature(bytes calldata, bytes calldata) external view returns (bytes4); 18 | function uniswapV3SwapCallback(int256 , int256 , bytes calldata data) external; 19 | function swapCallback(int256 , int256 , bytes calldata data) external; 20 | function callFunction(address, DyDxAccountInfo memory, bytes calldata data) external; 21 | function receiveFlashLoan(address[] memory,uint256[] memory ,uint256[] memory,bytes calldata) external; 22 | function transferTipsMinBalance(address token, uint256 min_balance, uint256 tips, address owner) external payable; 23 | function transferTipsMinBalanceWETH(uint256 min_balance, uint256 tips,address owner) external payable; 24 | function transferTipsMinBalanceNoPayout(address token, uint256 min_balance, uint256 tips) external payable; 25 | function uni2GetInAmountFrom0(address pool,uint256 amount) external; 26 | function uni2GetInAmountFrom1(address pool,uint256 amount) external; 27 | function uni2GetOutAmountFrom0(address pool,uint256 amount) external; 28 | function uni2GetOutAmountFrom1(address pool,uint256 amount) external; 29 | function uni2GetInAmountFrom0Comms(address pool,uint256 amount, uint256 fee) external; 30 | function uni2GetInAmountFrom1Comms(address pool,uint256 amount, uint256 fee) external; 31 | function uni2GetOutAmountFrom0Comms(address pool,uint256 amount, uint256 fee) external; 32 | function uni2GetOutAmountFrom1Comms(address pool,uint256 amount, uint256 fee) external; 33 | function revertArg(uint256 value) external; 34 | function logArg(uint256 value) external; 35 | function logStackOffset(uint256 offset) external; 36 | function logStack() external; 37 | 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /crates/execution/multicaller/src/pool_abi_encoder/pools/curve.rs: -------------------------------------------------------------------------------- 1 | use crate::pool_abi_encoder::ProtocolAbiSwapEncoderTrait; 2 | use alloy_primitives::{Address, Bytes, U256}; 3 | use eyre::OptionExt; 4 | use kabu_types_market::Pool; 5 | 6 | pub struct CurveProtocolAbiEncoder; 7 | 8 | impl ProtocolAbiSwapEncoderTrait for CurveProtocolAbiEncoder { 9 | fn encode_swap_in_amount_provided( 10 | &self, 11 | pool: &dyn Pool, 12 | token_from_address: Address, 13 | token_to_address: Address, 14 | amount: U256, 15 | recipient: Address, 16 | payload: Bytes, 17 | ) -> eyre::Result { 18 | pool.get_abi_encoder().ok_or_eyre("NO_POOL_ENCODER")?.encode_swap_in_amount_provided( 19 | token_from_address, 20 | token_to_address, 21 | amount, 22 | recipient, 23 | payload, 24 | ) 25 | } 26 | 27 | fn encode_swap_out_amount_provided( 28 | &self, 29 | pool: &dyn Pool, 30 | token_from_address: Address, 31 | token_to_address: Address, 32 | amount: U256, 33 | recipient: Address, 34 | payload: Bytes, 35 | ) -> eyre::Result { 36 | pool.get_abi_encoder().ok_or_eyre("NO_POOL_ENCODER")?.encode_swap_out_amount_provided( 37 | token_from_address, 38 | token_to_address, 39 | amount, 40 | recipient, 41 | payload, 42 | ) 43 | } 44 | 45 | fn swap_in_amount_offset(&self, _pool: &dyn Pool, _token_from_address: Address, _token_to_address: Address) -> Option { 46 | Some(0x44) 47 | } 48 | 49 | fn swap_out_amount_offset(&self, _pool: &dyn Pool, _token_from_address: Address, _token_to_address: Address) -> Option { 50 | None 51 | } 52 | 53 | fn swap_out_amount_return_offset(&self, _pool: &dyn Pool, _token_from_address: Address, _token_to_address: Address) -> Option { 54 | None 55 | } 56 | 57 | fn swap_in_amount_return_offset(&self, _pool: &dyn Pool, _token_from_address: Address, _token_to_address: Address) -> Option { 58 | None 59 | } 60 | 61 | fn swap_out_amount_return_script(&self, _pool: &dyn Pool, _token_from_address: Address, _token_to_address: Address) -> Option { 62 | None 63 | } 64 | 65 | fn swap_in_amount_return_script(&self, _pool: &dyn Pool, _token_from_address: Address, _token_to_address: Address) -> Option { 66 | None 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /crates/storage/db/src/pool.rs: -------------------------------------------------------------------------------- 1 | use diesel::Connection; 2 | use diesel::pg::PgConnection; 3 | use diesel_async::AsyncPgConnection; 4 | use diesel_async::pooled_connection::AsyncDieselConnectionManager; 5 | use diesel_migrations::{EmbeddedMigrations, MigrationHarness}; 6 | use thiserror::Error; 7 | use tracing::info; 8 | 9 | #[derive(Debug, Error)] 10 | pub enum PoolError { 11 | #[error("Failed to create connection pool: {0}")] 12 | PoolError(String), 13 | #[error("Failed to run migrations: {0}")] 14 | MigrationError(String), 15 | } 16 | 17 | pub type DbPool = bb8::Pool>; 18 | 19 | pub async fn init_db_pool_with_migrations(db_url: String, migrations: EmbeddedMigrations) -> Result { 20 | // First, check and run migrations using a synchronous connection 21 | check_and_run_migrations(&db_url, migrations)?; 22 | 23 | // set up connection pool using bb8 with diesel-async manager 24 | let manager = AsyncDieselConnectionManager::::new(db_url); 25 | let pool = bb8::Pool::builder().build(manager).await.map_err(|e| PoolError::PoolError(e.to_string()))?; 26 | Ok(pool) 27 | } 28 | 29 | pub fn check_and_run_migrations(db_url: &str, migrations: EmbeddedMigrations) -> Result<(), PoolError> { 30 | let mut conn = 31 | PgConnection::establish(db_url).map_err(|e| PoolError::MigrationError(format!("Failed to establish connection: {e}")))?; 32 | 33 | info!("Checking and applying database migrations..."); 34 | 35 | // Run migrations - this will check for pending and apply them 36 | match conn.run_pending_migrations(migrations) { 37 | Ok(applied) => { 38 | if applied.is_empty() { 39 | info!("Database is up to date - no pending migrations"); 40 | } else { 41 | info!("Successfully applied {} migration(s):", applied.len()); 42 | for (i, migration) in applied.iter().enumerate() { 43 | info!(" {}. {}", i + 1, migration); 44 | } 45 | } 46 | } 47 | Err(e) => { 48 | return Err(PoolError::MigrationError(format!("Failed to run migrations: {e}"))); 49 | } 50 | } 51 | 52 | // Always show the latest migration 53 | if let Ok(applied_migrations) = conn.applied_migrations() { 54 | if let Some(latest) = applied_migrations.last() { 55 | info!("Latest migration: {}", latest); 56 | } else { 57 | info!("No migrations have been applied yet"); 58 | } 59 | } 60 | 61 | Ok(()) 62 | } 63 | -------------------------------------------------------------------------------- /crates/defi/abi/src/pancake/pool.rs: -------------------------------------------------------------------------------- 1 | use alloy::sol; 2 | 3 | sol! { 4 | #[sol(abi = true, rpc)] 5 | #[derive(Debug, PartialEq, Eq)] 6 | interface IPancakeV3Pool { 7 | 8 | function factory() external view returns (address); 9 | 10 | function token0() external view returns (address); 11 | 12 | function token1() external view returns (address); 13 | 14 | function fee() external view returns (uint24); 15 | 16 | function tickSpacing() external view returns (int24); 17 | 18 | function maxLiquidityPerTick() external view returns (uint128); 19 | 20 | function slot0() 21 | external 22 | view 23 | returns ( 24 | uint160 sqrtPriceX96, 25 | int24 tick, 26 | uint16 observationIndex, 27 | uint16 observationCardinality, 28 | uint16 observationCardinalityNext, 29 | uint32 feeProtocol, 30 | bool unlocked 31 | ); 32 | 33 | function feeGrowthGlobal0X128() external view returns (uint256); 34 | 35 | function feeGrowthGlobal1X128() external view returns (uint256); 36 | 37 | function protocolFees() external view returns (uint128 token0, uint128 token1); 38 | 39 | function liquidity() external view returns (uint128); 40 | 41 | function ticks(int24 tick) 42 | external 43 | view 44 | returns ( 45 | uint128 liquidityGross, 46 | int128 liquidityNet, 47 | uint256 feeGrowthOutside0X128, 48 | uint256 feeGrowthOutside1X128, 49 | int56 tickCumulativeOutside, 50 | uint160 secondsPerLiquidityOutsideX128, 51 | uint32 secondsOutside, 52 | bool initialized 53 | ); 54 | 55 | function tickBitmap(int16 wordPosition) external view returns (uint256); 56 | 57 | function positions(bytes32 key) 58 | external 59 | view 60 | returns ( 61 | uint128 _liquidity, 62 | uint256 feeGrowthInside0LastX128, 63 | uint256 feeGrowthInside1LastX128, 64 | uint128 tokensOwed0, 65 | uint128 tokensOwed1 66 | ); 67 | 68 | function observations(uint256 index) 69 | external 70 | view 71 | returns ( 72 | uint32 blockTimestamp, 73 | int56 tickCumulative, 74 | uint160 secondsPerLiquidityCumulativeX128, 75 | bool initialized 76 | ); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /book/tips_and_tricks/custom_messages.md: -------------------------------------------------------------------------------- 1 | # Custom messages 2 | If you need to add new messages without modifying kabu, you can easily add a custom struct like `Blockchain` to keep the references for states and channels. 3 | 4 | ## Custom Blockchain 5 | ```rust,ignore 6 | pub struct CustomBlockchain { 7 | custom_channel: Broadcaster, 8 | } 9 | 10 | impl CustomBlockchain { 11 | pub fn new() -> Self { 12 | Self { 13 | custom_channel: Broadcaster::new(10), 14 | } 15 | } 16 | pub fn custom_channel(&self) -> Broadcaster { 17 | self.custom_channel.clone() 18 | } 19 | } 20 | ``` 21 | 22 | ## Custom Component 23 | Create a component that can use your custom blockchain: 24 | 25 | ```rust,ignore 26 | pub struct ExampleComponent { 27 | custom_channel_rx: Option>, 28 | } 29 | 30 | impl ExampleComponent { 31 | pub fn new() -> Self { 32 | Self { 33 | custom_channel_rx: None, 34 | } 35 | } 36 | 37 | pub fn on_custom_bc(self, custom_bc: &CustomBlockchain) -> Self { 38 | Self { 39 | custom_channel_rx: Some(custom_bc.custom_channel()), 40 | ..self 41 | } 42 | } 43 | } 44 | 45 | impl Component for ExampleComponent { 46 | fn spawn(self, executor: TaskExecutor) -> Result<()> { 47 | let mut rx = self.custom_channel_rx 48 | .ok_or_else(|| eyre!("custom channel not set"))? 49 | .subscribe(); 50 | 51 | executor.spawn_critical("ExampleComponent", async move { 52 | while let Ok(msg) = rx.recv().await { 53 | // Process custom messages 54 | } 55 | }); 56 | 57 | Ok(()) 58 | } 59 | 60 | fn name(&self) -> &'static str { 61 | "ExampleComponent" 62 | } 63 | } 64 | ``` 65 | 66 | ## Starting Custom Components 67 | When loading your custom component, you can set the custom blockchain: 68 | 69 | ```rust,ignore 70 | let custom_bc = CustomBlockchain::new(); 71 | let executor = TaskExecutor::new(); 72 | 73 | // Create and configure the component 74 | let component = ExampleComponent::new() 75 | .on_custom_bc(&custom_bc); 76 | 77 | // Spawn the component 78 | component.spawn(executor)?; 79 | ``` 80 | 81 | ## Integration with KabuComponentsBuilder 82 | For more complex setups, you can extend the component builder pattern: 83 | 84 | ```rust,ignore 85 | impl KabuComponentsBuilder { 86 | pub fn with_custom_components(self, custom_bc: &CustomBlockchain) -> Self { 87 | let component = ExampleComponent::new() 88 | .on_custom_bc(custom_bc); 89 | 90 | self.add_component(Box::new(component)) 91 | } 92 | } -------------------------------------------------------------------------------- /crates/node/reth-api/src/traits.rs: -------------------------------------------------------------------------------- 1 | use reth_chain_state::CanonStateSubscriptions; 2 | use reth_node_types::{BlockTy, HeaderTy, NodeTypesWithDB, ReceiptTy, TxTy}; 3 | use reth_provider::{ 4 | BlockHashReader, BlockIdReader, BlockNumReader, BlockReader, ChainSpecProvider, DatabaseProviderFactory, HeaderProvider, 5 | NodePrimitivesProvider, ReceiptProvider, StateProviderFactory, TransactionsProvider, 6 | }; 7 | use reth_storage_api::{BlockBodyIndicesProvider, BlockReaderIdExt, CanonChainTracker, ReceiptProviderIdExt}; 8 | use std::fmt::Debug; 9 | 10 | /// A trait that matches exactly what `reth_storage_rpc_provider::RpcBlockchainProvider` implements as minimum requirements. 11 | /// This provides all the necessary blockchain access methods for Kabu components. 12 | pub trait KabuRethFullProvider: 13 | // Block and header reading 14 | BlockHashReader 15 | + BlockNumReader 16 | + BlockIdReader 17 | + HeaderProvider 18 | + BlockBodyIndicesProvider 19 | + BlockReader 20 | + BlockReaderIdExt< 21 | Transaction = TxTy, 22 | Block = BlockTy, 23 | Receipt = ReceiptTy, 24 | Header = HeaderTy, 25 | > 26 | // Receipt and transaction access 27 | + ReceiptProvider 28 | + ReceiptProviderIdExt 29 | + TransactionsProvider 30 | // State access 31 | + StateProviderFactory 32 | + DatabaseProviderFactory 33 | // Chain tracking and subscriptions 34 | + CanonChainTracker 35 | + CanonStateSubscriptions 36 | // Node configuration 37 | + NodePrimitivesProvider 38 | + ChainSpecProvider 39 | // Base requirements 40 | + Clone 41 | + Debug 42 | + Send 43 | + Sync 44 | + 'static 45 | { 46 | } 47 | 48 | /// Blanket implementation for any type that implements all the required traits 49 | impl KabuRethFullProvider for T 50 | where 51 | N: NodeTypesWithDB, 52 | T: BlockHashReader 53 | + BlockNumReader 54 | + BlockIdReader 55 | + HeaderProvider 56 | + BlockBodyIndicesProvider 57 | + BlockReader 58 | + BlockReaderIdExt, Block = BlockTy, Receipt = ReceiptTy, Header = HeaderTy> 59 | + ReceiptProvider 60 | + ReceiptProviderIdExt 61 | + TransactionsProvider 62 | + StateProviderFactory 63 | + DatabaseProviderFactory 64 | + CanonChainTracker 65 | + CanonStateSubscriptions 66 | + NodePrimitivesProvider 67 | + ChainSpecProvider 68 | + Clone 69 | + Debug 70 | + Send 71 | + Sync 72 | + 'static, 73 | { 74 | } 75 | -------------------------------------------------------------------------------- /crates/types/entities/src/datafetcher.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | use std::future::Future; 3 | use std::hash::Hash; 4 | use std::sync::Arc; 5 | 6 | use eyre::Result; 7 | use tokio::sync::RwLock; 8 | use tracing::error; 9 | 10 | use crate::FetchState::Fetching; 11 | 12 | #[derive(Debug, Clone)] 13 | pub enum FetchState { 14 | Fetching(Arc>>), 15 | Ready(T), 16 | } 17 | 18 | #[derive(Debug, Clone, Default)] 19 | pub struct DataFetcher 20 | where 21 | K: Clone + Default + Eq + PartialEq + Hash + Send + Sync + 'static, 22 | V: Clone + Default + Send + Sync + 'static, 23 | { 24 | data: HashMap>, 25 | } 26 | 27 | impl DataFetcher 28 | where 29 | K: Clone + Default + Eq + PartialEq + Hash + Send + Sync + 'static, 30 | V: Clone + Default + Send + Sync + 'static, 31 | { 32 | pub fn new() -> Self { 33 | Self { 34 | //data : Arc::new(RwLock::new(HashMap::new())), 35 | data: HashMap::new(), 36 | } 37 | } 38 | 39 | pub async fn fetch(&mut self, key: K, fx: F) -> FetchState 40 | where 41 | F: FnOnce(K) -> Fut + Send + 'static, 42 | Fut: Future> + Send + 'static, 43 | { 44 | if let Some(x) = self.data.get(&key) { 45 | return x.clone(); 46 | } 47 | 48 | let lock: Arc>> = Arc::new(RwLock::new(None)); 49 | 50 | let lock_clone = lock.clone(); 51 | self.data.insert(key.clone(), Fetching(lock.clone())); 52 | 53 | let (tx, rx) = tokio::sync::oneshot::channel::(); 54 | 55 | tokio::task::spawn(async move { 56 | let mut write_guard = lock_clone.write().await; 57 | if let Err(e) = tx.send(true) { 58 | error!("{}", e) 59 | } 60 | let fetched_data = fx(key).await; 61 | 62 | match fetched_data { 63 | Ok(v) => { 64 | *write_guard = Some(v); 65 | } 66 | _ => { 67 | *write_guard = None; 68 | } 69 | } 70 | }); 71 | 72 | if let Err(e) = rx.await { 73 | error!("{}", e) 74 | }; 75 | Fetching(lock) 76 | } 77 | 78 | pub async fn get(&mut self, key: K, fx: F) -> Result> 79 | where 80 | F: FnOnce(K) -> Fut + Send + 'static, 81 | Fut: Future> + Send + 'static, 82 | { 83 | match self.fetch(key.clone(), fx).await { 84 | Fetching(lock) => { 85 | let ret = lock.read().await; 86 | Ok(ret.clone()) 87 | } 88 | FetchState::Ready(v) => Ok(Some(v)), 89 | } 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /crates/types/entities/src/account_nonce_balance.rs: -------------------------------------------------------------------------------- 1 | use alloy_primitives::{Address, U256}; 2 | use std::collections::HashMap; 3 | 4 | #[derive(Debug, Clone, Default)] 5 | pub struct AccountNonceAndBalances { 6 | nonce: u64, 7 | balance: HashMap, 8 | } 9 | 10 | impl AccountNonceAndBalances { 11 | pub fn new() -> Self { 12 | Self { nonce: 0, balance: HashMap::new() } 13 | } 14 | 15 | pub fn get_nonce(&self) -> u64 { 16 | self.nonce 17 | } 18 | 19 | pub fn set_nonce(&mut self, nonce: u64) -> &mut Self { 20 | self.nonce = nonce; 21 | self 22 | } 23 | 24 | pub fn set_balance(&mut self, token: Address, balance: U256) -> &mut Self { 25 | let entry = self.balance.entry(token).or_default(); 26 | *entry = balance; 27 | self 28 | } 29 | 30 | pub fn add_balance(&mut self, token: Address, balance: U256) -> &mut Self { 31 | let entry = self.balance.entry(token).or_default(); 32 | if let Some(value) = entry.checked_add(balance) { 33 | *entry = value 34 | } 35 | self 36 | } 37 | 38 | pub fn sub_balance(&mut self, token: Address, balance: U256) -> &mut Self { 39 | let entry = self.balance.entry(token).or_default(); 40 | if let Some(value) = entry.checked_sub(balance) { 41 | *entry = value 42 | } 43 | self 44 | } 45 | 46 | pub fn get_eth_balance(&self) -> U256 { 47 | self.balance.get(&Address::default()).cloned().unwrap_or_default() 48 | } 49 | pub fn get_balance(&self, token_address: &Address) -> U256 { 50 | self.balance.get(token_address).cloned().unwrap_or_default() 51 | } 52 | } 53 | 54 | #[derive(Debug, Clone, Default)] 55 | pub struct AccountNonceAndBalanceState { 56 | accounts: HashMap, 57 | } 58 | 59 | impl AccountNonceAndBalanceState { 60 | pub fn new() -> Self { 61 | Self::default() 62 | } 63 | 64 | pub fn add_account(&mut self, account: Address) -> &mut AccountNonceAndBalances { 65 | self.accounts.entry(account).or_default() 66 | } 67 | 68 | pub fn get_account(&self, account: &Address) -> Option<&AccountNonceAndBalances> { 69 | self.accounts.get(account) 70 | } 71 | 72 | pub fn get_mut_account(&mut self, account: &Address) -> Option<&mut AccountNonceAndBalances> { 73 | self.accounts.get_mut(account) 74 | } 75 | 76 | pub fn get_accounts_vec(&self) -> Vec
{ 77 | self.accounts.keys().copied().collect() 78 | } 79 | 80 | pub fn is_monitored(&self, account: &Address) -> bool { 81 | self.accounts.contains_key(account) 82 | } 83 | 84 | pub fn get_entry_or_default(&mut self, account: Address) -> &mut AccountNonceAndBalances { 85 | self.accounts.entry(account).or_default() 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /crates/defi/uniswap-v3-math/src/bit_math.rs: -------------------------------------------------------------------------------- 1 | use crate::error::UniswapV3MathError; 2 | use alloy::primitives::U256; 3 | 4 | pub fn most_significant_bit(x: U256) -> Result { 5 | if x.is_zero() { 6 | return Err(UniswapV3MathError::ZeroValue); 7 | } 8 | Ok(255 - x.leading_zeros() as u8) 9 | } 10 | 11 | pub fn least_significant_bit(x: U256) -> Result { 12 | if x.is_zero() { 13 | return Err(UniswapV3MathError::ZeroValue); 14 | } 15 | Ok(x.trailing_zeros() as u8) 16 | } 17 | 18 | #[cfg(test)] 19 | mod test { 20 | use super::most_significant_bit; 21 | use crate::{U256_1, bit_math::least_significant_bit}; 22 | use alloy::primitives::U256; 23 | use std::str::FromStr; 24 | 25 | #[test] 26 | fn test_most_significant_bit() { 27 | //0 28 | let result = most_significant_bit(U256::ZERO); 29 | assert_eq!(result.unwrap_err().to_string(), "Can not get most significant bit or least significant bit on zero value"); 30 | 31 | //1 32 | let result = most_significant_bit(U256_1); 33 | assert_eq!(result.unwrap(), 0); 34 | 35 | //2 36 | let result = most_significant_bit(U256::from(2)); 37 | assert_eq!(result.unwrap(), 1); 38 | 39 | //all powers of 2 40 | for i in 0..=255 { 41 | let result = most_significant_bit(U256::from(2).pow(U256::from(i))); 42 | assert_eq!(result.unwrap(), i as u8); 43 | } 44 | 45 | //uint256(-1) 46 | let result = most_significant_bit( 47 | //TODO:FIXME: might need to be from dec string 48 | U256::from_str("115792089237316195423570985008687907853269984665640564039457584007913129639935").unwrap(), 49 | ); 50 | assert_eq!(result.unwrap(), 255); 51 | } 52 | 53 | #[test] 54 | fn test_least_significant_bit() { 55 | //0 56 | let result = least_significant_bit(U256::ZERO); 57 | assert_eq!(result.unwrap_err().to_string(), "Can not get most significant bit or least significant bit on zero value"); 58 | 59 | //1 60 | let result = least_significant_bit(U256_1); 61 | assert_eq!(result.unwrap(), 0); 62 | 63 | //2 64 | let result = least_significant_bit(U256::from(2)); 65 | assert_eq!(result.unwrap(), 1); 66 | 67 | //all powers of 2 68 | for i in 0..=255 { 69 | let result = least_significant_bit(U256::from(2).pow(U256::from(i))); 70 | assert_eq!(result.unwrap(), i as u8); 71 | } 72 | 73 | //uint256(-1) 74 | let result = least_significant_bit( 75 | //TODO:FIXME: might need to be from dec string 76 | U256::from_str("115792089237316195423570985008687907853269984665640564039457584007913129639935").unwrap(), 77 | ); 78 | assert_eq!(result.unwrap(), 0); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /crates/evm/db/src/database_helpers.rs: -------------------------------------------------------------------------------- 1 | use crate::fast_cache_db::FastDbAccount; 2 | use alloy::primitives::map::HashMap; 3 | use alloy::primitives::{Address, U256}; 4 | use alloy::rpc::types::trace::geth::AccountState; 5 | use revm::bytecode::Bytecode; 6 | use revm::state::{Account, AccountStatus, EvmStorageSlot}; 7 | use revm::{DatabaseCommit, DatabaseRef}; 8 | use std::collections::BTreeMap; 9 | use tracing::trace; 10 | 11 | pub struct DatabaseHelpers {} 12 | 13 | impl DatabaseHelpers { 14 | #[inline] 15 | pub fn account_db_to_revm(db: FastDbAccount) -> Account { 16 | let storage = db.storage.into_iter().map(|(k, v)| (k, EvmStorageSlot::new(v, 0))).collect(); 17 | Account { info: db.info, storage, status: AccountStatus::empty(), transaction_id: 0 } 18 | } 19 | 20 | #[inline] 21 | pub fn trace_update_to_commit_update(db: &DB, update: BTreeMap) -> HashMap { 22 | let mut result: HashMap = Default::default(); 23 | for (address, state) in update { 24 | trace!(%address, code=state.code.is_some(), storage=state.storage.len(), "trace_update_to_commit_update"); 25 | if address.is_zero() { 26 | continue; 27 | } 28 | let mut info = db.basic_ref(address).map(|a| a.unwrap_or_default()).unwrap_or_default(); 29 | 30 | if let Some(code) = state.code { 31 | let code = Bytecode::new_raw(code); 32 | let hash = code.hash_slow(); 33 | info.code_hash = hash; 34 | info.code = Some(code); 35 | } 36 | 37 | if let Some(nonce) = state.nonce { 38 | info.nonce = nonce 39 | } 40 | 41 | if let Some(balance) = state.balance { 42 | info.balance = balance 43 | } 44 | 45 | let storage = 46 | state.storage.into_iter().map(|(k, v)| (k.into(), EvmStorageSlot::new_changed(U256::ZERO, v.into(), 0))).collect(); 47 | 48 | result.insert(address, Account { info, storage, status: AccountStatus::Touched, transaction_id: 0 }); 49 | } 50 | result 51 | } 52 | 53 | #[inline] 54 | pub fn apply_geth_state_update(db: &mut DB, update: BTreeMap) { 55 | let update = Self::trace_update_to_commit_update(db, update); 56 | db.commit(update); 57 | } 58 | 59 | #[inline] 60 | pub fn apply_geth_state_update_vec(db: &mut DB, update_vec: Vec>) { 61 | let mut update_map: HashMap = Default::default(); 62 | 63 | for update in update_vec { 64 | let update_record = Self::trace_update_to_commit_update(db, update); 65 | update_map.extend(update_record); 66 | } 67 | db.commit(update_map) 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /crates/node/json-rpc/src/wait_for_node_sync_actor.rs: -------------------------------------------------------------------------------- 1 | use alloy_network::Ethereum; 2 | use alloy_provider::Provider; 3 | use alloy_rpc_types::SyncStatus; 4 | use kabu_core_components::Component; 5 | 6 | use eyre::Result; 7 | use kabu_node_debug_provider::DebugProviderExt; 8 | use reth_tasks::TaskExecutor; 9 | use std::time::Duration; 10 | use tokio::time::timeout; 11 | use tracing::{error, info}; 12 | 13 | const SYNC_CHECK_INTERVAL: Duration = Duration::from_secs(1); 14 | const CLIENT_TIMEOUT: Duration = Duration::from_secs(10); 15 | 16 | /// Wait for the node to sync. This works only for http/ipc/ws providers. 17 | async fn wait_for_node_sync_one_shot_worker

(client: P) -> Result<()> 18 | where 19 | P: Provider + DebugProviderExt + Send + Sync + Clone + 'static, 20 | { 21 | info!("Waiting for node to sync..."); 22 | let mut print_count = 0; 23 | loop { 24 | match timeout(CLIENT_TIMEOUT, client.syncing()).await { 25 | Ok(result) => match result { 26 | Ok(syncing_status) => match syncing_status { 27 | SyncStatus::None => { 28 | break; 29 | } 30 | SyncStatus::Info(sync_progress) => { 31 | if print_count == 0 { 32 | info!("Sync progress: {:?}", sync_progress); 33 | } 34 | } 35 | }, 36 | Err(e) => { 37 | error!("Error retrieving syncing status: {:?}", e); 38 | break; 39 | } 40 | }, 41 | Err(elapsed) => { 42 | error!("Timeout during get syncing status. Elapsed time: {:?}", elapsed); 43 | break; 44 | } 45 | } 46 | tokio::time::sleep(SYNC_CHECK_INTERVAL).await; 47 | print_count = if print_count > 4 { 0 } else { print_count + 1 }; 48 | } 49 | Ok(()) 50 | } 51 | 52 | pub struct WaitForNodeSyncOneShotBlockingActor

{ 53 | client: P, 54 | } 55 | 56 | impl

WaitForNodeSyncOneShotBlockingActor

57 | where 58 | P: Provider + DebugProviderExt + Send + Sync + Clone + 'static, 59 | { 60 | pub fn new(client: P) -> WaitForNodeSyncOneShotBlockingActor

{ 61 | WaitForNodeSyncOneShotBlockingActor { client } 62 | } 63 | } 64 | 65 | impl

Component for WaitForNodeSyncOneShotBlockingActor

66 | where 67 | P: Provider + DebugProviderExt + Send + Sync + Clone + 'static, 68 | { 69 | fn spawn(self, executor: TaskExecutor) -> Result<()> { 70 | let client = self.client; 71 | executor.spawn(async move { 72 | if let Err(e) = wait_for_node_sync_one_shot_worker(client).await { 73 | error!("Wait for node sync failed: {}", e); 74 | } 75 | }); 76 | Ok(()) 77 | } 78 | fn name(&self) -> &'static str { 79 | "WaitForNodeSyncOneShotBlockingActor" 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /crates/core/blockchain/src/blockchain_tokens.rs: -------------------------------------------------------------------------------- 1 | use alloy::primitives::ChainId; 2 | use alloy_chains::{Chain, NamedChain}; 3 | use eyre::{OptionExt, Result, eyre}; 4 | use kabu_defi_address_book::{TokenAddressArbitrum, TokenAddressBase, TokenAddressEth}; 5 | use kabu_types_market::{Market, Token}; 6 | 7 | pub fn add_default_tokens_to_market(market: &mut Market, chain_id: ChainId) -> Result<()> { 8 | match Chain::from_id(chain_id).named().ok_or_eyre("NO_NAMED_CHAIN")? { 9 | NamedChain::Mainnet => { 10 | let weth_token = Token::new_with_data(TokenAddressEth::WETH, Some("WETH".to_string()), None, Some(18), true, false); 11 | let usdc_token = Token::new_with_data(TokenAddressEth::USDC, Some("USDC".to_string()), None, Some(6), true, false); 12 | let usdt_token = Token::new_with_data(TokenAddressEth::USDT, Some("USDT".to_string()), None, Some(6), true, false); 13 | let dai_token = Token::new_with_data(TokenAddressEth::DAI, Some("DAI".to_string()), None, Some(18), true, false); 14 | let wbtc_token = Token::new_with_data(TokenAddressEth::WBTC, Some("WBTC".to_string()), None, Some(8), true, false); 15 | let threecrv_token = Token::new_with_data(TokenAddressEth::THREECRV, Some("3Crv".to_string()), None, Some(18), false, true); 16 | 17 | market.add_token(weth_token); 18 | market.add_token(usdc_token); 19 | market.add_token(usdt_token); 20 | market.add_token(dai_token); 21 | market.add_token(wbtc_token); 22 | market.add_token(threecrv_token); 23 | } 24 | NamedChain::Arbitrum => { 25 | let weth_token = Token::new_with_data(TokenAddressArbitrum::WETH, Some("WETH".to_string()), None, Some(18), true, false); 26 | let wbtc_token = Token::new_with_data(TokenAddressArbitrum::WBTC, Some("WBTC".to_string()), None, Some(8), true, false); 27 | let usdc_token = Token::new_with_data(TokenAddressArbitrum::USDC, Some("USDC".to_string()), None, Some(6), true, false); 28 | let usdt_token = Token::new_with_data(TokenAddressArbitrum::USDT, Some("USDT".to_string()), None, Some(6), true, false); 29 | let dai_token = Token::new_with_data(TokenAddressEth::DAI, Some("DAI".to_string()), None, Some(18), true, false); 30 | 31 | market.add_token(weth_token); 32 | market.add_token(wbtc_token); 33 | market.add_token(usdc_token); 34 | market.add_token(usdt_token); 35 | market.add_token(dai_token); 36 | } 37 | NamedChain::Base => { 38 | let weth_token = Token::new_with_data(TokenAddressBase::WETH, Some("WETH".to_string()), None, Some(18), true, false); 39 | let usdc_token = Token::new_with_data(TokenAddressBase::USDC, Some("USDC".to_string()), None, Some(6), true, false); 40 | 41 | market.add_token(weth_token); 42 | market.add_token(usdc_token); 43 | } 44 | _ => return Err(eyre!("CHAIN_TOKENS_NOT_LOADED")), 45 | } 46 | Ok(()) 47 | } 48 | --------------------------------------------------------------------------------