diff --git a/src/api/api_handlers_common.ml b/src/api/api_handlers_common.ml index fe9604dfc9bc272563d4d7a27135e80ee7b522bf..1b3acad5021a64de7f097634733e7a34a5d54a6d 100644 --- a/src/api/api_handlers_common.ml +++ b/src/api/api_handlers_common.ml @@ -430,3 +430,18 @@ let rec update_cache_eth_loop node_or_db update_cache_aux let> _ = Lwt_unix.sleep 1. in update_cache_eth_loop node_or_db update_cache_aux update_analyses_cache_aux get_latest_block_number cache ws_node_mutex latest_block_number_cache + +let filter_analyses analyses = + List.filter + (fun analysis -> + match analysis with + | A_Sandwich _ -> true + | A_Sandwich_liquidity _ -> true + | A_FlashLoan a -> List.length a.txao_output > 0 + | A_Liquidate a -> List.length a.txao_output > 0 + | A_Transfer _ -> true + | A_Arbitrage a -> ( + match a.txao_output with + | [] -> false + | output :: _ -> Option.is_some output.txaio_resume.cftao_arbitrage)) + analyses diff --git a/src/api/api_handlers_db.ml b/src/api/api_handlers_db.ml index 2f923f001172c9da258b691be6b34119c649f615..0a6b5331aa40b09b57158e2b0ec09c46c4453be7 100644 --- a/src/api/api_handlers_db.ml +++ b/src/api/api_handlers_db.ml @@ -806,6 +806,7 @@ let update_analyses_cache_db_eth_aux () = match analyis with | A_FlashLoan a | A_Liquidate a -> a.txao_block_number | A_Sandwich s -> s.bao_block_number + | A_Sandwich_liquidity s -> s.bao_block_number | A_Transfer t -> t.txao_block_number | A_Arbitrage a -> a.txao_block_number in let old_analyses_opt = Live_analyses_map.find_opt block_number acc in diff --git a/src/common/types.ml b/src/common/types.ml index 46a64c8d1fb190177dd1b0c372aa5d049f5e8dcf..91e83a588144fadbfd7755ccb0a0d65893d775d6 100644 --- a/src/common/types.ml +++ b/src/common/types.ml @@ -411,14 +411,26 @@ end module Ethereum_analysis = struct open Ethereum_decode + type z = Z.t + + let z_enc = + let open Json_encoding in + union + [ + case string (fun z -> Some (Z.format "%d" z)) Z.of_string; + case int53 (fun _ -> None) Z.of_int64; + ] + + let equal_z = Z.equal + type swap = { - s_amountIn : bz; - s_amountOut : bz; - s_in_token : int; - s_out_token : int; + s_amountIn : z; + s_amountOut : z; + s_in_token : contract_information; + s_out_token : contract_information; s_sender : known_address_or_address; s_recipient : known_address_or_address; - s_contract : known_address_or_address; + s_contract : contract_information; } [@@deriving encoding { camel; remove_prefix = "s_" }, eq] @@ -439,46 +451,214 @@ module Ethereum_analysis = struct [@@deriving encoding { camel; remove_prefix = "st_" }, eq] type uni_v2_swap = { + uv2s_index : index_or_position; uv2s_contract : contract_information; uv2s_sender : known_address_or_address; uv2s_recipient : known_address_or_address; - uv2s_amount0In : bz; - uv2s_amount1In : bz; - uv2s_amount0Out : bz; - uv2s_amount1Out : bz; - uv2s_token0 : int; - uv2s_token1 : int; + uv2s_amount0In : z; + uv2s_amount1In : z; + uv2s_amount0Out : z; + uv2s_amount1Out : z; + uv2s_token0 : contract_information; + uv2s_token1 : contract_information; } - [@@deriving encoding { camel; remove_prefix = "uv2s_" }] + [@@deriving encoding { camel; remove_prefix = "uv2s_" }, eq] type uni_v3_swap = { + uv3s_index : index_or_position; uv3s_contract : contract_information; uv3s_sender : known_address_or_address; uv3s_recipient : known_address_or_address; - uv3s_amount0 : bz; - uv3s_amount1 : bz; - uv3s_sqrt_price : bz; - uv3s_liquidity : bz; - uv3s_tick : bz; + uv3s_amount0 : z; + uv3s_amount1 : z; + uv3s_sqrt_price : z; + uv3s_liquidity : z; + uv3s_tick : z; uv3s_token0 : contract_information; uv3s_token1 : contract_information; } - [@@deriving encoding { camel; remove_prefix = "uv3s_" }] + [@@deriving encoding { camel; remove_prefix = "uv3s_" }, eq] + + type uni_v3_mint = { + uv3m_victim : uni_v3_swap list; + uv3m_index : index_or_position; + uv3m_contract : contract_information; + uv3m_sender : known_address_or_address; + uv3m_owner : known_address_or_address; + uv3m_token0 : contract_information; + uv3m_token1 : contract_information; + uv3m_amount : z; + uv3m_amount0 : z; + uv3m_amount1 : z; + uv3m_tick_upper : z; + uv3m_tick_lower : z; + } + [@@deriving encoding { camel; remove_prefix = "uv3m_" }, eq] + + type uni_v3_burn = { + uv3b_victim : uni_v3_swap list; + uv3b_index : index_or_position; + uv3b_contract : contract_information; + uv3b_owner : known_address_or_address; + uv3b_tick_lower : z; + uv3b_tick_upper : z; + uv3b_amount : z; + uv3b_amount0 : z; + uv3b_amount1 : z; + uv3b_token0 : contract_information; + uv3b_token1 : contract_information; + } + [@@deriving encoding { camel; remove_prefix = "uv3b_" }, eq] + + type uni_v3_collect = { + uv3c_index : index_or_position; + uv3c_contract : contract_information; + uv3c_recipient : known_address_or_address; + uv3c_owner : known_address_or_address; + uv3c_tick_upper : z; + uv3c_tick_lower : z; + uv3c_amount0 : z; + uv3c_amount1 : z; + uv3c_token0 : contract_information; + uv3c_token1 : contract_information; + } + [@@deriving encoding { camel; remove_prefix = "uv3c_" }, eq] type token_ratio_swap = { trs_swap_transaction : swap_transaction; - trs_ratio : (bz * bz) list; + trs_ratio : (z * z) list; } [@@deriving encoding { camel; remove_prefix = "trs_" }, eq] + type token_profit = { + tp_token : contract_information; + tp_profit : z; + } + [@@deriving encoding { camel; remove_prefix = "tp_" }, eq] + type swap_transaction_output = { sto_transaction_hash : b; - sto_index : bint; + sto_index : int; sto_sender : known_address_or_address; sto_swap_list : swap list; } [@@deriving encoding { camel; remove_prefix = "sto_" }, eq] + type swap_transaction_mint = { + stm_transaction_hash : b; + stm_index : int; + stm_sender : known_address_or_address; + stm_mint_list : uni_v3_mint list; + stm_victim_list : swap_transaction_output; + } + [@@deriving encoding { camel; remove_prefix = "stm_" }, eq] + + type swap_transaction_burn = { + stb_transaction_hash : b; + stb_index : int; + stb_sender : known_address_or_address; + stb_burn_list : uni_v3_burn list; + stb_victim_list : swap_transaction_output; + } + [@@deriving encoding { camel; remove_prefix = "stb_" }, eq] + + type swap_transaction_collect = { + stc_transaction_hash : b; + stc_index : int; + stc_sender : known_address_or_address; + stc_collect_list : uni_v3_collect list; + } + [@@deriving encoding { camel; remove_prefix = "stc_" }, eq] + + type events = + [ `swapV2 of uni_v2_swap list + | `swapV3 of uni_v3_swap list + | `mint of uni_v3_mint list + | `burn of uni_v3_burn list + | `collect of uni_v3_collect list ] + [@@deriving encoding { camel }] + + type swap_transaction_mint_aggregated = { + stma_transaction_hash : b; + stma_index : int; + stma_sender : known_address_or_address; + stma_contract : contract_information; + stma_tick_lower : z; + stma_tick_upper : z; + stma_mint_list : swap_transaction_mint; + } + [@@deriving encoding { camel; remove_prefix = "stma_" }, eq] + + type swap_transaction_burn_aggregated = { + stba_transaction_hash : b; + stba_index : int; + stba_sender : known_address_or_address; + stba_contract : contract_information; + stba_tick_lower : z; + stba_tick_upper : z; + stba_burn_list : swap_transaction_burn; + } + [@@deriving encoding { camel; remove_prefix = "stba_" }, eq] + + type swap_transaction_collect_aggregated = { + stca_transaction_hash : b; + stca_index : int; + stca_sender : known_address_or_address; + stca_contract : contract_information; + stca_tick_lower : z; + stca_tick_upper : z; + stca_collect_list : uni_v3_collect list; + } + [@@deriving encoding { camel; remove_prefix = "stca_" }, eq] + + type burn_collect_aggregated = { + bca_transaction_hash : b; + bca_index : int; + bca_sender : known_address_or_address; + bca_contract : contract_information; + bca_tick_lower : z; + bca_tick_upper : z; + bca_burn_list : swap_transaction_burn_aggregated; + bca_collect_list : swap_transaction_collect_aggregated option; + } + [@@deriving encoding { camel; remove_prefix = "bca_" }, eq] + + type sandwich_liquidity_possible = { + slp_mint : swap_transaction_mint_aggregated; + slp_burn_collect : burn_collect_aggregated list; + } + [@@deriving encoding { camel; remove_prefix = "slp_" }, eq] + + type token_liquidity = { + tl_amount0 : z; + tl_amount1 : z; + tl_token0 : contract_information; + tl_token1 : contract_information; + } + [@@deriving encoding { camel; remove_prefix = "tl_" }, eq] + + type token_liquidity_pool = { + tlp_amount : z; + tlp_liquidity : token_liquidity; + } + [@@deriving encoding { camel; remove_prefix = "tlp_" }, eq] + + type sandwich_liquidity_burn_collect = { + slbc_burn : token_liquidity_pool; + slbc_collect : token_liquidity; + } + [@@deriving encoding { camel; remove_prefix = "slbc_" }, eq] + + type sandwich_liquidity = { + sl_mint : swap_transaction_mint_aggregated; + sl_burn_collect : burn_collect_aggregated list; + sl_victim : swap_transaction_output list; + sl_profit : token_liquidity; + sl_mint_liquidity : token_liquidity_pool; + sl_burn_collect_liquidity : sandwich_liquidity_burn_collect list; + } + [@@deriving encoding { camel; remove_prefix = "sl_" }, eq] + type sandwich_possible = { sdp_front_run : swap_transaction_output; sdp_back_run : swap_transaction_output; @@ -486,28 +666,10 @@ module Ethereum_analysis = struct } [@@deriving encoding { camel; remove_prefix = "sdp_" }, eq] - type z = Z.t - - let z_enc = - let open Json_encoding in - union - [ - case string (fun z -> Some (Z.format "%d" z)) Z.of_string; - case int53 (fun _ -> None) Z.of_int64; - ] - - let equal_z = Z.equal - (* A sandwich is defined for a unique token_swap. We can have multiple sandwich with the same victim, front run and back run, but with different token_swap.*) - type token_profit = { - tp_token : contract_information option; - tp_profit : z; - } - [@@deriving encoding { camel; remove_prefix = "tp_" }, eq] - type sandwich_output = { sdo_front_run : swap_transaction_output; sdo_back_run : swap_transaction_output; @@ -733,12 +895,31 @@ module Ethereum_analysis = struct type transfer_output = transfer_analysis list [@@deriving encoding { camel }, eq] + type sandwich = + [ `sandwich of sandwich_output + | `sandwich_liquidity of sandwich_liquidity ] + [@@deriving encoding { camel }, eq] + + type merged_per_contract = { + mpc_contract : contract_information; + mpc_sandwich_list : sandwich list; + mpc_profit : token_profit list; + } + [@@deriving encoding { camel; remove_prefix = "mpc_" }, eq] + + type merged_per_user = { + mpu_sender : known_address_or_address; + mpu_per_contract : merged_per_contract list; + } + [@@deriving encoding { camel; remove_prefix = "mpu_" }, eq] + type bottom = | [@@deriving encoding] type +'a eth_analysis_type = [ `liquidate | `flashloan | `sandwich + | `sandwich_liquidity | `transfer | `arbitrage | `bottom of bottom [@encoding.skip] ] @@ -825,6 +1006,8 @@ module Ethereum_analysis = struct | A_Transfer of transfer_output tx_analysis_output [@wrap "value"] | A_Sandwich of sandwich_output blk_analysis_output [@wrap "value"] | A_Arbitrage of cft_arbitrage_output tx_analysis_output [@wrap "value"] + | A_Sandwich_liquidity of sandwich_liquidity blk_analysis_output + [@wrap "value"] [@@deriving encoding { remove_prefix = "A_"; camel; kind = "type"; union }, eq] diff --git a/src/ethereum_analysis/dune b/src/ethereum_analysis/dune index 984f0ff3903b0cb70e23d157fc0727db3cdf54d8..e582269f6f87ea99948fe952feefef5cf36a531a 100644 --- a/src/ethereum_analysis/dune +++ b/src/ethereum_analysis/dune @@ -2,6 +2,7 @@ (name eth_analysis) (modules eth_analysis_sandwich + eth_analysis_sandwich_liquidity eth_analysis_detect_pool_creation eth_analyse eth_graph diff --git a/src/ethereum_analysis/eth_analysis_detect_pool_creation.ml b/src/ethereum_analysis/eth_analysis_detect_pool_creation.ml index 2edc352dec43cc18995b8cc3f751d49d76a816ba..265c6b2a0d49793e6d8c5d0125ef4620db815868 100644 --- a/src/ethereum_analysis/eth_analysis_detect_pool_creation.ml +++ b/src/ethereum_analysis/eth_analysis_detect_pool_creation.ml @@ -2,6 +2,7 @@ open Common open Types.Ethereum_analysis open Types.Ethereum_decode open Olympus.Types +open Eth module Pool_param = Map.Make (String) let dir_map_hashes = Common.Env.get_env Common.Env.hashes_var diff --git a/src/ethereum_analysis/eth_analysis_sandwich.ml b/src/ethereum_analysis/eth_analysis_sandwich.ml index 7e07977375114e3c7e23a436ab1de711077612cf..1a112aec830ac9fe6c1f0330dd5a6f3737d83dbf 100644 --- a/src/ethereum_analysis/eth_analysis_sandwich.ml +++ b/src/ethereum_analysis/eth_analysis_sandwich.ml @@ -1,210 +1,21 @@ -open Eth open Common open Types.Ethereum_analysis -open Types.Ethereum_decode -open Tools open Olympus.Types - -let eq_inverse_token_swapped ts1 ts2 = - ts1.ts_out_token = ts2.ts_in_token - && ts1.ts_in_token = ts2.ts_out_token - && ts1.ts_contract = ts2.ts_contract - -let eq_token_swapped ts1 ts2 = - ts1.ts_in_token = ts2.ts_in_token - && ts1.ts_out_token = ts2.ts_out_token - && ts1.ts_contract = ts2.ts_contract - -let list_uniq_token_swapped list = - List.fold_left - (fun acc arg -> - match List.find_opt (eq_token_swapped arg) acc with - | None -> arg :: acc - | Some _ -> acc) - [] list - -let filter_swap token_swapped swap_list = - List.filter - (fun s -> - Int.equal s.s_in_token token_swapped.ts_in_token - && Int.equal s.s_out_token token_swapped.ts_out_token) - swap_list - -let v2_event_id = - "d78ad95fa46c994b6551d0da85fc275fe613ce37657fb8d5e3d130840159d822" - -let v3_event_id = - "c42079f94a6350d7e6235f29174924f928cc2ac818eb64fed8004e115fbcca67" - -let get_int_from_evmvalue (contract_or_evm_value : known_address_or_evm_value) = - match contract_or_evm_value with - | Coev_evm_value evm_value -> ( - match evm_value with - | `int int -> int - | e -> - Log.log_error_fail ~here:[%here] - @@ Format.sprintf "fail get_int_from_evmvalue %s" - @@ EzEncoding.construct Eth.evm_value_enc e) - | _ -> - Log.log_error_fail ~here:[%here] - @@ Format.sprintf "fail get_int_from_evmvalue %s" - @@ EzEncoding.construct known_address_or_evm_value_enc contract_or_evm_value - -let decode_abi_v2 event_hash abi s_contract = - try - match String.equal event_hash v2_event_id with - | false -> None - | true -> - let inputs = - List.sort - (fun arg1 arg2 -> compare arg1.pda_index arg2.pda_index) - abi.evdai_inputs in - let s_sender = - let tmp = List.nth inputs 0 in - known_address_or_evm_value_to_known_address_or_address tmp.pda_data - in - let uv2s_amount0In = - let tmp = List.nth inputs 1 in - get_int_from_evmvalue tmp.pda_data in - let uv2s_amount1In = - let tmp = List.nth inputs 2 in - get_int_from_evmvalue tmp.pda_data in - let uv2s_amount0Out = - let tmp = List.nth inputs 3 in - get_int_from_evmvalue tmp.pda_data in - let uv2s_amount1Out = - let tmp = List.nth inputs 4 in - get_int_from_evmvalue tmp.pda_data in - let s_recipient = - let tmp = List.nth inputs 5 in - known_address_or_evm_value_to_known_address_or_address tmp.pda_data - in - let s_amountIn, s_in_token = - match Z.compare uv2s_amount0In uv2s_amount1In with - | -1 -> (uv2s_amount1In, 1) - | 1 -> (uv2s_amount0In, 0) - | _ -> assert false in - let s_amountOut, s_out_token = - match Z.compare uv2s_amount0Out uv2s_amount1Out with - | -1 -> (uv2s_amount1Out, 1) - | 1 -> (uv2s_amount0Out, 0) - | _ -> assert false in - Some - { - s_amountIn; - s_amountOut; - s_in_token; - s_out_token; - s_sender; - s_recipient; - s_contract; - } - with _ -> None - -let decode_abi_v3 event_hash abi s_contract = - try - match String.equal event_hash v3_event_id with - | false -> None - | true -> - let inputs = - List.sort - (fun arg1 arg2 -> compare arg1.pda_index arg2.pda_index) - abi.evdai_inputs in - let s_sender = - let tmp = List.nth inputs 0 in - known_address_or_evm_value_to_known_address_or_address tmp.pda_data - in - let s_recipient = - let tmp = List.nth inputs 1 in - known_address_or_evm_value_to_known_address_or_address tmp.pda_data - in - let uv3s_amount0 = - let tmp = List.nth inputs 2 in - get_int_from_evmvalue tmp.pda_data in - let uv3s_amount1 = - let tmp = List.nth inputs 3 in - get_int_from_evmvalue tmp.pda_data in - let s_amountIn, s_amountOut, s_in_token, s_out_token = - match Z.gt uv3s_amount0 Z.zero with - | true -> - let b1 = not @@ Z.equal uv3s_amount1 Z.zero in - let b2 = Z.gt Z.zero uv3s_amount1 in - if b1 && b2 then - (uv3s_amount0, Z.neg uv3s_amount1, 0, 1) - else - Log.log_error_fail ~here:[%here] "assert failed general_swapV3" - | false -> - let b1 = not @@ Z.equal uv3s_amount0 Z.zero in - let b2 = Z.gt uv3s_amount1 Z.zero in - if b1 && b2 then - (uv3s_amount1, Z.neg uv3s_amount0, 1, 0) - else - Log.log_error_fail ~here:[%here] "assert failed general_swapV3" - in - Some - { - s_amountIn; - s_amountOut; - s_in_token; - s_out_token; - s_sender; - s_recipient; - s_contract; - } - with _ -> None - -let get_v2_swap log = - match log.evdri_decode with - | None -> None - | Some s -> ( - let event_decode = s.evdi_decode in - match String.equal (s.evdi_hash :> string) (v2_event_id :> string) with - | false -> None - | true -> ( - match event_decode with - | Event_known_contract_abi abi -> - decode_abi_v2 (s.evdi_hash :> string) abi.evdka_inputs s.evdi_contract - | _ -> None)) - -let get_v3_swap log = - match log.evdri_decode with - | None -> None - | Some s -> ( - let event_decode = s.evdi_decode in - match String.equal (s.evdi_hash :> string) (v3_event_id :> string) with - | false -> None - | true -> ( - match event_decode with - | Event_known_contract_abi abi -> - decode_abi_v3 (s.evdi_hash :> string) abi.evdka_inputs s.evdi_contract - | _ -> None)) - -let get_v2_swaps = List.filter_map get_v2_swap - -let get_v3_swaps = List.filter_map get_v3_swap - -let find_swaps events = - let v2 = get_v2_swaps events in - let v3 = get_v3_swaps events in - concat_list v2 v3 - -let gen_swap_transaction (st_sender : known_address_or_address) - (st_transaction_hash : b) (st_index : bint) (st_swap_list : swap list) = - let st_exchange_token_contract = - list_uniq_token_swapped - @@ List.fold_left - (fun acc (arg : swap) -> - let ts_in_token, ts_out_token, ts_contract = - (arg.s_in_token, arg.s_out_token, arg.s_contract) in - { ts_in_token; ts_out_token; ts_contract } :: acc) - [] st_swap_list in - { - st_transaction_hash; - st_index; - st_sender; - st_swap_list; - st_exchange_token_contract; - } +open Tools +open Eth_analysis_utils + +let get_event_sandwich transaction_trace hashtbl = + Hashtbl.add hashtbl `swapV2 (`swapV2 []) ; + Hashtbl.add hashtbl `swapV3 (`swapV3 []) ; + let events_function_sandwich log hashtbl = + let v2 = Eth_tools.get_v2_swaps log.dtr_logs in + let v3 = Eth_tools.get_v3_swaps log.dtr_logs in + Eth_tools.add_or_replace_information hashtbl `swapV2 (`swapV2 v2) ; + Eth_tools.add_or_replace_information hashtbl `swapV3 (`swapV3 v3) in + let _ = + Eth_tools.get_pair_swaps_v2_v3 events_function_sandwich transaction_trace + hashtbl in + hashtbl let detect_fr_br (swap_transaction_list : swap_transaction list) = let rec inter (swap_transaction_list : swap_transaction list) acc_sandwich = @@ -226,7 +37,7 @@ let detect_fr_br (swap_transaction_list : swap_transaction list) = (fun acc_possible_sandwich sdp_token_sandwich -> match List.find_opt - (eq_inverse_token_swapped sdp_token_sandwich) + (Eth_tools.eq_inverse_token_swapped sdp_token_sandwich) br_exchange_token_contract with | None -> acc_possible_sandwich @@ -237,7 +48,7 @@ let detect_fr_br (swap_transaction_list : swap_transaction list) = sto_index = fr_possible.st_index; sto_sender = sender; sto_swap_list = - filter_swap sdp_token_sandwich + Eth_tools.filter_swap sdp_token_sandwich fr_possible.st_swap_list; } in let sdp_back_run = @@ -247,7 +58,7 @@ let detect_fr_br (swap_transaction_list : swap_transaction list) = sto_index = br_current_possible.st_index; sto_sender = br_current_possible.st_sender; sto_swap_list = - filter_swap inverse_token_swapped + Eth_tools.filter_swap inverse_token_swapped br_current_possible.st_swap_list; } in let sandwich_possible = @@ -257,16 +68,9 @@ let detect_fr_br (swap_transaction_list : swap_transaction list) = concat_list sandwich_possible_list acc) acc_sandwich possible_br in inter swap_rest sandwich in - let swap_transaction_list = List.sort - (fun swap1 swap2 -> - if swap1.st_index > swap2.st_index then - 1 - else if swap1.st_index = swap2.st_index then - 0 - else - -1) + (fun swap1 swap2 -> compare swap1.st_index swap2.st_index) swap_transaction_list in inter swap_transaction_list [] @@ -288,7 +92,7 @@ let find_victim (sandwich_possible : sandwich_possible) List.find_all (fun in_index_swap -> List.exists - (eq_token_swapped sdp_token_sandwich) + (Eth_tools.eq_token_swapped sdp_token_sandwich) in_index_swap.st_exchange_token_contract) in_index_swap_list in match victim_list with @@ -298,7 +102,7 @@ let find_victim (sandwich_possible : sandwich_possible) List.fold_left (fun acc_victim victim -> let sto_swap_list = - filter_swap sdp_token_sandwich victim.st_swap_list in + Eth_tools.filter_swap sdp_token_sandwich victim.st_swap_list in { sto_transaction_hash = victim.st_transaction_hash; sto_index = victim.st_index; @@ -318,7 +122,7 @@ let find_victim (sandwich_possible : sandwich_possible) }) let extract_sandwich_list (sandwich_possible_list : sandwich_possible list) - (swap_list : swap_transaction list) : sandwich_output list = + (swap_list : swap_transaction list) = List.fold_left (fun acc_sandwich sandwich_possible -> match find_victim sandwich_possible swap_list with @@ -326,19 +130,45 @@ let extract_sandwich_list (sandwich_possible_list : sandwich_possible list) | Some sandwich -> sandwich :: acc_sandwich) [] sandwich_possible_list -let detect_sandwich (receipts : transaction_receipt_decode list) = - let swap_transaction_list = +let detect_sandwich debug_trace_list = + let get_sandwich_data debug_trace_list = List.fold_left - (fun acc (arg : transaction_receipt_decode) -> - let transaction_hash = arg.trd_transaction_receipt.r_transaction_hash in - let sender = arg.trd_from in - let index = arg.trd_transaction_receipt.r_transaction_index in - let swap_list = find_swaps arg.trd_events_info in - match swap_list with - | [] -> acc - | swap_list -> - gen_swap_transaction sender transaction_hash index swap_list :: acc) - [] receipts in + (fun swap arg -> + let hashtbl = Hashtbl.create 7 in + + let transaction_hash, sender, index = + Eth_tools.get_transaction_information arg in + let swapV2, swapV3 = + let hashtbl = get_event_sandwich arg.dt_result hashtbl in + + let swapV2 = + let tmp = Hashtbl.find hashtbl `swapV2 in + match tmp with + | `swapV2 v2 -> v2 + | _ -> assert false in + let swapV3 = + let tmp = Hashtbl.find hashtbl `swapV3 in + match tmp with + | `swapV3 v3 -> v3 + | _ -> assert false in + + (swapV2, swapV3) in + + let swap_list = + let v2 = List.map Eth_tools.general_swapV2 swapV2 in + let v3 = List.filter_map Eth_tools.general_swapV3 swapV3 in + concat_list v2 v3 in + + let swap = + match swap_list with + | [] -> swap + | swap_list -> + Eth_tools.gen_swap_transaction sender transaction_hash index + swap_list + :: swap in + swap) + [] debug_trace_list in + let swap_transaction_list = get_sandwich_data debug_trace_list in let detect_fr_br = detect_fr_br swap_transaction_list in match detect_fr_br with | [] -> [] diff --git a/src/ethereum_analysis/eth_analysis_sandwich.mli b/src/ethereum_analysis/eth_analysis_sandwich.mli index 3d76bb8617780c6fe3cea80915e6953e08a8375e..1e17b9c50003d33457c9453038527447a124eaf1 100644 --- a/src/ethereum_analysis/eth_analysis_sandwich.mli +++ b/src/ethereum_analysis/eth_analysis_sandwich.mli @@ -15,5 +15,8 @@ A list of [Common.Types.Ethereum_analysis.sandwich_output] records, each describing a detected sandwich attack. *) val detect_sandwich : - Common.Types.Ethereum_decode.transaction_receipt_decode list -> + ( Common.Types.Ethereum_decode.known_address_or_address, + Common.Types.Ethereum_decode.known_address_or_evm_value ) + Olympus.Types.debug_trace + list -> Common.Types.Ethereum_analysis.sandwich_output list diff --git a/src/ethereum_analysis/eth_analysis_sandwich_liquidity.ml b/src/ethereum_analysis/eth_analysis_sandwich_liquidity.ml new file mode 100644 index 0000000000000000000000000000000000000000..4352ee406c3fc5155c664db5352893b3404bda8e --- /dev/null +++ b/src/ethereum_analysis/eth_analysis_sandwich_liquidity.ml @@ -0,0 +1,525 @@ +open Eth +open Eth_analysis_utils +open Common +open Tools +open Olympus.Types +open Types.Ethereum_analysis +open Types.Ethereum_decode + +let get_event_sandwich transaction_trace hashtbl = + Hashtbl.add hashtbl `swapV3 (`swapV3 []) ; + Hashtbl.add hashtbl `mint (`mint []) ; + Hashtbl.add hashtbl `burn (`burn []) ; + Hashtbl.add hashtbl `collect (`collect []) ; + let events_function_sandwich_liquidity log hashtbl = + let v3_swap = Eth_tools.get_v3_swaps log.dtr_logs in + let v3_m = Eth_tools.get_v3_mint log.dtr_logs in + let v3_b = Eth_tools.get_v3_burn log.dtr_logs in + let v3_c = Eth_tools.get_v3_collect log.dtr_logs in + + Eth_tools.add_or_replace_information hashtbl `collect (`collect v3_c) ; + + match v3_swap with + | [] -> + Eth_tools.add_or_replace_information hashtbl `mint (`mint v3_m) ; + Eth_tools.add_or_replace_information hashtbl `burn (`burn v3_b) + | l_v3_swap -> + let _ = + match (v3_m, v3_b) with + | [], [] -> + Eth_tools.add_or_replace_information hashtbl `swapV3 + (`swapV3 l_v3_swap) + | l_v3_m, [] -> + let add_swap_bool, mint_swap = + Eth_tools.get_mint_swap l_v3_m l_v3_swap hashtbl in + Hashtbl.replace hashtbl `mint (`mint mint_swap) ; + Eth_tools.add_or_replace_information hashtbl `swapV3 + (`swapV3 (Eth_tools.add_swap [] add_swap_bool)) + | [], l_v3_b -> + let add_swap_bool, burn_swap = + Eth_tools.get_burn_swap l_v3_b l_v3_swap hashtbl in + Hashtbl.replace hashtbl `burn (`burn burn_swap) ; + Eth_tools.add_or_replace_information hashtbl `swapV3 + (`swapV3 (Eth_tools.add_swap [] add_swap_bool)) + | l_v3_m, l_v3_b -> + let add_swap1, mint_swap = + Eth_tools.get_mint_swap l_v3_m l_v3_swap hashtbl in + let add_swap2, burn_swap = + Eth_tools.get_burn_swap l_v3_b l_v3_swap hashtbl in + let add_swap_list = + let tmp = Eth_tools.add_swap [] add_swap2 in + Eth_tools.add_swap tmp add_swap1 in + + Hashtbl.replace hashtbl `mint (`mint mint_swap) ; + Hashtbl.replace hashtbl `burn (`burn burn_swap) ; + Eth_tools.add_or_replace_information hashtbl `swapV3 + (`swapV3 add_swap_list) in + + () in + + let _ = + Eth_tools.get_pair_swaps_v2_v3 events_function_sandwich_liquidity + transaction_trace hashtbl in + hashtbl + +let get_sandwich_liquidity_possible swap_transaction_mint swap_transaction_burn + swap_transaction_collect = + (* Aggregates Mint by transaction, DEX and tick *) + let aggregate_mint swap_transaction_mint = + let hash = Hashtbl.create 3 in + List.fold_left + (fun acc_aggregated mint -> + List.iter + (fun m -> + let add_mint = + match + Hashtbl.find_opt hash + (m.uv3m_contract, m.uv3m_tick_lower, m.uv3m_tick_upper) + with + | None -> [m] + | Some s -> m :: s in + + Hashtbl.replace hash + (m.uv3m_contract, m.uv3m_tick_lower, m.uv3m_tick_upper) + add_mint) + mint.stm_mint_list ; + + Hashtbl.fold + (fun (stma_contract, stma_tick_lower, stma_tick_upper) stm_mint_list + acc_mint -> + let stma_mint_list = + { + stm_transaction_hash = mint.stm_transaction_hash; + stm_index = mint.stm_index; + stm_sender = mint.stm_sender; + stm_victim_list = mint.stm_victim_list; + stm_mint_list; + } in + { + stma_transaction_hash = mint.stm_transaction_hash; + stma_index = mint.stm_index; + stma_sender = mint.stm_sender; + stma_contract; + stma_tick_lower; + stma_tick_upper; + stma_mint_list; + } + :: acc_mint) + hash acc_aggregated) + [] swap_transaction_mint in + + (* Aggregates Burn Collect by transaction, DEX and tick *) + let aggregate_burn_collect swap_transaction_burn swap_transaction_collect = + let aggregate_burn = + let hash = Hashtbl.create 3 in + List.fold_left + (fun acc_aggregated burn -> + List.iter + (fun m -> + let add_burn = + match + Hashtbl.find_opt hash + (m.uv3b_contract, m.uv3b_tick_lower, m.uv3b_tick_upper) + with + | None -> [m] + | Some s -> m :: s in + Hashtbl.replace hash + (m.uv3b_contract, m.uv3b_tick_lower, m.uv3b_tick_upper) + add_burn) + burn.stb_burn_list ; + + Hashtbl.fold + (fun (stba_contract, stba_tick_lower, stba_tick_upper) stb_burn_list + acc_burn -> + let stba_burn_list = + { + stb_transaction_hash = burn.stb_transaction_hash; + stb_index = burn.stb_index; + stb_sender = burn.stb_sender; + stb_victim_list = burn.stb_victim_list; + stb_burn_list; + } in + { + stba_transaction_hash = burn.stb_transaction_hash; + stba_index = burn.stb_index; + stba_sender = burn.stb_sender; + stba_contract; + stba_tick_lower; + stba_tick_upper; + stba_burn_list; + } + :: acc_burn) + hash acc_aggregated) + [] swap_transaction_burn in + + let aggregate_collect = + let hash = Hashtbl.create 3 in + List.fold_left + (fun acc_aggregated collect -> + List.iter + (fun m -> + let add_collect = + match + Hashtbl.find_opt hash + (m.uv3c_contract, m.uv3c_tick_lower, m.uv3c_tick_upper) + with + | None -> [m] + | Some s -> m :: s in + Hashtbl.replace hash + (m.uv3c_contract, m.uv3c_tick_lower, m.uv3c_tick_upper) + add_collect) + collect.stc_collect_list ; + + Hashtbl.fold + (fun (stca_contract, stca_tick_lower, stca_tick_upper) + stca_collect_list acc_collect -> + { + stca_transaction_hash = collect.stc_transaction_hash; + stca_index = collect.stc_index; + stca_sender = collect.stc_sender; + stca_contract; + stca_tick_lower; + stca_tick_upper; + stca_collect_list; + } + :: acc_collect) + hash acc_aggregated) + [] swap_transaction_collect in + + match aggregate_burn with + | [] -> [] + | aggregate_burn -> + List.fold_left + (fun acc_aggregated burn -> + let bca_collect_list = + List.find_opt + (fun c -> + equal_b c.stca_transaction_hash burn.stba_transaction_hash + && equal_z c.stca_tick_upper burn.stba_tick_upper + && equal_z c.stca_tick_lower burn.stba_tick_lower + && equal_contract_information c.stca_contract burn.stba_contract) + aggregate_collect in + { + bca_transaction_hash = burn.stba_transaction_hash; + bca_index = burn.stba_index; + bca_sender = burn.stba_sender; + bca_contract = burn.stba_contract; + bca_tick_lower = burn.stba_tick_lower; + bca_tick_upper = burn.stba_tick_upper; + bca_burn_list = burn; + bca_collect_list; + } + :: acc_aggregated) + [] aggregate_burn in + + let a_mint = aggregate_mint swap_transaction_mint in + let a_burn_collect = + aggregate_burn_collect swap_transaction_burn swap_transaction_collect in + + match (a_mint, a_burn_collect) with + | [], [] | _, [] | [], _ -> [] + | a_mint, a_burn_collect -> + List.fold_left + (fun acc slp_mint -> + let tmp = + List.find_all + (fun burn_collect -> + equal_b slp_mint.stma_transaction_hash + burn_collect.bca_transaction_hash + && equal_z slp_mint.stma_tick_upper burn_collect.bca_tick_upper + && equal_z slp_mint.stma_tick_lower burn_collect.bca_tick_lower + && equal_contract_information slp_mint.stma_contract + burn_collect.bca_contract) + a_burn_collect in + match tmp with + | [] -> acc + | slp_burn_collect -> { slp_mint; slp_burn_collect } :: acc) + [] a_mint + +let detect_sandwich_liquidity swap_transaction_v3 + (sandwich_possible_list : sandwich_liquidity_possible list) = + match (swap_transaction_v3, sandwich_possible_list) with + | [], [] | _, [] | [], _ -> [] + | swap_transaction_v3, sandwich_possible_list -> + List.fold_left + (fun acc sandwich_possible -> + let contract_fr = sandwich_possible.slp_mint.stma_contract in + let first_index : bint = + (sandwich_possible.slp_mint.stma_index :> bint) in + let last_index : bint = + List.fold_left + (fun old arg -> + let current : bint = (arg.bca_index :> bint) in + if old < current then + current + else + old) + (-1) sandwich_possible.slp_burn_collect in + + let sl_mint_liquidity = + let mint_liquidity_list = + sandwich_possible.slp_mint.stma_mint_list.stm_mint_list in + + let token0, token1 = + let tmp = List.hd mint_liquidity_list in + (tmp.uv3m_token0, tmp.uv3m_token1) in + + let mint_amount, mint_amount0, mint_amount1 = + List.fold_left + (fun (acc_amount, acc_amount0, acc_amount1) arg_mint -> + let acc_amount = Z.add acc_amount arg_mint.uv3m_amount in + let acc_amount0 = Z.add acc_amount0 arg_mint.uv3m_amount0 in + let acc_amount1 = Z.add acc_amount1 arg_mint.uv3m_amount1 in + + (acc_amount, acc_amount0, acc_amount1)) + (Z.zero, Z.zero, Z.zero) mint_liquidity_list in + + let tlp_liquidity = + { + tl_token0 = token0; + tl_token1 = token1; + tl_amount0 = mint_amount0; + tl_amount1 = mint_amount1; + } in + { tlp_amount = mint_amount; tlp_liquidity } in + + let sl_burn_collect_liquidity, fr_br_victim = + let fr_victim = + sandwich_possible.slp_mint.stma_mint_list.stm_victim_list in + let sl_burn_collect_liquidity, br_victim = + List.fold_left + (fun (acc_burn_collect, acc_local) + (burn_collect_list : burn_collect_aggregated) -> + let token0, token1 = + let tmp = + List.hd + burn_collect_list.bca_burn_list.stba_burn_list + .stb_burn_list in + (tmp.uv3b_token0, tmp.uv3b_token1) in + + let burn_amount, burn_amount0, burn_amount1 = + List.fold_left + (fun (acc_amount, acc_amount0, acc_amount1) arg_burn -> + let acc_amount = Z.add acc_amount arg_burn.uv3b_amount in + let acc_amount0 = + Z.add acc_amount0 arg_burn.uv3b_amount0 in + let acc_amount1 = + Z.add acc_amount1 arg_burn.uv3b_amount1 in + + (acc_amount, acc_amount0, acc_amount1)) + (Z.zero, Z.zero, Z.zero) + burn_collect_list.bca_burn_list.stba_burn_list.stb_burn_list + in + + let slbc_burn = + let tlp_liquidity = + { + tl_token0 = token0; + tl_token1 = token1; + tl_amount0 = burn_amount0; + tl_amount1 = burn_amount1; + } in + { tlp_amount = burn_amount; tlp_liquidity } in + + let slbc_collect = + let collect_amount0, collect_amount1 = + match burn_collect_list.bca_collect_list with + | None -> (Z.zero, Z.zero) + | Some collect_list -> + List.fold_left + (fun (acc_amount0, acc_amount1) arg_collect -> + let acc_amount0 = + Z.add acc_amount0 arg_collect.uv3c_amount0 in + let acc_amount1 = + Z.add acc_amount1 arg_collect.uv3c_amount1 in + + (acc_amount0, acc_amount1)) + (Z.zero, Z.zero) collect_list.stca_collect_list in + { + tl_token0 = token0; + tl_token1 = token1; + tl_amount0 = collect_amount0; + tl_amount1 = collect_amount1; + } in + + let v = + burn_collect_list.bca_burn_list.stba_burn_list.stb_victim_list + in + ({ slbc_burn; slbc_collect } :: acc_burn_collect, v :: acc_local)) + ([], []) sandwich_possible.slp_burn_collect in + (sl_burn_collect_liquidity, fr_victim :: br_victim) in + + let sl_victim = + List.fold_left + (fun acc_victim arg -> + if arg.sto_index > first_index && arg.sto_index < last_index then + let swap = + List.find_all + (fun s -> + equal_contract_information contract_fr s.s_contract) + arg.sto_swap_list in + match swap with + | [] -> acc_victim + | swap_list -> + { arg with sto_swap_list = swap_list } :: acc_victim + else + acc_victim) + fr_br_victim swap_transaction_v3 in + + let sl_profit = + let burn_amount0, burn_amount1 = + List.fold_left + (fun (burn_amount0, burn_amount1) arg -> + let a0 = + Z.add arg.slbc_burn.tlp_liquidity.tl_amount0 burn_amount0 + in + let a1 = + Z.add arg.slbc_burn.tlp_liquidity.tl_amount1 burn_amount1 + in + (a0, a1)) + (Z.zero, Z.zero) sl_burn_collect_liquidity in + { + tl_token0 = sl_mint_liquidity.tlp_liquidity.tl_token0; + tl_token1 = sl_mint_liquidity.tlp_liquidity.tl_token1; + tl_amount0 = + Z.add burn_amount0 sl_mint_liquidity.tlp_liquidity.tl_amount0; + tl_amount1 = + Z.add burn_amount1 sl_mint_liquidity.tlp_liquidity.tl_amount1; + } in + { + sl_victim; + sl_mint = sandwich_possible.slp_mint; + sl_burn_collect = sandwich_possible.slp_burn_collect; + sl_profit; + sl_mint_liquidity; + sl_burn_collect_liquidity; + } + :: acc) + [] sandwich_possible_list + +let detect_sandwich debug_trace_list = + let get_sanwdich_liquidity_data debug_trace_list = + let ( swap_transaction_mint, + swap_transaction_burn, + swap_transaction_collect, + swap_transaction_v3 ) = + List.fold_left + (fun (st_v3_m, st_v3_b, st_v3_c, st_v3_s) arg -> + let transaction_hash, sender, index = + Eth_tools.get_transaction_information arg in + + let hashtbl = Hashtbl.create 7 in + let hashtbl = get_event_sandwich arg.dt_result hashtbl in + + let v3_mint = + let tmp = Hashtbl.find hashtbl `mint in + match tmp with + | `mint m -> ( + match m with + | [] -> st_v3_m + | v3_mint -> + let stm_victim_list = + let sto_swap_list = + List.fold_left + (fun acc arg -> + concat_list acc + @@ List.filter_map Eth_tools.general_swapV3 + arg.uv3m_victim) + [] v3_mint in + { + sto_transaction_hash = transaction_hash; + sto_index = index; + sto_sender = sender; + sto_swap_list; + } in + + { + stm_transaction_hash = transaction_hash; + stm_index = index; + stm_sender = sender; + stm_mint_list = v3_mint; + stm_victim_list; + } + :: st_v3_m) + | _ -> Log.log_error_fail ~here:[%here] "couldn't find mint" in + + let v3_burn = + let tmp = Hashtbl.find hashtbl `burn in + match tmp with + | `burn b -> ( + match b with + | [] -> st_v3_b + | v3_burn -> + let stb_victim_list = + let sto_swap_list = + List.fold_left + (fun acc arg -> + concat_list acc + @@ List.filter_map Eth_tools.general_swapV3 + arg.uv3b_victim) + [] v3_burn in + { + sto_transaction_hash = transaction_hash; + sto_index = index; + sto_sender = sender; + sto_swap_list; + } in + + { + stb_transaction_hash = transaction_hash; + stb_index = index; + stb_sender = sender; + stb_burn_list = v3_burn; + stb_victim_list; + } + :: st_v3_b) + | _ -> Log.log_error_fail ~here:[%here] "couldn't find burn" in + + let v3_collect = + let tmp = Hashtbl.find hashtbl `collect in + match tmp with + | `collect c -> ( + match c with + | [] -> st_v3_c + | v3_collect -> + { + stc_transaction_hash = transaction_hash; + stc_index = index; + stc_sender = sender; + stc_collect_list = v3_collect; + } + :: st_v3_c) + | _ -> Log.log_error_fail ~here:[%here] "couldn't find collect" + in + + let v3_swap = + let tmp = Hashtbl.find hashtbl `swapV3 in + match tmp with + | `swapV3 s -> ( + match s with + | [] -> st_v3_s + | swapV3 -> + let swap_transaction_list = + let sto_swap_list = + List.filter_map Eth_tools.general_swapV3 swapV3 in + { + sto_transaction_hash = transaction_hash; + sto_index = index; + sto_sender = sender; + sto_swap_list; + } in + + swap_transaction_list :: st_v3_s) + | _ -> Log.log_error_fail ~here:[%here] "couldn't find swap" in + + (v3_mint, v3_burn, v3_collect, v3_swap)) + ([], [], [], []) debug_trace_list in + + ( swap_transaction_v3, + get_sandwich_liquidity_possible swap_transaction_mint + swap_transaction_burn swap_transaction_collect ) in + let swap_transaction_v3, sandwich_possible = + get_sanwdich_liquidity_data debug_trace_list in + let sandwich_liquidity = + detect_sandwich_liquidity swap_transaction_v3 sandwich_possible in + sandwich_liquidity diff --git a/src/ethereum_analysis/eth_analysis_sandwich_liquidity.mli b/src/ethereum_analysis/eth_analysis_sandwich_liquidity.mli new file mode 100644 index 0000000000000000000000000000000000000000..02c606fd5c5cbcf4609eeff1356cdccdf151a9f1 --- /dev/null +++ b/src/ethereum_analysis/eth_analysis_sandwich_liquidity.mli @@ -0,0 +1,25 @@ +(** Sandwich_liquidity Detection Interface + + Provides the main function to detect sandwich_liquidity attacks from debug + traces. *) + +(** [detect_sandwich traces] Analyze a list of Ethereum debug traces to identify + sandwich_liquidity attacks. + + This function scans the provided traces for the characteristic pattern of + sandwich_liquidity trades: a front-running transaction, victim swap, and + back-running transaction within the same block. + + @param decoded_traces + A list of [Olympus.Types.debug_trace] entries for a single block or + transaction. + @return + An Lwt promise resolving to a list of + [Common.Types.Ethereum_analysis.sandwich_liquidity] records, each + describing a detected sandwich_liquidity attack. *) +val detect_sandwich : + ( Common.Types.Ethereum_decode.known_address_or_address, + Common.Types.Ethereum_decode.known_address_or_evm_value ) + Olympus.Types.debug_trace + list -> + Common.Types.Ethereum_analysis.sandwich_liquidity list diff --git a/src/ethereum_analysis/eth_analysis_tools/eth_tools.ml b/src/ethereum_analysis/eth_analysis_tools/eth_tools.ml index 994865ef9ca59e0d3927df9e9248ae3b6ef7ea3b..fc9e9e45328c08836314a391025d5976e4c03477 100644 --- a/src/ethereum_analysis/eth_analysis_tools/eth_tools.ml +++ b/src/ethereum_analysis/eth_analysis_tools/eth_tools.ml @@ -227,6 +227,7 @@ let evt_to_string tag = match tag with | `liquidate -> "liquidate" | `sandwich -> "sandwich" + | `sandwich_liquidity -> "sandwich_liquidity" | `flashloan -> "flashloan" | `transfer -> "transfer" | `arbitrage -> "arbitrage" @@ -1072,3 +1073,528 @@ let rec annotate_cycles from_ cft = let is_null_address addr = address_of_eth_address addr = Common.null_address || address_of_eth_address addr = Common.burn_address +let get_transaction_information transaction = + let transaction_hash = transaction.dt_tx_hash in + let sender = + match transaction.dt_result.dtr_from_decode with + | None -> Coadr_address transaction.dt_result.dtr_from + | Some known_address -> known_address in + let index = transaction.dt_tx_index in + (transaction_hash, sender, index) + +let get_burn_swap l_v3_b l_v3_swap hashtbl = + List.fold_left + (fun (acc_swap, acc_gen_burn) swap -> + let used_swap_local, old_burn_swap = + match Hashtbl.find_opt hashtbl `burn with + | Some (`burn []) -> (false, acc_gen_burn) + | Some (`burn local_burn) -> + List.fold_left + (fun (bool_swap, acc_loc_burn) burn -> + if + equal_contract_information burn.uv3b_contract swap.uv3s_contract + then + ( true, + { burn with uv3b_victim = swap :: burn.uv3b_victim } + :: acc_loc_burn ) + else + (bool_swap, burn :: acc_loc_burn)) + (false, acc_gen_burn) local_burn + | _ -> assert false in + + let used_swap_local, burn_swap = + List.fold_left + (fun (bool_swap, acc_loc_burn) burn -> + if + swap.uv3s_index > burn.uv3b_index + && equal_contract_information burn.uv3b_contract + swap.uv3s_contract + then + ( true, + { burn with uv3b_victim = swap :: burn.uv3b_victim } + :: acc_loc_burn ) + else + (bool_swap, burn :: acc_loc_burn)) + (used_swap_local, old_burn_swap) + l_v3_b in + + ((used_swap_local, swap) :: acc_swap, burn_swap)) + ([], []) l_v3_swap + +let get_swap tx_logs (event_id : b) decode = + List.filter_map + (fun log -> + match log.dtrl_decode_signature with + | None -> None + | Some s -> ( + match String.equal (s.evdi_hash :> string) (event_id :> string) with + | false -> None + | true -> ( + let event_decode = s.evdi_decode in + match event_decode with + | Event_known_contract_abi abi -> decode log abi.evdka_inputs + | _ -> None))) + tx_logs + +let eq_inverse_token_swapped ts1 ts2 = + ts1.ts_out_token = ts2.ts_in_token + && ts1.ts_in_token = ts2.ts_out_token + && ts1.ts_contract = ts2.ts_contract + +let eq_token_swapped ts1 ts2 = + ts1.ts_in_token = ts2.ts_in_token + && ts1.ts_out_token = ts2.ts_out_token + && ts1.ts_contract = ts2.ts_contract + +let list_uniq_token_swapped list = + List.fold_left + (fun acc arg -> + match List.find_opt (eq_token_swapped arg) acc with + | None -> arg :: acc + | Some _ -> acc) + [] list + +let filter_swap token_swapped swap_list = + List.filter + (fun s -> + equal_contract_information s.s_in_token token_swapped.ts_in_token + && equal_contract_information s.s_out_token token_swapped.ts_out_token) + swap_list + +let get_contract_info_from_known_address_or_address contract = + match contract with + | Coadr_address _ -> None + | Coadr_known_address address_info -> address_info.ai_is_contract + +let get_token_address log = + match log.dtrl_decode_signature with + | None -> Log.log_warning_fail ~here:[%here] "couldn't retreive token address" + | Some arg -> ( + let contract_info_opt = + get_contract_info_from_known_address_or_address arg.evdi_contract in + let pool = + Option.bind contract_info_opt (fun contract -> contract.ci_pool_contracts) + in + match pool with + | None -> + Log.log_warning_fail ~here:[%here] "couldn't retreive token address" + | Some (contract1, contract2) -> + (Option.get contract_info_opt, contract1, contract2)) + +let get_int_from_evmvalue (contract_or_evm_value : known_address_or_evm_value) = + match contract_or_evm_value with + | Coev_evm_value evm_value -> ( + match evm_value with + | `int int -> int + | e -> + Log.log_error_fail ~here:[%here] + @@ Format.sprintf "fail get_int_from_evmvalue %s" + @@ EzEncoding.construct Eth.evm_value_enc e) + | _ -> + Log.log_error_fail ~here:[%here] + @@ Format.sprintf "fail get_int_from_evmvalue %s" + @@ EzEncoding.construct known_address_or_evm_value_enc contract_or_evm_value + +let general_swapV2 (swap : uni_v2_swap) = + let s_amountIn, s_in_token = + match Z.compare swap.uv2s_amount0In swap.uv2s_amount1In with + | -1 -> (swap.uv2s_amount1In, swap.uv2s_token1) + | 1 -> (swap.uv2s_amount0In, swap.uv2s_token0) + | _ -> assert false in + let s_amountOut, s_out_token = + match Z.compare swap.uv2s_amount0Out swap.uv2s_amount1Out with + | -1 -> (swap.uv2s_amount1Out, swap.uv2s_token1) + | 1 -> (swap.uv2s_amount0Out, swap.uv2s_token0) + | _ -> assert false in + let s_sender, s_recipient, s_contract = + (swap.uv2s_sender, swap.uv2s_recipient, swap.uv2s_contract) in + { + s_amountIn; + s_amountOut; + s_in_token; + s_out_token; + s_sender; + s_recipient; + s_contract; + } + +let general_swapV3 (swap : uni_v3_swap) = + try + let s_amountIn, s_amountOut, s_in_token, s_out_token = + match Z.gt swap.uv3s_amount0 Z.zero with + | true -> + let b1 = not @@ Z.equal swap.uv3s_amount1 Z.zero in + let b2 = Z.gt Z.zero swap.uv3s_amount1 in + if b1 && b2 then + ( swap.uv3s_amount0, + Z.neg swap.uv3s_amount1, + swap.uv3s_token0, + swap.uv3s_token1 ) + else + Log.log_error_fail ~here:[%here] "assert failed general_swapV3" + | false -> + let b1 = not @@ Z.equal swap.uv3s_amount0 Z.zero in + let b2 = Z.gt swap.uv3s_amount1 Z.zero in + if b1 && b2 then + ( swap.uv3s_amount1, + Z.neg swap.uv3s_amount0, + swap.uv3s_token1, + swap.uv3s_token0 ) + else + Log.log_error_fail ~here:[%here] "assert failed general_swapV3" in + let s_sender, s_recipient, s_contract = + (swap.uv3s_sender, swap.uv3s_recipient, swap.uv3s_contract) in + Some + { + s_amountIn; + s_amountOut; + s_in_token; + s_out_token; + s_sender; + s_recipient; + s_contract; + } + with e -> + Log.log @@ Printexc.to_string e ; + None + +let get_mint_swap l_v3_m l_v3_swap hashtbl = + List.fold_left + (fun (acc_swap, acc_gen_mint) swap -> + let used_swap_local, old_mint_swap = + match Hashtbl.find_opt hashtbl `mint with + | Some (`mint []) -> (false, acc_gen_mint) + | Some (`mint local_mint) -> + List.fold_left + (fun (bool_swap, acc_loc_mint) mint -> + if + equal_contract_information mint.uv3m_contract swap.uv3s_contract + then + ( true, + { mint with uv3m_victim = swap :: mint.uv3m_victim } + :: acc_loc_mint ) + else + (bool_swap, mint :: acc_loc_mint)) + (false, acc_gen_mint) local_mint + | _ -> assert false in + + let used_swap_local, mint_swap = + List.fold_left + (fun (bool_swap, acc_loc_mint) mint -> + if + swap.uv3s_index > mint.uv3m_index + && equal_contract_information mint.uv3m_contract + swap.uv3s_contract + then + ( true, + { mint with uv3m_victim = swap :: mint.uv3m_victim } + :: acc_loc_mint ) + else + (bool_swap, mint :: acc_loc_mint)) + (used_swap_local, old_mint_swap) + l_v3_m in + + ((used_swap_local, swap) :: acc_swap, mint_swap)) + ([], []) l_v3_swap + +let gen_swap_transaction (st_sender : known_address_or_address) + (st_transaction_hash : b) (st_index : bint) (st_swap_list : swap list) = + let st_exchange_token_contract = + list_uniq_token_swapped + @@ List.fold_left + (fun acc (arg : swap) -> + let ts_in_token, ts_out_token, ts_contract = + (arg.s_in_token, arg.s_out_token, arg.s_contract) in + { ts_in_token; ts_out_token; ts_contract } :: acc) + [] st_swap_list in + { + st_transaction_hash; + st_index; + st_sender; + st_swap_list; + st_exchange_token_contract; + } + +let decode_abi_mint_v3 log abi = + try + let inputs = + List.sort + (fun arg1 arg2 -> compare arg1.pda_index arg2.pda_index) + abi.evdai_inputs in + + let uv3m_contract, uv3m_token0, uv3m_token1 = get_token_address log in + let uv3m_sender = + let tmp = List.nth inputs 0 in + known_address_or_evm_value_to_known_address_or_address tmp.pda_data in + let uv3m_owner = + let tmp = List.nth inputs 1 in + known_address_or_evm_value_to_known_address_or_address tmp.pda_data in + let uv3m_tick_lower = + let tmp = List.nth inputs 2 in + get_int_from_evmvalue tmp.pda_data in + let uv3m_tick_upper = + let tmp = List.nth inputs 3 in + get_int_from_evmvalue tmp.pda_data in + let uv3m_amount = + let tmp = List.nth inputs 4 in + get_int_from_evmvalue tmp.pda_data in + let uv3m_amount0 = + let tmp = List.nth inputs 5 in + get_int_from_evmvalue tmp.pda_data in + let uv3m_amount1 = + let tmp = List.nth inputs 6 in + get_int_from_evmvalue tmp.pda_data in + Some + { + uv3m_victim = []; + uv3m_index = log.dtrl_index; + uv3m_contract; + uv3m_token0; + uv3m_token1; + uv3m_sender; + uv3m_owner; + uv3m_amount; + uv3m_amount0; + uv3m_amount1; + uv3m_tick_upper; + uv3m_tick_lower; + } + with e -> + Log.log @@ Printexc.to_string e ; + None + +let decode_abi_burn_v3 log abi = + try + let inputs = + List.sort + (fun arg1 arg2 -> compare arg1.pda_index arg2.pda_index) + abi.evdai_inputs in + + let uv3b_contract, uv3b_token0, uv3b_token1 = get_token_address log in + let uv3b_owner = + let tmp = List.nth inputs 0 in + known_address_or_evm_value_to_known_address_or_address tmp.pda_data in + let uv3b_tick_lower = + let tmp = List.nth inputs 1 in + get_int_from_evmvalue tmp.pda_data in + let uv3b_tick_upper = + let tmp = List.nth inputs 2 in + get_int_from_evmvalue tmp.pda_data in + let uv3b_amount = + let tmp = List.nth inputs 3 in + get_int_from_evmvalue tmp.pda_data in + let uv3b_amount0 = + let tmp = List.nth inputs 4 in + get_int_from_evmvalue tmp.pda_data in + let uv3b_amount1 = + let tmp = List.nth inputs 5 in + get_int_from_evmvalue tmp.pda_data in + + Some + { + uv3b_victim = []; + uv3b_index = log.dtrl_index; + uv3b_contract; + uv3b_token0; + uv3b_token1; + uv3b_owner; + uv3b_tick_lower; + uv3b_tick_upper; + uv3b_amount; + uv3b_amount0; + uv3b_amount1; + } + with e -> + Log.log @@ Printexc.to_string e ; + None + +let decode_abi_collect_v3 log abi = + try + let inputs = + List.sort + (fun arg1 arg2 -> compare arg1.pda_index arg2.pda_index) + abi.evdai_inputs in + + let uv3c_contract, uv3c_token0, uv3c_token1 = get_token_address log in + let uv3c_owner = + let tmp = List.nth inputs 0 in + known_address_or_evm_value_to_known_address_or_address tmp.pda_data in + let uv3c_recipient = + let tmp = List.nth inputs 1 in + known_address_or_evm_value_to_known_address_or_address tmp.pda_data in + let uv3c_tick_lower = + let tmp = List.nth inputs 2 in + get_int_from_evmvalue tmp.pda_data in + let uv3c_tick_upper = + let tmp = List.nth inputs 3 in + get_int_from_evmvalue tmp.pda_data in + let uv3c_amount0 = + let tmp = List.nth inputs 4 in + get_int_from_evmvalue tmp.pda_data in + let uv3c_amount1 = + let tmp = List.nth inputs 5 in + get_int_from_evmvalue tmp.pda_data in + + Some + { + uv3c_index = log.dtrl_index; + uv3c_contract; + uv3c_owner; + uv3c_recipient; + uv3c_tick_lower; + uv3c_tick_upper; + uv3c_amount0; + uv3c_amount1; + uv3c_token0; + uv3c_token1; + } + with e -> + Log.log @@ Printexc.to_string e ; + None + +let decode_abi_v2 log abi = + try + let inputs = + List.sort + (fun arg1 arg2 -> compare arg1.pda_index arg2.pda_index) + abi.evdai_inputs in + + let uv2s_contract, uv2s_token0, uv2s_token1 = get_token_address log in + let uv2s_sender = + let tmp = List.nth inputs 0 in + known_address_or_evm_value_to_known_address_or_address tmp.pda_data in + let uv2s_amount0In = + let tmp = List.nth inputs 1 in + get_int_from_evmvalue tmp.pda_data in + let uv2s_amount1In = + let tmp = List.nth inputs 2 in + get_int_from_evmvalue tmp.pda_data in + let uv2s_amount0Out = + let tmp = List.nth inputs 3 in + get_int_from_evmvalue tmp.pda_data in + let uv2s_amount1Out = + let tmp = List.nth inputs 4 in + get_int_from_evmvalue tmp.pda_data in + let uv2s_recipient = + let tmp = List.nth inputs 5 in + known_address_or_evm_value_to_known_address_or_address tmp.pda_data in + Some + { + uv2s_index = log.dtrl_index; + uv2s_contract; + uv2s_sender; + uv2s_recipient; + uv2s_amount0In; + uv2s_amount1In; + uv2s_amount0Out; + uv2s_amount1Out; + uv2s_token0; + uv2s_token1; + } + with e -> + Log.log @@ Printexc.to_string e ; + None + +let decode_abi_v3 log abi = + try + let inputs = + List.sort + (fun arg1 arg2 -> compare arg1.pda_index arg2.pda_index) + abi.evdai_inputs in + + let uv3s_contract, uv3s_token0, uv3s_token1 = get_token_address log in + let uv3s_sender = + let tmp = List.nth inputs 0 in + known_address_or_evm_value_to_known_address_or_address tmp.pda_data in + let uv3s_recipient = + let tmp = List.nth inputs 1 in + known_address_or_evm_value_to_known_address_or_address tmp.pda_data in + let uv3s_amount0 = + let tmp = List.nth inputs 2 in + get_int_from_evmvalue tmp.pda_data in + let uv3s_amount1 = + let tmp = List.nth inputs 3 in + get_int_from_evmvalue tmp.pda_data in + let uv3s_sqrt_price = + let tmp = List.nth inputs 4 in + get_int_from_evmvalue tmp.pda_data in + let uv3s_liquidity = + let tmp = List.nth inputs 5 in + get_int_from_evmvalue tmp.pda_data in + let uv3s_tick = + let tmp = List.nth inputs 6 in + get_int_from_evmvalue tmp.pda_data in + + Some + { + uv3s_index = log.dtrl_index; + uv3s_contract; + uv3s_sender; + uv3s_recipient; + uv3s_amount0; + uv3s_amount1; + uv3s_sqrt_price; + uv3s_liquidity; + uv3s_tick; + uv3s_token0; + uv3s_token1; + } + with e -> + Log.log @@ Printexc.to_string e ; + None + +let v2_event_id = + b "d78ad95fa46c994b6551d0da85fc275fe613ce37657fb8d5e3d130840159d822" + +let v3_event_id = + b "c42079f94a6350d7e6235f29174924f928cc2ac818eb64fed8004e115fbcca67" + +let v3_event_mint_id = + b "7a53080ba414158be7ec69b987b5fb7d07dee101fe85488f0853ae16239d0bde" + +let v3_event_burn_id = + b "0c396cd989a39f4459b5fa1aed6a9a8dcdbc45908acfd67e028cd568da98982c" + +let v3_event_collect_id = + b "70935338e69775456a85ddef226c395fb668b63fa0115f5f20610b388e6ca9c0" + +let get_v2_swaps tx_logs = get_swap tx_logs v2_event_id decode_abi_v2 + +let get_v3_swaps tx_logs = get_swap tx_logs v3_event_id decode_abi_v3 + +let get_v3_mint tx_logs = get_swap tx_logs v3_event_mint_id decode_abi_mint_v3 + +let get_v3_burn tx_logs = get_swap tx_logs v3_event_burn_id decode_abi_burn_v3 + +let get_v3_collect tx_logs = + get_swap tx_logs v3_event_collect_id decode_abi_collect_v3 + +let add_swap acc_swap swap_list = + List.fold_left + (fun acc (bool_swap, swap) -> + match bool_swap with + | true -> acc + | false -> swap :: acc) + acc_swap swap_list + +let add_or_replace_information hashtbl key arg = + let adding = + match Hashtbl.find_opt hashtbl key with + | None -> arg + | Some s -> ( + match (arg, s) with + | `swapV2 l1, `swapV2 l2 -> `swapV2 (concat_list l1 l2) + | `swapV3 l1, `swapV3 l2 -> `swapV3 (concat_list l1 l2) + | `mint l1, `mint l2 -> `mint (concat_list l1 l2) + | `burn l1, `burn l2 -> `burn (concat_list l1 l2) + | `collect l1, `collect l2 -> `collect (concat_list l1 l2) + | _ -> Log.log_error_fail ~here:[%here] "get_event sandwich fail") in + Hashtbl.replace hashtbl key adding + +let rec get_pair_swaps_v2_v3 events_function log hashtbl = + let _ = events_function log hashtbl in + let _ = + List.iter + (fun arg -> get_pair_swaps_v2_v3 events_function arg hashtbl) + log.dtr_calls in + () diff --git a/src/ethereum_analysis/eth_analysis_tools/eth_tools.mli b/src/ethereum_analysis/eth_analysis_tools/eth_tools.mli index a5bbc48dc3d36b6291aebaf0f0f5bdba41a5f3de..2a1e149f801922b548e1ab56b0f2b9fc49f31622 100644 --- a/src/ethereum_analysis/eth_analysis_tools/eth_tools.mli +++ b/src/ethereum_analysis/eth_analysis_tools/eth_tools.mli @@ -331,3 +331,185 @@ val annotate_cycles : val transfer_analysis_of_cft_transfer_node_list : Common.Types.Ethereum_analysis.cft_transfer_node list -> Common.Types.Ethereum_analysis.transfer_analysis list +val get_transaction_information : + ( Common.Types.Ethereum_decode.known_address_or_address, + 'a ) + Olympus.Types.debug_trace -> + Eth.b + * Common.Types.Ethereum_decode.known_address_or_address + * Olympus.Types.index_or_position + +val get_pair_swaps_v2_v3 : + (('a, 'b) Olympus.Types.debug_trace_result -> 'c -> 'd) -> + ('a, 'b) Olympus.Types.debug_trace_result -> + 'c -> + unit + +val add_or_replace_information : + ( 'a, + ([> `burn of 'c list + | `collect of 'd list + | `mint of 'e list + | `swapV2 of 'f list + | `swapV3 of 'g list ] + as + 'b) ) + Hashtbl.t -> + 'a -> + 'b -> + unit + +val add_swap : 'a list -> (bool * 'a) list -> 'a list + +val get_v3_collect : + ( Common.Types.Ethereum_decode.known_address_or_address, + Common.Types.Ethereum_decode.known_address_or_evm_value ) + Olympus.Types.debug_trace_result_log + list -> + Common.Types.Ethereum_analysis.uni_v3_collect list + +val get_v3_burn : + ( Common.Types.Ethereum_decode.known_address_or_address, + Common.Types.Ethereum_decode.known_address_or_evm_value ) + Olympus.Types.debug_trace_result_log + list -> + Common.Types.Ethereum_analysis.uni_v3_burn list + +val get_v3_mint : + ( Common.Types.Ethereum_decode.known_address_or_address, + Common.Types.Ethereum_decode.known_address_or_evm_value ) + Olympus.Types.debug_trace_result_log + list -> + Common.Types.Ethereum_analysis.uni_v3_mint list + +val get_v3_swaps : + ( Common.Types.Ethereum_decode.known_address_or_address, + Common.Types.Ethereum_decode.known_address_or_evm_value ) + Olympus.Types.debug_trace_result_log + list -> + Common.Types.Ethereum_analysis.uni_v3_swap list + +val get_v2_swaps : + ( Common.Types.Ethereum_decode.known_address_or_address, + Common.Types.Ethereum_decode.known_address_or_evm_value ) + Olympus.Types.debug_trace_result_log + list -> + Common.Types.Ethereum_analysis.uni_v2_swap list + +val v3_event_collect_id : Eth.b + +val v2_event_id : Eth.b + +val v3_event_id : Eth.b + +val v3_event_mint_id : Eth.b + +val v3_event_burn_id : Eth.b + +val decode_abi_v3 : + ( Common.Types.Ethereum_decode.known_address_or_address, + 'a ) + Olympus.Types.debug_trace_result_log -> + Common.Types.Ethereum_decode.known_address_or_evm_value + Olympus.Types.event_decode_abi_inputs -> + Common.Types.Ethereum_analysis.uni_v3_swap option + +val decode_abi_v2 : + ( Common.Types.Ethereum_decode.known_address_or_address, + 'a ) + Olympus.Types.debug_trace_result_log -> + Common.Types.Ethereum_decode.known_address_or_evm_value + Olympus.Types.event_decode_abi_inputs -> + Common.Types.Ethereum_analysis.uni_v2_swap option + +val decode_abi_collect_v3 : + ( Common.Types.Ethereum_decode.known_address_or_address, + 'a ) + Olympus.Types.debug_trace_result_log -> + Common.Types.Ethereum_decode.known_address_or_evm_value + Olympus.Types.event_decode_abi_inputs -> + Common.Types.Ethereum_analysis.uni_v3_collect option + +val decode_abi_burn_v3 : + ( Common.Types.Ethereum_decode.known_address_or_address, + 'a ) + Olympus.Types.debug_trace_result_log -> + Common.Types.Ethereum_decode.known_address_or_evm_value + Olympus.Types.event_decode_abi_inputs -> + Common.Types.Ethereum_analysis.uni_v3_burn option + +val decode_abi_mint_v3 : + ( Common.Types.Ethereum_decode.known_address_or_address, + 'a ) + Olympus.Types.debug_trace_result_log -> + Common.Types.Ethereum_decode.known_address_or_evm_value + Olympus.Types.event_decode_abi_inputs -> + Common.Types.Ethereum_analysis.uni_v3_mint option + +val gen_swap_transaction : + Common.Types.Ethereum_decode.known_address_or_address -> + Eth.b -> + Olympus.Types.index_or_position -> + Common.Types.Ethereum_analysis.swap list -> + Common.Types.Ethereum_analysis.swap_transaction + +val general_swapV3 : + Common.Types.Ethereum_analysis.uni_v3_swap -> + Common.Types.Ethereum_analysis.swap option + +val general_swapV2 : + Common.Types.Ethereum_analysis.uni_v2_swap -> + Common.Types.Ethereum_analysis.swap + +val get_int_from_evmvalue : + Common.Types.Ethereum_decode.known_address_or_evm_value -> + Common.Types.Ethereum_analysis.z + +val get_token_address : + ( Common.Types.Ethereum_decode.known_address_or_address, + 'a ) + Olympus.Types.debug_trace_result_log -> + Common.Types.Ethereum_decode.contract_information + * Common.Types.Ethereum_decode.contract_information + * Common.Types.Ethereum_decode.contract_information + +val get_contract_info_from_known_address_or_address : + Common.Types.Ethereum_decode.known_address_or_address -> + Common.Types.Ethereum_decode.contract_information option + +val filter_swap : + Common.Types.Ethereum_analysis.token_swapped -> + Common.Types.Ethereum_analysis.swap list -> + Common.Types.Ethereum_analysis.swap list + +val list_uniq_token_swapped : + Common.Types.Ethereum_analysis.token_swapped list -> + Common.Types.Ethereum_analysis.token_swapped list + +val eq_token_swapped : + Common.Types.Ethereum_analysis.token_swapped -> + Common.Types.Ethereum_analysis.token_swapped -> + bool + +val eq_inverse_token_swapped : + Common.Types.Ethereum_analysis.token_swapped -> + Common.Types.Ethereum_analysis.token_swapped -> + bool + +val get_mint_swap : + Common.Types.Ethereum_analysis.uni_v3_mint list -> + Common.Types.Ethereum_analysis.uni_v3_swap list -> + ( [> `mint], + [> `mint of Common.Types.Ethereum_analysis.uni_v3_mint list] ) + Hashtbl.t -> + (bool * Common.Types.Ethereum_analysis.uni_v3_swap) list + * Common.Types.Ethereum_analysis.uni_v3_mint list + +val get_burn_swap : + Common.Types.Ethereum_analysis.uni_v3_burn list -> + Common.Types.Ethereum_analysis.uni_v3_swap list -> + ( [> `burn], + [> `burn of Common.Types.Ethereum_analysis.uni_v3_burn list] ) + Hashtbl.t -> + (bool * Common.Types.Ethereum_analysis.uni_v3_swap) list + * Common.Types.Ethereum_analysis.uni_v3_burn list diff --git a/src/ethereum_decode/eth_decode.ml b/src/ethereum_decode/eth_decode.ml index 911742bd451dec82aa5750cae2671a024b72606a..8fe0a411f0c6be43b2872897eed21b07492c9512 100644 --- a/src/ethereum_decode/eth_decode.ml +++ b/src/ethereum_decode/eth_decode.ml @@ -439,6 +439,7 @@ module Decode (Decode_address : Decode_address_signature) : Decode_type = struct in Lwt.return @@ A_FlashLoan { tx_analysis with txao_output = log_decode } | A_Sandwich s -> Lwt.return (A_Sandwich s) + | A_Sandwich_liquidity s -> Lwt.return (A_Sandwich_liquidity s) | A_Transfer t -> Lwt.return (A_Transfer t) | A_Arbitrage a -> Lwt.return (A_Arbitrage a) diff --git a/src/ethereum_indexer/eth_db_psql/eth_db_psql_analysis.ml b/src/ethereum_indexer/eth_db_psql/eth_db_psql_analysis.ml index b35792833a596cee6a641b73d7cbdef6e9012abf..bc9b5335f694726d376685dbc4542374a41e9afc 100644 --- a/src/ethereum_indexer/eth_db_psql/eth_db_psql_analysis.ml +++ b/src/ethereum_indexer/eth_db_psql/eth_db_psql_analysis.ml @@ -18,6 +18,7 @@ let analysis_type_to_integer (analysis : [> bottom eth_analysis_type]) = | `sandwich -> 2 | `transfer -> 3 | `arbitrage -> 4 + | `sandwich_liquidity -> 5 | `bottom _ -> . let integer_to_analysis_type i64 = @@ -36,6 +37,7 @@ let analysis_to_analysis_type (analysis : 'a analysis) = | A_Liquidate _ -> `liquidate | A_Sandwich _ -> `sandwich | A_Arbitrage _ -> `arbitrage + | A_Sandwich_liquidity _ -> `sandwich_liquidity let add_sandwich_swap_transaction_swap ?dbh ~(swap : swap) ~st_id () = Log.log_func_lwt __FUNCTION__ "" @@ fun () -> @@ -318,7 +320,11 @@ VALUES (decode($tx_hash,'hex'),${block_number},${analysis_type_integer},${tag_id "Transfer analysis not yet stored in the db" | A_Arbitrage _trace_analysis -> Log.log_warning_fail ~here:[%here] - "Transfer analysis not yet stored in the db" + "Arbitrage analysis not yet stored in the db" + | A_Sandwich_liquidity _sandwich_liquidity -> + (* TODO *) + Log.log_warning_fail ~here:[%here] + "Sandwich liquidity analysis not yet stored in the db" module Sandwich_fetched_id = Set.Make (Int64) module Analysis_map = Map.Make (Int) @@ -334,6 +340,8 @@ let build_tx_output_list (analyses : 'a analysis list) = Analysis_map.add (analysis_type_to_integer `transfer) analysis acc | A_Arbitrage _ -> Analysis_map.add (analysis_type_to_integer `arbitrage) analysis acc + (* TODO *) + | A_Sandwich_liquidity _ -> acc | A_Sandwich _ -> Analysis_map.add (analysis_type_to_integer `sandwich) analysis acc | A_FlashLoan flashloan -> ( @@ -345,8 +353,11 @@ let build_tx_output_list (analyses : 'a analysis list) = Analysis_map.add (analysis_type_to_integer `flashloan) analysis acc | Some analysis_2 -> ( match analysis_2 with - | A_Sandwich _ | A_Liquidate _ | A_Transfer _ | A_Arbitrage _ -> - Log.log_error_fail ~here:[%here] "Bottom" + | A_Sandwich_liquidity _ + | A_Sandwich _ + | A_Liquidate _ + | A_Transfer _ + | A_Arbitrage _ -> Log.log_error_fail ~here:[%here] "Bottom" | A_FlashLoan flashloan_all -> let result = { @@ -366,8 +377,11 @@ let build_tx_output_list (analyses : 'a analysis list) = Analysis_map.add (analysis_type_to_integer `liquidate) analysis acc | Some analysis_2 -> ( match analysis_2 with - | A_Sandwich _ | A_FlashLoan _ | A_Transfer _ | A_Arbitrage _ -> - Log.log_error_fail ~here:[%here] "Bottom" + | A_Sandwich_liquidity _ + | A_Sandwich _ + | A_FlashLoan _ + | A_Transfer _ + | A_Arbitrage _ -> Log.log_error_fail ~here:[%here] "Bottom" | A_Liquidate liquidate_all -> let result = { @@ -388,6 +402,8 @@ let normalize_analyses (analyses : 'a analysis list) = match analysis with | A_Sandwich _ | A_Transfer _ | A_Arbitrage _ -> (analysis :: sd_acc, ot_acc) + (* TODO *) + | A_Sandwich_liquidity _ -> (sd_acc, ot_acc) | A_FlashLoan _ | A_Liquidate _ -> (sd_acc, analysis :: ot_acc)) ([], []) analyses in Tools.concat_list sandwiches @@ -400,6 +416,8 @@ let normalize_analyses (analyses : 'a analysis list) = @@ List.fold_left (fun acc analysis -> match analysis with + (* TODO *) + | A_Sandwich_liquidity _ -> acc | A_Transfer _ -> acc | A_Sandwich _ -> acc | A_Arbitrage _ -> acc diff --git a/test/test_common/test_common.ml b/test/test_common/test_common.ml index b702809b93d2257dd222c7e0942428b5d420dd00..265442344b33a5c0f488d7328e14e25f2aa269dd 100644 --- a/test/test_common/test_common.ml +++ b/test/test_common/test_common.ml @@ -125,6 +125,9 @@ module Test_tools = struct bat_block_number = sandwich.bao_block_number; bat_resume = sandwich_output_to_sandwich_test sandwich.bao_resume; } + | A_Sandwich_liquidity _ -> + Log.log_warning_fail ~here:[%here] + "Sandwich liquidity analysis has still no tests designed for it." | A_Transfer _ -> Log.log_warning_fail ~here:[%here] "Transfer analysis has still no tests designed for it."