From ba37beee2ac2558303555b91968cb85b4913d10c Mon Sep 17 00:00:00 2001 From: "rayane.lattari" Date: Mon, 7 Jul 2025 11:42:22 +0200 Subject: [PATCH 01/33] [Ethereum-analysis-sandwich-liquidity] Add mint and burn types --- src/common/types.ml | 62 ++++++++++++++++++++++++++++++++------------- 1 file changed, 45 insertions(+), 17 deletions(-) diff --git a/src/common/types.ml b/src/common/types.ml index 46a64c8d..972e3477 100644 --- a/src/common/types.ml +++ b/src/common/types.ml @@ -410,6 +410,18 @@ 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; @@ -418,10 +430,11 @@ module Ethereum_analysis = struct s_out_token : int; 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] + type token_swapped = { ts_in_token : int; ts_out_token : int; @@ -449,7 +462,7 @@ module Ethereum_analysis = struct uv2s_token0 : int; uv2s_token1 : int; } - [@@deriving encoding { camel; remove_prefix = "uv2s_" }] + [@@deriving encoding { camel; remove_prefix = "uv2s_" }, eq] type uni_v3_swap = { uv3s_contract : contract_information; @@ -463,11 +476,38 @@ module Ethereum_analysis = struct 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_contract : Ethereum_indexer.db_contract_information; + uv3m_sender : Ethereum_indexer.known_address_or_address; + uv3m_owner : Ethereum_indexer.known_address_or_address; + uv3m_token0 : Ethereum_indexer.db_contract_information; + uv3m_token1 : Ethereum_indexer.db_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_" }] + + type uni_v3_burn = { + uv3b_contract : Ethereum_indexer.db_contract_information; + uv3b_owner : Ethereum_indexer.known_address_or_address; + uv3b_tick_lower : z; + uv3b_tick_upper : z; + uv3b_amount : z; + uv3b_amount0 : z; + uv3b_amount1 : z; + uv3b_token0 : Ethereum_indexer.db_contract_information; + uv3b_token1 : Ethereum_indexer.db_contract_information; + } + [@@deriving encoding { camel; remove_prefix = "uv3b_" }] 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] @@ -486,18 +526,6 @@ 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.*) -- GitLab From 11b4c0bbba67f95aaa1225632da3de15e601efe4 Mon Sep 17 00:00:00 2001 From: "rayane.lattari" Date: Wed, 9 Jul 2025 17:19:59 +0200 Subject: [PATCH 02/33] [Ethereum-analysis-sandwich-liquidity] Add collect types --- src/common/types.ml | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/common/types.ml b/src/common/types.ml index 972e3477..08743035 100644 --- a/src/common/types.ml +++ b/src/common/types.ml @@ -505,6 +505,19 @@ module Ethereum_analysis = struct } [@@deriving encoding { camel; remove_prefix = "uv3b_" }] + type uni_v3_collect = { + uv3c_contract : Ethereum_indexer.db_contract_information; + uv3c_recipient : Ethereum_indexer.known_address_or_address; + uv3c_owner : Ethereum_indexer.known_address_or_address; + uv3c_tick_upper : z; + uv3c_tick_lower : z; + uv3c_amount0 : z; + uv3c_amount1 : z; + uv3c_token0 : Ethereum_indexer.db_contract_information; + uv3c_token1 : Ethereum_indexer.db_contract_information; + } + [@@deriving encoding { camel; remove_prefix = "uv3c_" }] + type token_ratio_swap = { trs_swap_transaction : swap_transaction; trs_ratio : (z * z) list; -- GitLab From b86c8466b5b671068c3889e336ce5289037039d9 Mon Sep 17 00:00:00 2001 From: "rayane.lattari" Date: Wed, 9 Jul 2025 17:22:46 +0200 Subject: [PATCH 03/33] [Ethereum-analysis-sandwich-liquidity] Add types and first implementation --- .../eth_analysis_sandwich.ml | 601 +++++++++++++++++- 1 file changed, 577 insertions(+), 24 deletions(-) diff --git a/src/ethereum_analysis/eth_analysis_sandwich.ml b/src/ethereum_analysis/eth_analysis_sandwich.ml index 7e079773..fe46003d 100644 --- a/src/ethereum_analysis/eth_analysis_sandwich.ml +++ b/src/ethereum_analysis/eth_analysis_sandwich.ml @@ -1,9 +1,35 @@ open Eth -open Common +open Common open Types.Ethereum_analysis open Types.Ethereum_decode -open Tools +open Types.Ethereum_indexer open Olympus.Types +open Tools + + +type swap_transaction_mint = { + stm_transaction_hash : b; + stm_index : bint; + stm_sender : known_address_or_address; + stm_mint_list : uni_v3_mint list; +} +[@@deriving encoding { camel; remove_prefix = "stm_" }, eq] + +type swap_transaction_burn = { + stb_transaction_hash : b; + stb_index : bint; + stb_sender : known_address_or_address; + stb_burn_list : uni_v3_burn list; +} +[@@deriving encoding { camel; remove_prefix = "stb_" }, eq] + +type swap_transaction_collect = { + stc_transaction_hash : b; + stc_index : bint; + stc_sender : known_address_or_address; + stc_collect_list : uni_v3_collect list; +} +[@@deriving encoding { camel; remove_prefix = "stc_" }, eq] let eq_inverse_token_swapped ts1 ts2 = ts1.ts_out_token = ts2.ts_in_token @@ -36,6 +62,27 @@ let v2_event_id = let v3_event_id = "c42079f94a6350d7e6235f29174924f928cc2ac818eb64fed8004e115fbcca67" +let v3_event_mint_id = + b "7a53080ba414158be7ec69b987b5fb7d07dee101fe85488f0853ae16239d0bde" + +let v3_event_burn_id = + b "0c396cd989a39f4459b5fa1aed6a9a8dcdbc45908acfd67e028cd568da98982c" + +let v3_event_collect_id = + b "70935338e69775456a85ddef226c395fb668b63fa0115f5f20610b388e6ca9c0" + +let get_address_from_evmvalue + (contract_or_evm_value : known_address_or_evm_value) = + match contract_or_evm_value with + | Coev_known_address contract_info -> Coadr_known_address contract_info + | Coev_evm_value evm_value -> ( + match evm_value with + | `address a -> Coadr_address a + | _ as e -> + failwith + @@ Format.sprintf "fail get_address_from_evmvalue %s" + @@ EzEncoding.construct Eth.evm_value_enc e) + 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 -> ( @@ -100,6 +147,180 @@ let decode_abi_v2 event_hash abi s_contract = s_contract; } with _ -> None +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 + get_address_from_evmvalue tmp.pda_data in + let uv3m_owner = + let tmp = List.nth inputs 1 in + get_address_from_evmvalue 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_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 + get_address_from_evmvalue 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_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 + get_address_from_evmvalue tmp.pda_data in + let uv3c_recipient = + let tmp = List.nth inputs 1 in + get_address_from_evmvalue 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_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 + get_address_from_evmvalue 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 + get_address_from_evmvalue tmp.pda_data in + + Some + { + 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 event_hash abi s_contract = try @@ -152,15 +373,19 @@ let decode_abi_v3 event_hash abi s_contract = s_contract; } with _ -> None + let inputs = + List.sort + (fun arg1 arg2 -> compare arg1.pda_index arg2.pda_index) + abi.evdai_inputs in let get_v2_swap log = - match log.evdri_decode with + match log.dtrl_decode_signature with | None -> None | Some s -> ( - let event_decode = s.evdi_decode in - match String.equal (s.evdi_hash :> string) (v2_event_id :> string) with + match String.equal (s.evdi_hash :> string) (signature :> string) with | false -> None | true -> ( + let event_decode = s.evdi_decode in match event_decode with | Event_known_contract_abi abi -> decode_abi_v2 (s.evdi_hash :> string) abi.evdka_inputs s.evdi_contract @@ -179,14 +404,143 @@ let get_v3_swap log = 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 tx_logs = + Lwt_list.filter_map_s (get_event_generic v3_event_id decode_abi_v3) tx_logs + +let get_v3_mint tx_logs = + Lwt_list.filter_map_s + (get_event_generic v3_event_mint_id decode_abi_mint_v3) + tx_logs + +let get_v3_burn tx_logs = + Lwt_list.filter_map_s + (get_event_generic v3_event_burn_id decode_abi_burn_v3) + tx_logs + +let get_v3_collect tx_logs = + Lwt_list.filter_map_s + (get_event_generic v3_event_collect_id decode_abi_collect_v3) + tx_logs + +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 find_swaps (transaction_trace : debug_trace_result) = + let rec get_pair_swaps_v2_v3 (log : debug_trace_result) + (swapv2, swapv3, v3_burn, v3_mint) = + let> swapv2', swapv3', v3_burn', v3_mint' = + let> v2 = get_v2_swaps log.dtr_logs in + let> v3 = get_v3_swaps log.dtr_logs in + let> v3_b = get_v3_burn log.dtr_logs in + let> v3_m = get_v3_mint log.dtr_logs in + Lwt.return + ( concat_list swapv2 v2, + concat_list swapv3 v3, + concat_list v3_b v3_burn, + concat_list v3_m v3_mint ) in + + let> sv2, sv3, v3_b, v3_m = + Lwt_list.fold_left_s + (fun acc arg -> + let> s2, s3, v3_b, v3_m = get_pair_swaps_v2_v3 arg acc in + Lwt.return (s2, s3, v3_b, v3_m)) + ([], [], [], []) log.dtr_calls in -let get_v3_swaps = List.filter_map get_v3_swap + Lwt.return + ( concat_list sv2 swapv2', + concat_list sv3 swapv3', + concat_list v3_b v3_burn', + concat_list v3_m v3_mint' ) in -let find_swaps events = - let v2 = get_v2_swaps events in - let v3 = get_v3_swaps events in - concat_list v2 v3 + get_pair_swaps_v2_v3 transaction_trace ([], [], [], []) + +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 }] + +let get_events (transaction_trace : debug_trace_result) = + 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) + | _ -> assert false) in + Hashtbl.replace hashtbl key adding in + let rec get_pair_swaps_v2_v3 (log : debug_trace_result) hashtbl = + let> v2 = get_v2_swaps log.dtr_logs in + let> v3 = get_v3_swaps log.dtr_logs in + let> v3_b = get_v3_burn log.dtr_logs in + let> v3_m = get_v3_mint log.dtr_logs in + let> v3_c = get_v3_collect log.dtr_logs in + add_or_replace_information hashtbl `swapV2 (`swapV2 v2) ; + add_or_replace_information hashtbl `swapV3 (`swapV3 v3) ; + add_or_replace_information hashtbl `mint (`mint v3_b) ; + add_or_replace_information hashtbl `burn (`burn v3_m) ; + add_or_replace_information hashtbl `collect (`collect v3_c) ; + + let> _ = + Lwt_list.iter_s + (fun arg -> get_pair_swaps_v2_v3 arg hashtbl) + log.dtr_calls in + + Lwt.return_unit in + + let hashtbl = Hashtbl.create 7 in + Hashtbl.add hashtbl `swapV2 (`swapV2 []) ; + Hashtbl.add hashtbl `swapV3 (`swapV3 []) ; + Hashtbl.add hashtbl `mint (`mint []) ; + Hashtbl.add hashtbl `burn (`burn []) ; + Hashtbl.add hashtbl `collect (`collect []) ; + + let> _ = get_pair_swaps_v2_v3 transaction_trace hashtbl in + Lwt.return hashtbl let gen_swap_transaction (st_sender : known_address_or_address) (st_transaction_hash : b) (st_index : bint) (st_swap_list : swap list) = @@ -326,19 +680,218 @@ 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 = - 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 +let detect_sandwich_liquidity swap_transaction_mint swap_transaction_burn + swap_transaction_collect = + (* let concat_or_add hashtbl key arg = let adding = match Hashtbl.find_opt + hashtbl key with | None -> [arg] | Some s -> arg :: s in Hashtbl.replace + hashtbl key adding in let hash_table_mint = Hashtbl.create 7 in let + hash_table_burn = Hashtbl.create 7 in let hash_table_collect = + Hashtbl.create 7 in + + List.iter (fun stm -> concat_or_add hash_table_mint stm.stm_sender stm) + swap_transaction_mint ; List.iter (fun stb -> concat_or_add hash_table_burn + stb.stb_sender stb) swap_transaction_burn ; List.iter (fun stc -> + concat_or_add hash_table_collect stc.stc_sender stc) + swap_transaction_collect ; let agregate_sender = Hashtbl.fold (fun key m + acc -> match Hashtbl.find_opt hash_table_burn key, Hashtbl.find_opt + hash_table_collect key with | Some b, Some c -> (m, b, c)::acc | _ -> acc ) + hash_table_mint ([]) in *) + let sort_m = + List.sort + (fun m1 m2 -> + if m1.stm_index > m2.stm_index then + 1 + else if m1.stm_index = m2.stm_index then + 0 + else + -1) + swap_transaction_mint in + let sort_b = + List.sort + (fun m1 m2 -> + if m1.stb_index > m2.stb_index then + 1 + else if m1.stb_index = m2.stb_index then + 0 + else + -1) + swap_transaction_burn in + + let sort_c = + List.sort + (fun m1 m2 -> + if m1.stc_index > m2.stc_index then + 1 + else if m1.stc_index = m2.stc_index then + 0 + else + -1) + swap_transaction_collect in + + let rec aux (sort_m : swap_transaction_mint) sort_b acc = + match sort_b with + | [] -> acc + | sort_b :: b -> + if + sort_b.stb_index > sort_m.stm_index + && equal_known_address_or_address sort_b.stb_sender sort_m.stm_sender + then + List.fold_left + (fun acc stm -> + + + let x = + List.find_all + (fun sb -> + equal_db_contract_information sb.uv3b_contract + stm.uv3m_contract) + sort_b.stb_burn_list in + acc) + [] sort_m.stm_mint_list + else + [] in + + (* Il faut ejecter le b *) + + (* List.fold_left (fun acc3 m' -> + + let b_tmp = List.fold_left (fun acc2 b -> + + ) [] m.stm_mint_list + + (* [] *) (* List.find_all ( fun stb -> false ) b.stb_burn_list *) + + else acc2 + + ) sort_b [] + + in [] (* ) [] sort_m; *) + + [] ) [] sort_m in *) + + (* let agregate_by_transaction = List.fold_left (fun acc m -> + + let b_tmp = List.fold_left (fun acc2 b -> if b.stb_index > m.stm_index && + equal_known_address_or_address b.stb_sender m.stm_sender then + List.fold_left (fun acc3 m' -> + + ) [] m.stm_mint_list + + (* [] *) (* List.find_all ( fun stb -> false ) b.stb_burn_list *) + + else acc2 + + ) sort_b [] + + in [] (* ) [] sort_m; *) + + [] ) [] sort_m *) + (* swap_transaction_mint *) + (* ) [] agregate_sender *) + () + +let detect_sandwich debug_trace_list = + let> ( swap_transaction_list, + swap_transaction_mint, + swap_transaction_burn, + swap_transaction_collect ) = + Lwt_list.fold_left_s + (fun (swap, st_v3_m, st_v3_b, st_v3_c) arg -> + let transaction_hash = arg.dt_tx_hash in + let sender = + match arg.dt_result.dtr_from_decode with + | None -> Coadr_address arg.dt_result.dtr_from + | Some known_address -> known_address in + let index = arg.dt_tx_index in + let> swapV2, swapV3, v3_mint, v3_burn, v3_collect = + let> hashtbl = get_events arg.dt_result 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 + + let v3_mint = + let tmp = Hashtbl.find hashtbl `mint in + match tmp with + | `mint m -> m + | _ -> assert false in + + let v3_burn = + let tmp = Hashtbl.find hashtbl `burn in + match tmp with + | `burn b -> b + | _ -> assert false in + + let v3_collect = + let tmp = Hashtbl.find hashtbl `collect in + match tmp with + | `collect c -> c + | _ -> assert false in + + Lwt.return (swapV2, swapV3, v3_burn, v3_mint, v3_collect) in + + let swap_list = + let v2 = List.map general_swapV2 swapV2 in + let v3 = List.filter_map general_swapV3 swapV3 in + concat_list v2 v3 in + + let> swap = + match swap_list with + | [] -> Lwt.return swap + | swap_list -> + Lwt.return + @@ gen_swap_transaction sender transaction_hash index swap_list + :: swap in + + let swap_transaction_mint = + match v3_mint with + | [] -> st_v3_m + | v3_mint -> + { + stm_transaction_hash = transaction_hash; + stm_index = index; + stm_sender = sender; + stm_mint_list = v3_mint; + } + :: st_v3_m in + + let swap_transaction_burn = + match v3_burn with + | [] -> st_v3_b + | v3_burn -> + { + stb_transaction_hash = transaction_hash; + stb_index = index; + stb_sender = sender; + stb_burn_list = v3_burn; + } + :: st_v3_b in + + let swap_transaction_collect = + match v3_collect 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 in + + Lwt.return + ( swap, + swap_transaction_mint, + swap_transaction_burn, + swap_transaction_collect )) + ([], [], [], []) debug_trace_list in let detect_fr_br = detect_fr_br swap_transaction_list in match detect_fr_br with | [] -> [] -- GitLab From 69d1304cef1382b5b1bfc173083d13e9280b17f7 Mon Sep 17 00:00:00 2001 From: "rayane.lattari" Date: Fri, 11 Jul 2025 15:37:25 +0200 Subject: [PATCH 04/33] [Ethereum-analysis-sandwich-liquidity] Add detect_sandwich_liquidity --- .../eth_analysis_sandwich.ml | 296 +++++++++++------- 1 file changed, 184 insertions(+), 112 deletions(-) diff --git a/src/ethereum_analysis/eth_analysis_sandwich.ml b/src/ethereum_analysis/eth_analysis_sandwich.ml index fe46003d..6ba137bd 100644 --- a/src/ethereum_analysis/eth_analysis_sandwich.ml +++ b/src/ethereum_analysis/eth_analysis_sandwich.ml @@ -31,6 +31,72 @@ type swap_transaction_collect = { } [@@deriving encoding { camel; remove_prefix = "stc_" }, eq] +type swap_transaction_mint_final = { + stmf_transaction_hash : b; + stmf_index : bint; + stmf_sender : known_address_or_address; + stmf_mint : uni_v3_mint; +} +[@@deriving encoding { camel; remove_prefix = "stmf_" }, eq] + +type swap_transaction_burn_final = { + stbf_transaction_hash : b; + stbf_index : bint; + stbf_sender : known_address_or_address; + stbf_burn : uni_v3_burn; +} +[@@deriving encoding { camel; remove_prefix = "stbf_" }, eq] + +type swap_transaction_collect_final = { + stcf_transaction_hash : b; + stcf_index : bint; + stcf_sender : known_address_or_address; + stcf_collect : uni_v3_collect; +} +[@@deriving encoding { camel; remove_prefix = "stcf_" }, 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 key_burn = { + kb_transaction_hash : b; + kb_contract : db_contract_information; + kb_tick_lower : z; + kb_tick_upper : z; +} +[@@deriving encoding { camel; remove_prefix = "kb_" }, eq] + +type burn_collect = { + bc_burn : swap_transaction_burn_final; + bc_collect : swap_transaction_collect_final; +} +[@@deriving encoding { camel; remove_prefix = "bc_" }, eq] + +type sandwich_liquidity_possible = { + slp_mint : swap_transaction_mint_final; + slp_burn : key_burn list; +} +[@@deriving encoding { camel; remove_prefix = "slp_" }, eq] + +type sandwich_liquidity = { + sl_mint : swap_transaction_mint_final; + sl_burn_collect : burn_collect list; +} +[@@deriving encoding { camel; remove_prefix = "sl_" }, eq] + +let sort index1 index2 = + if index1 > index2 then + 1 + else if index1 = index2 then + 0 + else + -1 + let eq_inverse_token_swapped ts1 ts2 = ts1.ts_out_token = ts2.ts_in_token && ts1.ts_in_token = ts2.ts_out_token @@ -491,14 +557,6 @@ let find_swaps (transaction_trace : debug_trace_result) = get_pair_swaps_v2_v3 transaction_trace ([], [], [], []) -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 }] - let get_events (transaction_trace : debug_trace_result) = let add_or_replace_information hashtbl key arg = let adding = @@ -614,13 +672,7 @@ let detect_fr_br (swap_transaction_list : swap_transaction list) = 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 -> sort swap1.st_index swap2.st_index) swap_transaction_list in inter swap_transaction_list [] @@ -682,113 +734,133 @@ let extract_sandwich_list (sandwich_possible_list : sandwich_possible list) let detect_sandwich_liquidity swap_transaction_mint swap_transaction_burn swap_transaction_collect = - (* let concat_or_add hashtbl key arg = let adding = match Hashtbl.find_opt - hashtbl key with | None -> [arg] | Some s -> arg :: s in Hashtbl.replace - hashtbl key adding in let hash_table_mint = Hashtbl.create 7 in let - hash_table_burn = Hashtbl.create 7 in let hash_table_collect = - Hashtbl.create 7 in - - List.iter (fun stm -> concat_or_add hash_table_mint stm.stm_sender stm) - swap_transaction_mint ; List.iter (fun stb -> concat_or_add hash_table_burn - stb.stb_sender stb) swap_transaction_burn ; List.iter (fun stc -> - concat_or_add hash_table_collect stc.stc_sender stc) - swap_transaction_collect ; let agregate_sender = Hashtbl.fold (fun key m - acc -> match Hashtbl.find_opt hash_table_burn key, Hashtbl.find_opt - hash_table_collect key with | Some b, Some c -> (m, b, c)::acc | _ -> acc ) - hash_table_mint ([]) in *) + let rec find_opt_final burn uni_v3_collect_list = + match uni_v3_collect_list with + | [] -> None + | c::uv3cl -> + if not ( + c.uv3c_contract = burn.stbf_burn.uv3b_contract && + Z.equal burn.stbf_burn.uv3b_tick_upper c.uv3c_tick_upper && + Z.equal burn.stbf_burn.uv3b_tick_lower c.uv3c_tick_lower) then + find_opt_final burn uv3cl + else + Some { + stcf_transaction_hash = burn.stbf_transaction_hash ; + stcf_index = burn.stbf_index; + stcf_sender = burn.stbf_sender; + stcf_collect = c; + } + in + let find_collect hashtbl sort_c = + Hashtbl.iter + (fun key (burn, _) -> + let collect_swap_transac = + List.find_opt + (fun c -> burn.stbf_transaction_hash = c.stc_transaction_hash) + sort_c in + match collect_swap_transac with + | None -> () + | Some collect_swap_transac -> + let collect = find_opt_final burn collect_swap_transac.stc_collect_list in + Hashtbl.replace hashtbl key (burn, collect)) + hashtbl in + + let rec aux (sort_m : swap_transaction_mint) sort_b hashtbl = + List.fold_left + (fun acc_slp sort_b -> + if + not + (sort_b.stb_index > sort_m.stm_index + && equal_known_address_or_address sort_b.stb_sender + sort_m.stm_sender) + then + acc_slp + else + List.fold_left + (fun acc_slp stm -> + let filterer_uni_v3_burn_list = + List.find_all + (fun sb -> + equal_db_contract_information sb.uv3b_contract + stm.uv3m_contract + && equal_z sb.uv3b_tick_upper stm.uv3m_tick_upper + && equal_z sb.uv3b_tick_lower stm.uv3m_tick_lower) + sort_b.stb_burn_list in + + let kb_list = + List.fold_left + (fun acc_kb arg -> + let kb = + { + kb_transaction_hash = sort_b.stb_transaction_hash; + kb_contract = arg.uv3b_contract; + kb_tick_lower = arg.uv3b_tick_lower; + kb_tick_upper = arg.uv3b_tick_upper; + } in + let swap_transaction_burn_final = + { + stbf_transaction_hash = sort_b.stb_transaction_hash; + stbf_index = sort_b.stb_index; + stbf_sender = sort_b.stb_sender; + stbf_burn = arg; + } in + + Hashtbl.replace hashtbl kb + (swap_transaction_burn_final, None) ; + kb :: acc_kb) + [] filterer_uni_v3_burn_list in + match kb_list with + | [] -> acc_slp + | slp_burn -> + let slp_mint = + { + stmf_transaction_hash = sort_m.stm_transaction_hash; + stmf_index = sort_m.stm_index; + stmf_sender = sort_m.stm_sender; + stmf_mint = stm; + } in + { slp_mint; slp_burn } :: acc_slp) + acc_slp sort_m.stm_mint_list) + [] sort_b in + let sort_m = List.sort - (fun m1 m2 -> - if m1.stm_index > m2.stm_index then - 1 - else if m1.stm_index = m2.stm_index then - 0 - else - -1) + (fun m1 m2 -> sort m1.stm_index m2.stm_index) swap_transaction_mint in let sort_b = List.sort - (fun m1 m2 -> - if m1.stb_index > m2.stb_index then - 1 - else if m1.stb_index = m2.stb_index then - 0 - else - -1) + (fun m1 m2 -> sort m1.stb_index m2.stb_index) swap_transaction_burn in - let sort_c = List.sort - (fun m1 m2 -> - if m1.stc_index > m2.stc_index then - 1 - else if m1.stc_index = m2.stc_index then - 0 - else - -1) + (fun m1 m2 -> sort m1.stc_index m2.stc_index) swap_transaction_collect in - let rec aux (sort_m : swap_transaction_mint) sort_b acc = - match sort_b with - | [] -> acc - | sort_b :: b -> - if - sort_b.stb_index > sort_m.stm_index - && equal_known_address_or_address sort_b.stb_sender sort_m.stm_sender - then - List.fold_left - (fun acc stm -> - - - let x = - List.find_all - (fun sb -> - equal_db_contract_information sb.uv3b_contract - stm.uv3m_contract) - sort_b.stb_burn_list in - acc) - [] sort_m.stm_mint_list - else - [] in - - (* Il faut ejecter le b *) - - (* List.fold_left (fun acc3 m' -> - - let b_tmp = List.fold_left (fun acc2 b -> - - ) [] m.stm_mint_list - - (* [] *) (* List.find_all ( fun stb -> false ) b.stb_burn_list *) - - else acc2 - - ) sort_b [] - - in [] (* ) [] sort_m; *) - - [] ) [] sort_m in *) - - (* let agregate_by_transaction = List.fold_left (fun acc m -> - - let b_tmp = List.fold_left (fun acc2 b -> if b.stb_index > m.stm_index && - equal_known_address_or_address b.stb_sender m.stm_sender then - List.fold_left (fun acc3 m' -> - - ) [] m.stm_mint_list - - (* [] *) (* List.find_all ( fun stb -> false ) b.stb_burn_list *) - - else acc2 - - ) sort_b [] - - in [] (* ) [] sort_m; *) - - [] ) [] sort_m *) - (* swap_transaction_mint *) - (* ) [] agregate_sender *) - () + let hashtbl = Hashtbl.create 7 in + let sandwich_liquidity_possible_list = + List.fold_left + (fun acc slp_mint -> + match aux slp_mint sort_b hashtbl with + | [] -> acc + | slp -> List.fold_left (fun acc arg -> arg::acc) acc slp ) [] sort_m in + + find_collect hashtbl sort_c; + + + (* ignore @@ *) + List.fold_left (fun result sandwich_liquidity_possible -> + let sl_burn_collect = + List.fold_left (fun acc key_burn -> + (* let tmp = *) + match Hashtbl.find_opt hashtbl key_burn with + | None | Some (_, None ) -> acc + | Some (bc_burn, Some bc_collect) -> + {bc_burn; bc_collect} :: acc ) [] sandwich_liquidity_possible.slp_burn + in + { sl_burn_collect; sl_mint = sandwich_liquidity_possible.slp_mint }::result + ) [] sandwich_liquidity_possible_list + (* ; + () *) let detect_sandwich debug_trace_list = let> ( swap_transaction_list, -- GitLab From 7f14ab01e56bd7a80399930c9113d4909d9321c7 Mon Sep 17 00:00:00 2001 From: "rayane.lattari" Date: Wed, 16 Jul 2025 16:09:27 +0200 Subject: [PATCH 05/33] [Ethereum-analysis-sandwich-liquidity] finalyze analysis --- .../eth_analysis_sandwich.ml | 125 ++++++++++-------- 1 file changed, 71 insertions(+), 54 deletions(-) diff --git a/src/ethereum_analysis/eth_analysis_sandwich.ml b/src/ethereum_analysis/eth_analysis_sandwich.ml index 6ba137bd..3c3bbb0f 100644 --- a/src/ethereum_analysis/eth_analysis_sandwich.ml +++ b/src/ethereum_analysis/eth_analysis_sandwich.ml @@ -72,8 +72,12 @@ type key_burn = { [@@deriving encoding { camel; remove_prefix = "kb_" }, eq] type burn_collect = { - bc_burn : swap_transaction_burn_final; - bc_collect : swap_transaction_collect_final; + bc_transaction_hash : b; + bc_index : bint; + bc_sender : known_address_or_address; + bc_burn : uni_v3_burn; + bc_collect : uni_v3_collect; + bc_profit : token_profit * token_profit; } [@@deriving encoding { camel; remove_prefix = "bc_" }, eq] @@ -89,14 +93,6 @@ type sandwich_liquidity = { } [@@deriving encoding { camel; remove_prefix = "sl_" }, eq] -let sort index1 index2 = - if index1 > index2 then - 1 - else if index1 = index2 then - 0 - else - -1 - let eq_inverse_token_swapped ts1 ts2 = ts1.ts_out_token = ts2.ts_in_token && ts1.ts_in_token = ts2.ts_out_token @@ -672,7 +668,7 @@ let detect_fr_br (swap_transaction_list : swap_transaction list) = let swap_transaction_list = List.sort - (fun swap1 swap2 -> sort swap1.st_index swap2.st_index) + (fun swap1 swap2 -> compare swap1.st_index swap2.st_index) swap_transaction_list in inter swap_transaction_list [] @@ -734,24 +730,21 @@ let extract_sandwich_list (sandwich_possible_list : sandwich_possible list) let detect_sandwich_liquidity swap_transaction_mint swap_transaction_burn swap_transaction_collect = - let rec find_opt_final burn uni_v3_collect_list = - match uni_v3_collect_list with - | [] -> None - | c::uv3cl -> - if not ( - c.uv3c_contract = burn.stbf_burn.uv3b_contract && - Z.equal burn.stbf_burn.uv3b_tick_upper c.uv3c_tick_upper && - Z.equal burn.stbf_burn.uv3b_tick_lower c.uv3c_tick_lower) then - find_opt_final burn uv3cl - else - Some { - stcf_transaction_hash = burn.stbf_transaction_hash ; - stcf_index = burn.stbf_index; - stcf_sender = burn.stbf_sender; - stcf_collect = c; - } - in + let rec find_opt_final burn uni_v3_collect_list = + match uni_v3_collect_list with + | [] -> None + | c :: uv3cl -> + if + not + (c.uv3c_contract = burn.stbf_burn.uv3b_contract + && Z.equal burn.stbf_burn.uv3b_tick_upper c.uv3c_tick_upper + && Z.equal burn.stbf_burn.uv3b_tick_lower c.uv3c_tick_lower) + then + find_opt_final burn uv3cl + else + Some c in let find_collect hashtbl sort_c = + let final_hashtbl = Hashtbl.create 7 in Hashtbl.iter (fun key (burn, _) -> let collect_swap_transac = @@ -760,10 +753,12 @@ let detect_sandwich_liquidity swap_transaction_mint swap_transaction_burn sort_c in match collect_swap_transac with | None -> () - | Some collect_swap_transac -> - let collect = find_opt_final burn collect_swap_transac.stc_collect_list in - Hashtbl.replace hashtbl key (burn, collect)) - hashtbl in + | Some collect_swap_transac -> ( + match find_opt_final burn collect_swap_transac.stc_collect_list with + | None -> () + | Some collect -> Hashtbl.replace final_hashtbl key (burn, collect))) + hashtbl ; + final_hashtbl in let rec aux (sort_m : swap_transaction_mint) sort_b hashtbl = List.fold_left @@ -825,15 +820,15 @@ let detect_sandwich_liquidity swap_transaction_mint swap_transaction_burn let sort_m = List.sort - (fun m1 m2 -> sort m1.stm_index m2.stm_index) + (fun m1 m2 -> compare m1.stm_index m2.stm_index) swap_transaction_mint in let sort_b = List.sort - (fun m1 m2 -> sort m1.stb_index m2.stb_index) + (fun m1 m2 -> compare m1.stb_index m2.stb_index) swap_transaction_burn in let sort_c = List.sort - (fun m1 m2 -> sort m1.stc_index m2.stc_index) + (fun m1 m2 -> compare m1.stc_index m2.stc_index) swap_transaction_collect in let hashtbl = Hashtbl.create 7 in @@ -842,25 +837,47 @@ let detect_sandwich_liquidity swap_transaction_mint swap_transaction_burn (fun acc slp_mint -> match aux slp_mint sort_b hashtbl with | [] -> acc - | slp -> List.fold_left (fun acc arg -> arg::acc) acc slp ) [] sort_m in - - find_collect hashtbl sort_c; - - - (* ignore @@ *) - List.fold_left (fun result sandwich_liquidity_possible -> - let sl_burn_collect = - List.fold_left (fun acc key_burn -> - (* let tmp = *) - match Hashtbl.find_opt hashtbl key_burn with - | None | Some (_, None ) -> acc - | Some (bc_burn, Some bc_collect) -> - {bc_burn; bc_collect} :: acc ) [] sandwich_liquidity_possible.slp_burn - in - { sl_burn_collect; sl_mint = sandwich_liquidity_possible.slp_mint }::result - ) [] sandwich_liquidity_possible_list - (* ; - () *) + | slp -> List.fold_left (fun acc arg -> arg :: acc) acc slp) + [] sort_m in + + let hashtbl_final = find_collect hashtbl sort_c in + + List.fold_left + (fun result sandwich_liquidity_possible -> + let sl_burn_collect = + List.fold_left + (fun acc key_burn -> + match Hashtbl.find_opt hashtbl_final key_burn with + | None -> acc + | Some (burn, bc_collect) -> + let profit0 = + { + tp_token = bc_collect.uv3c_token0; + tp_profit = + Z.sub bc_collect.uv3c_amount0 burn.stbf_burn.uv3b_amount0; + } in + + let profit1 = + { + tp_token = bc_collect.uv3c_token1; + tp_profit = + Z.sub bc_collect.uv3c_amount1 burn.stbf_burn.uv3b_amount1; + } in + + { + bc_transaction_hash = burn.stbf_transaction_hash; + bc_index = burn.stbf_index; + bc_sender = burn.stbf_sender; + bc_burn = burn.stbf_burn; + bc_collect; + bc_profit = (profit0, profit1); + } + :: acc) + [] sandwich_liquidity_possible.slp_burn in + + { sl_burn_collect; sl_mint = sandwich_liquidity_possible.slp_mint } + :: result) + [] sandwich_liquidity_possible_list let detect_sandwich debug_trace_list = let> ( swap_transaction_list, -- GitLab From b4c36da37a6d954dab64d3bf49ca36de2f7da32a Mon Sep 17 00:00:00 2001 From: "rayane.lattari" Date: Thu, 17 Jul 2025 13:28:02 +0200 Subject: [PATCH 06/33] [Ethereum-analysis-sandwich-liquidity] Fix and moving types to types.ml --- src/common/types.ml | 100 +++++++++++- .../eth_analysis_sandwich.ml | 143 ++++-------------- 2 files changed, 122 insertions(+), 121 deletions(-) diff --git a/src/common/types.ml b/src/common/types.ml index 08743035..538bbaf4 100644 --- a/src/common/types.ml +++ b/src/common/types.ml @@ -490,7 +490,7 @@ module Ethereum_analysis = struct uv3m_tick_upper : z; uv3m_tick_lower : z; } - [@@deriving encoding { camel; remove_prefix = "uv3m_" }] + [@@deriving encoding { camel; remove_prefix = "uv3m_" }, eq] type uni_v3_burn = { uv3b_contract : Ethereum_indexer.db_contract_information; @@ -503,7 +503,7 @@ module Ethereum_analysis = struct uv3b_token0 : Ethereum_indexer.db_contract_information; uv3b_token1 : Ethereum_indexer.db_contract_information; } - [@@deriving encoding { camel; remove_prefix = "uv3b_" }] + [@@deriving encoding { camel; remove_prefix = "uv3b_" }, eq] type uni_v3_collect = { uv3c_contract : Ethereum_indexer.db_contract_information; @@ -516,7 +516,7 @@ module Ethereum_analysis = struct uv3c_token0 : Ethereum_indexer.db_contract_information; uv3c_token1 : Ethereum_indexer.db_contract_information; } - [@@deriving encoding { camel; remove_prefix = "uv3c_" }] + [@@deriving encoding { camel; remove_prefix = "uv3c_" }, eq] type token_ratio_swap = { trs_swap_transaction : swap_transaction; @@ -524,9 +524,101 @@ module Ethereum_analysis = struct } [@@deriving encoding { camel; remove_prefix = "trs_" }, eq] + type token_profit = { + tp_token : Ethereum_indexer.db_contract_information; + tp_profit : z; + } + [@@deriving encoding { camel; remove_prefix = "tp_" }, eq] + + type swap_transaction_mint = { + stm_transaction_hash : b; + stm_index : int; + stm_sender : Ethereum_indexer.known_address_or_address; + stm_mint_list : uni_v3_mint list; + } + [@@deriving encoding { camel; remove_prefix = "stm_" }, eq] + + type swap_transaction_burn = { + stb_transaction_hash : b; + stb_index : int; + stb_sender : Ethereum_indexer.known_address_or_address; + stb_burn_list : uni_v3_burn list; + } + [@@deriving encoding { camel; remove_prefix = "stb_" }, eq] + + type swap_transaction_collect = { + stc_transaction_hash : b; + stc_index : int; + stc_sender : Ethereum_indexer.known_address_or_address; + stc_collect_list : uni_v3_collect list; + } + [@@deriving encoding { camel; remove_prefix = "stc_" }, eq] + + type swap_transaction_mint_final = { + stmf_transaction_hash : b; + stmf_index : int; + stmf_sender : Ethereum_indexer.known_address_or_address; + stmf_mint : uni_v3_mint; + } + [@@deriving encoding { camel; remove_prefix = "stmf_" }, eq] + + type swap_transaction_burn_final = { + stbf_transaction_hash : b; + stbf_index : int; + stbf_sender : Ethereum_indexer.known_address_or_address; + stbf_burn : uni_v3_burn; + } + [@@deriving encoding { camel; remove_prefix = "stbf_" }, eq] + + type swap_transaction_collect_final = { + stcf_transaction_hash : b; + stcf_index : int; + stcf_sender : Ethereum_indexer.known_address_or_address; + stcf_collect : uni_v3_collect; + } + [@@deriving encoding { camel; remove_prefix = "stcf_" }, 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 key_burn = { + kb_transaction_hash : b; + kb_contract : Ethereum_indexer.db_contract_information; + kb_tick_lower : z; + kb_tick_upper : z; + } + [@@deriving encoding { camel; remove_prefix = "kb_" }, eq] + + type burn_collect = { + bc_transaction_hash : b; + bc_index : int; + bc_sender : Ethereum_indexer.known_address_or_address; + bc_burn : uni_v3_burn; + bc_collect : uni_v3_collect; + bc_profit : token_profit * token_profit; + } + [@@deriving encoding { camel; remove_prefix = "bc_" }, eq] + + type sandwich_liquidity_possible = { + slp_mint : swap_transaction_mint_final; + slp_burn : key_burn list; + } + [@@deriving encoding { camel; remove_prefix = "slp_" }, eq] + + type sandwich_liquidity = { + sl_mint : swap_transaction_mint_final; + sl_burn_collect : burn_collect list; + } + [@@deriving encoding { camel; remove_prefix = "sl_" }, 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; } diff --git a/src/ethereum_analysis/eth_analysis_sandwich.ml b/src/ethereum_analysis/eth_analysis_sandwich.ml index 3c3bbb0f..734eea0d 100644 --- a/src/ethereum_analysis/eth_analysis_sandwich.ml +++ b/src/ethereum_analysis/eth_analysis_sandwich.ml @@ -1,3 +1,4 @@ +(* open Lwt *) open Eth open Common open Types.Ethereum_analysis @@ -7,92 +8,6 @@ open Olympus.Types open Tools -type swap_transaction_mint = { - stm_transaction_hash : b; - stm_index : bint; - stm_sender : known_address_or_address; - stm_mint_list : uni_v3_mint list; -} -[@@deriving encoding { camel; remove_prefix = "stm_" }, eq] - -type swap_transaction_burn = { - stb_transaction_hash : b; - stb_index : bint; - stb_sender : known_address_or_address; - stb_burn_list : uni_v3_burn list; -} -[@@deriving encoding { camel; remove_prefix = "stb_" }, eq] - -type swap_transaction_collect = { - stc_transaction_hash : b; - stc_index : bint; - stc_sender : known_address_or_address; - stc_collect_list : uni_v3_collect list; -} -[@@deriving encoding { camel; remove_prefix = "stc_" }, eq] - -type swap_transaction_mint_final = { - stmf_transaction_hash : b; - stmf_index : bint; - stmf_sender : known_address_or_address; - stmf_mint : uni_v3_mint; -} -[@@deriving encoding { camel; remove_prefix = "stmf_" }, eq] - -type swap_transaction_burn_final = { - stbf_transaction_hash : b; - stbf_index : bint; - stbf_sender : known_address_or_address; - stbf_burn : uni_v3_burn; -} -[@@deriving encoding { camel; remove_prefix = "stbf_" }, eq] - -type swap_transaction_collect_final = { - stcf_transaction_hash : b; - stcf_index : bint; - stcf_sender : known_address_or_address; - stcf_collect : uni_v3_collect; -} -[@@deriving encoding { camel; remove_prefix = "stcf_" }, 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 key_burn = { - kb_transaction_hash : b; - kb_contract : db_contract_information; - kb_tick_lower : z; - kb_tick_upper : z; -} -[@@deriving encoding { camel; remove_prefix = "kb_" }, eq] - -type burn_collect = { - bc_transaction_hash : b; - bc_index : bint; - bc_sender : known_address_or_address; - bc_burn : uni_v3_burn; - bc_collect : uni_v3_collect; - bc_profit : token_profit * token_profit; -} -[@@deriving encoding { camel; remove_prefix = "bc_" }, eq] - -type sandwich_liquidity_possible = { - slp_mint : swap_transaction_mint_final; - slp_burn : key_burn list; -} -[@@deriving encoding { camel; remove_prefix = "slp_" }, eq] - -type sandwich_liquidity = { - sl_mint : swap_transaction_mint_final; - sl_burn_collect : burn_collect list; -} -[@@deriving encoding { camel; remove_prefix = "sl_" }, eq] - let eq_inverse_token_swapped ts1 ts2 = ts1.ts_out_token = ts2.ts_in_token && ts1.ts_in_token = ts2.ts_out_token @@ -523,35 +438,22 @@ let general_swapV3 (swap : uni_v3_swap) = with e -> Log.log @@ Printexc.to_string e ; None +(* let find_swaps (transaction_trace : debug_trace_result) = let rec + get_pair_swaps_v2_v3 (log : debug_trace_result) (swapv2, swapv3, v3_burn, + v3_mint) = let> swapv2', swapv3', v3_burn', v3_mint' = let> v2 = get_v2_swaps + log.dtr_logs in let> v3 = get_v3_swaps log.dtr_logs in let> v3_b = + get_v3_burn log.dtr_logs in let> v3_m = get_v3_mint log.dtr_logs in + Lwt.return ( concat_list swapv2 v2, concat_list swapv3 v3, concat_list v3_b + v3_burn, concat_list v3_m v3_mint ) in + + let> sv2, sv3, v3_b, v3_m = Lwt_list.fold_left_s (fun acc arg -> let> s2, s3, + v3_b, v3_m = get_pair_swaps_v2_v3 arg acc in Lwt.return (s2, s3, v3_b, v3_m)) + ([], [], [], []) log.dtr_calls in -let find_swaps (transaction_trace : debug_trace_result) = - let rec get_pair_swaps_v2_v3 (log : debug_trace_result) - (swapv2, swapv3, v3_burn, v3_mint) = - let> swapv2', swapv3', v3_burn', v3_mint' = - let> v2 = get_v2_swaps log.dtr_logs in - let> v3 = get_v3_swaps log.dtr_logs in - let> v3_b = get_v3_burn log.dtr_logs in - let> v3_m = get_v3_mint log.dtr_logs in - Lwt.return - ( concat_list swapv2 v2, - concat_list swapv3 v3, - concat_list v3_b v3_burn, - concat_list v3_m v3_mint ) in - - let> sv2, sv3, v3_b, v3_m = - Lwt_list.fold_left_s - (fun acc arg -> - let> s2, s3, v3_b, v3_m = get_pair_swaps_v2_v3 arg acc in - Lwt.return (s2, s3, v3_b, v3_m)) - ([], [], [], []) log.dtr_calls in - - Lwt.return - ( concat_list sv2 swapv2', - concat_list sv3 swapv3', - concat_list v3_b v3_burn', - concat_list v3_m v3_mint' ) in - - get_pair_swaps_v2_v3 transaction_trace ([], [], [], []) + Lwt.return ( concat_list sv2 swapv2', concat_list sv3 swapv3', concat_list + v3_b v3_burn', concat_list v3_m v3_mint' ) in + + get_pair_swaps_v2_v3 transaction_trace ([], [], [], []) *) let get_events (transaction_trace : debug_trace_result) = let add_or_replace_information hashtbl key arg = @@ -760,7 +662,7 @@ let detect_sandwich_liquidity swap_transaction_mint swap_transaction_burn hashtbl ; final_hashtbl in - let rec aux (sort_m : swap_transaction_mint) sort_b hashtbl = + let aux (sort_m : swap_transaction_mint) sort_b hashtbl = List.fold_left (fun acc_slp sort_b -> if @@ -982,6 +884,13 @@ let detect_sandwich debug_trace_list = swap_transaction_collect )) ([], [], [], []) debug_trace_list in let detect_fr_br = detect_fr_br swap_transaction_list in + let sandwich_liquidity = + detect_sandwich_liquidity swap_transaction_mint swap_transaction_burn + swap_transaction_collect in + Lwt.return + @@ match detect_fr_br with - | [] -> [] - | detect_fr_br -> extract_sandwich_list detect_fr_br swap_transaction_list + | [] -> ([], sandwich_liquidity) + | detect_fr_br -> + ( extract_sandwich_list detect_fr_br swap_transaction_list, + sandwich_liquidity ) -- GitLab From 03e1b8c00803a0fdbf36f6ab36795e340144be3b Mon Sep 17 00:00:00 2001 From: "rayane.lattari" Date: Fri, 18 Jul 2025 11:26:13 +0200 Subject: [PATCH 07/33] [Ethereum-analysis-sandwich-liquidity] Fixes and change imports --- .../eth_analysis_sandwich.ml | 141 +++++++++--------- 1 file changed, 70 insertions(+), 71 deletions(-) diff --git a/src/ethereum_analysis/eth_analysis_sandwich.ml b/src/ethereum_analysis/eth_analysis_sandwich.ml index 734eea0d..abf9c4e0 100644 --- a/src/ethereum_analysis/eth_analysis_sandwich.ml +++ b/src/ethereum_analysis/eth_analysis_sandwich.ml @@ -134,10 +134,16 @@ let decode_abi_mint_v3 log abi = let uv3m_contract, uv3m_token0, uv3m_token1 = get_token_address log in let uv3m_sender = let tmp = List.nth inputs 0 in - get_address_from_evmvalue tmp.pda_data in + known_address_or_evm_value_to_known_address_or_address tmp.pda_data + in + (* get_address_from_evmvalue tmp.pda_data in *) + let uv3m_owner = let tmp = List.nth inputs 1 in - get_address_from_evmvalue tmp.pda_data in + known_address_or_evm_value_to_known_address_or_address tmp.pda_data + in + (* get_address_from_evmvalue tmp.pda_data in *) + let uv3m_tick_lower = let tmp = List.nth inputs 2 in get_int_from_evmvalue tmp.pda_data in @@ -180,7 +186,10 @@ let decode_abi_burn_v3 log abi = let uv3b_contract, uv3b_token0, uv3b_token1 = get_token_address log in let uv3b_owner = let tmp = List.nth inputs 0 in - get_address_from_evmvalue tmp.pda_data in + known_address_or_evm_value_to_known_address_or_address tmp.pda_data + in + (* get_address_from_evmvalue tmp.pda_data in *) + let uv3b_tick_lower = let tmp = List.nth inputs 1 in get_int_from_evmvalue tmp.pda_data in @@ -223,10 +232,16 @@ let decode_abi_collect_v3 log abi = let uv3c_contract, uv3c_token0, uv3c_token1 = get_token_address log in let uv3c_owner = let tmp = List.nth inputs 0 in - get_address_from_evmvalue tmp.pda_data in + known_address_or_evm_value_to_known_address_or_address tmp.pda_data + in + (* get_address_from_evmvalue tmp.pda_data in *) + let uv3c_recipient = let tmp = List.nth inputs 1 in - get_address_from_evmvalue tmp.pda_data in + known_address_or_evm_value_to_known_address_or_address tmp.pda_data + in + (* get_address_from_evmvalue tmp.pda_data in *) + let uv3c_tick_lower = let tmp = List.nth inputs 2 in get_int_from_evmvalue tmp.pda_data in @@ -266,7 +281,10 @@ let decode_abi_v2 log abi = let uv2s_contract, uv2s_token0, uv2s_token1 = get_token_address log in let uv2s_sender = let tmp = List.nth inputs 0 in - get_address_from_evmvalue tmp.pda_data in + known_address_or_evm_value_to_known_address_or_address tmp.pda_data + in + (* get_address_from_evmvalue tmp.pda_data in *) + let uv2s_amount0In = let tmp = List.nth inputs 1 in get_int_from_evmvalue tmp.pda_data in @@ -281,7 +299,9 @@ let decode_abi_v2 log abi = get_int_from_evmvalue tmp.pda_data in let uv2s_recipient = let tmp = List.nth inputs 5 in - get_address_from_evmvalue tmp.pda_data in + known_address_or_evm_value_to_known_address_or_address tmp.pda_data + in + (* get_address_from_evmvalue tmp.pda_data in *) Some { @@ -301,54 +321,49 @@ let decode_abi_v2 log abi = 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; - } + 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_contract; + uv3s_sender; + uv3s_recipient; + uv3s_amount0; + uv3s_amount1; + uv3s_sqrt_price; + uv3s_liquidity; + uv3s_tick; + uv3s_token0; + uv3s_token1; + }) with _ -> None let inputs = List.sort @@ -438,22 +453,6 @@ let general_swapV3 (swap : uni_v3_swap) = with e -> Log.log @@ Printexc.to_string e ; None -(* let find_swaps (transaction_trace : debug_trace_result) = let rec - get_pair_swaps_v2_v3 (log : debug_trace_result) (swapv2, swapv3, v3_burn, - v3_mint) = let> swapv2', swapv3', v3_burn', v3_mint' = let> v2 = get_v2_swaps - log.dtr_logs in let> v3 = get_v3_swaps log.dtr_logs in let> v3_b = - get_v3_burn log.dtr_logs in let> v3_m = get_v3_mint log.dtr_logs in - Lwt.return ( concat_list swapv2 v2, concat_list swapv3 v3, concat_list v3_b - v3_burn, concat_list v3_m v3_mint ) in - - let> sv2, sv3, v3_b, v3_m = Lwt_list.fold_left_s (fun acc arg -> let> s2, s3, - v3_b, v3_m = get_pair_swaps_v2_v3 arg acc in Lwt.return (s2, s3, v3_b, v3_m)) - ([], [], [], []) log.dtr_calls in - - Lwt.return ( concat_list sv2 swapv2', concat_list sv3 swapv3', concat_list - v3_b v3_burn', concat_list v3_m v3_mint' ) in - - get_pair_swaps_v2_v3 transaction_trace ([], [], [], []) *) let get_events (transaction_trace : debug_trace_result) = let add_or_replace_information hashtbl key arg = -- GitLab From 95d5b793f315179b2206419ad1b0a7f99d7fc465 Mon Sep 17 00:00:00 2001 From: "rayane.lattari" Date: Fri, 18 Jul 2025 11:44:12 +0200 Subject: [PATCH 08/33] [Ethereum-analysis-sandwich-liquidity] remove get_address_from_evmvalue --- .../eth_analysis_sandwich.ml | 133 +++++++----------- 1 file changed, 49 insertions(+), 84 deletions(-) diff --git a/src/ethereum_analysis/eth_analysis_sandwich.ml b/src/ethereum_analysis/eth_analysis_sandwich.ml index abf9c4e0..8277348e 100644 --- a/src/ethereum_analysis/eth_analysis_sandwich.ml +++ b/src/ethereum_analysis/eth_analysis_sandwich.ml @@ -48,18 +48,6 @@ let v3_event_burn_id = let v3_event_collect_id = b "70935338e69775456a85ddef226c395fb668b63fa0115f5f20610b388e6ca9c0" -let get_address_from_evmvalue - (contract_or_evm_value : known_address_or_evm_value) = - match contract_or_evm_value with - | Coev_known_address contract_info -> Coadr_known_address contract_info - | Coev_evm_value evm_value -> ( - match evm_value with - | `address a -> Coadr_address a - | _ as e -> - failwith - @@ Format.sprintf "fail get_address_from_evmvalue %s" - @@ EzEncoding.construct Eth.evm_value_enc e) - 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 -> ( @@ -134,16 +122,10 @@ let decode_abi_mint_v3 log abi = 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 - (* get_address_from_evmvalue tmp.pda_data 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 - (* get_address_from_evmvalue tmp.pda_data 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 @@ -186,10 +168,7 @@ let decode_abi_burn_v3 log abi = 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 - (* get_address_from_evmvalue tmp.pda_data 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 @@ -232,16 +211,10 @@ let decode_abi_collect_v3 log abi = 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 - (* get_address_from_evmvalue tmp.pda_data 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 - (* get_address_from_evmvalue tmp.pda_data 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 @@ -281,10 +254,7 @@ let decode_abi_v2 log abi = 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 - (* get_address_from_evmvalue tmp.pda_data 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 @@ -299,10 +269,7 @@ let decode_abi_v2 log abi = 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 - (* get_address_from_evmvalue tmp.pda_data in *) - + known_address_or_evm_value_to_known_address_or_address tmp.pda_data in Some { uv2s_contract; @@ -326,51 +293,49 @@ let decode_abi_v3 event_hash abi s_contract = (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_contract; - uv3s_sender; - uv3s_recipient; - uv3s_amount0; - uv3s_amount1; - uv3s_sqrt_price; - uv3s_liquidity; - uv3s_tick; - uv3s_token0; - uv3s_token1; - }) - with _ -> None - 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 -let get_v2_swap log = + Some + { + 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 get_event_generic (signature : b) decode log = + Lwt.return + @@ match log.dtrl_decode_signature with | None -> None | Some s -> ( -- GitLab From 9e11e84836b97c87e143745fbaf5d58e65b1f23e Mon Sep 17 00:00:00 2001 From: "rayane.lattari" Date: Tue, 22 Jul 2025 15:36:13 +0200 Subject: [PATCH 09/33] [Ethereum-analysis-sandwich-liquidity] remove get_address_from_evmvalue --- .../eth_analysis_sandwich.ml | 67 +++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/src/ethereum_analysis/eth_analysis_sandwich.ml b/src/ethereum_analysis/eth_analysis_sandwich.ml index 8277348e..815b0cbf 100644 --- a/src/ethereum_analysis/eth_analysis_sandwich.ml +++ b/src/ethereum_analysis/eth_analysis_sandwich.ml @@ -858,3 +858,70 @@ let detect_sandwich debug_trace_list = | detect_fr_br -> ( extract_sandwich_list detect_fr_br swap_transaction_list, sandwich_liquidity ) +let merge_sandwich sandwich_liquidity_list = + let hashtbl_add_to_list hashtbl key arg = + let content = + match Hashtbl.find_opt hashtbl key with + | None -> [arg] + | Some l -> arg :: l in + Hashtbl.replace hashtbl key content in + + let add_profit hashtbl_token_profit key_token profit = + let content = + match Hashtbl.find_opt hashtbl_token_profit key_token with + | None -> profit + | Some z -> Z.add z profit in + Hashtbl.replace hashtbl_token_profit key_token content in + + let create_type mpc_contract mpc_sandwich_list = + let hashtbl_token_profit = Hashtbl.create 7 in + List.iter + (fun sandwich -> + match sandwich with + | `sandwich sandwich -> + add_profit hashtbl_token_profit sandwich.sdo_profit.tp_token + sandwich.sdo_profit.tp_profit + | `sandwich_liquidity sandwich -> + let token_profit1, token_profit2 = sandwich.sl_profit in + add_profit hashtbl_token_profit token_profit1.tp_token + token_profit1.tp_profit ; + add_profit hashtbl_token_profit token_profit2.tp_token + token_profit2.tp_profit) + mpc_sandwich_list ; + + let mpc_profit = + Hashtbl.fold + (fun tp_token tp_profit acc -> { tp_token; tp_profit } :: acc) + hashtbl_token_profit [] in + { mpc_contract; mpc_sandwich_list; mpc_profit } in + + let merge_sandwich_per_contract sandwich_list = + let hashtbl_sandwich_per_contract = Hashtbl.create 7 in + List.iter + (fun sandwich -> + let contract = + match sandwich with + | `sandwich sandwich -> + (List.hd sandwich.sdo_front_run.sto_swap_list).s_contract + | `sandwich_liquidity sandwich -> + sandwich.sl_mint.stmf_mint.uv3m_contract in + + hashtbl_add_to_list hashtbl_sandwich_per_contract contract sandwich) + sandwich_list ; + Hashtbl.fold + (fun key arg acc -> create_type key arg :: acc) + hashtbl_sandwich_per_contract [] in + + let hashtbl_sandwich_per_user = Hashtbl.create 7 in + List.iter + (fun sandwich -> + let sender = + match sandwich with + | `sandwich sandwich -> sandwich.sdo_front_run.sto_sender + | `sandwich_liquidity sandwich -> sandwich.sl_mint.stmf_sender in + hashtbl_add_to_list hashtbl_sandwich_per_user sender sandwich) + sandwich_liquidity_list ; + Hashtbl.fold + (fun mpu_sender arg acc -> + { mpu_sender; mpu_per_contract = merge_sandwich_per_contract arg } :: acc) + hashtbl_sandwich_per_user [] -- GitLab From ae0fe6f4d4abe6af37748cee07bc3889f08433e2 Mon Sep 17 00:00:00 2001 From: "rayane.lattari" Date: Tue, 22 Jul 2025 15:37:20 +0200 Subject: [PATCH 10/33] [Ethereum-analysis-sandwich-liquidity] detect_sandwich update --- .../eth_analysis_sandwich.ml | 355 ++++++++++-------- 1 file changed, 207 insertions(+), 148 deletions(-) diff --git a/src/ethereum_analysis/eth_analysis_sandwich.ml b/src/ethereum_analysis/eth_analysis_sandwich.ml index 815b0cbf..11c31386 100644 --- a/src/ethereum_analysis/eth_analysis_sandwich.ml +++ b/src/ethereum_analysis/eth_analysis_sandwich.ml @@ -419,7 +419,7 @@ let general_swapV3 (swap : uni_v3_swap) = Log.log @@ Printexc.to_string e ; None -let get_events (transaction_trace : debug_trace_result) = +let get_event (transaction_trace : debug_trace_result) sandwich = let add_or_replace_information hashtbl key arg = let adding = match Hashtbl.find_opt hashtbl key with @@ -433,34 +433,46 @@ let get_events (transaction_trace : debug_trace_result) = | `collect l1, `collect l2 -> `collect (concat_list l1 l2) | _ -> assert false) in Hashtbl.replace hashtbl key adding in - let rec get_pair_swaps_v2_v3 (log : debug_trace_result) hashtbl = - let> v2 = get_v2_swaps log.dtr_logs in - let> v3 = get_v3_swaps log.dtr_logs in - let> v3_b = get_v3_burn log.dtr_logs in - let> v3_m = get_v3_mint log.dtr_logs in - let> v3_c = get_v3_collect log.dtr_logs in - add_or_replace_information hashtbl `swapV2 (`swapV2 v2) ; - add_or_replace_information hashtbl `swapV3 (`swapV3 v3) ; - add_or_replace_information hashtbl `mint (`mint v3_b) ; - add_or_replace_information hashtbl `burn (`burn v3_m) ; - add_or_replace_information hashtbl `collect (`collect v3_c) ; - + let rec get_pair_swaps_v2_v3 (log : debug_trace_result) hashtbl + events_function = + let> _ = events_function log hashtbl in let> _ = Lwt_list.iter_s - (fun arg -> get_pair_swaps_v2_v3 arg hashtbl) + (fun arg -> get_pair_swaps_v2_v3 arg hashtbl events_function) log.dtr_calls in Lwt.return_unit in let hashtbl = Hashtbl.create 7 in - Hashtbl.add hashtbl `swapV2 (`swapV2 []) ; - Hashtbl.add hashtbl `swapV3 (`swapV3 []) ; - Hashtbl.add hashtbl `mint (`mint []) ; - Hashtbl.add hashtbl `burn (`burn []) ; - Hashtbl.add hashtbl `collect (`collect []) ; - let> _ = get_pair_swaps_v2_v3 transaction_trace hashtbl in - Lwt.return hashtbl + match sandwich with + | `sandwich -> + Hashtbl.add hashtbl `swapV2 (`swapV2 []) ; + Hashtbl.add hashtbl `swapV3 (`swapV3 []) ; + let event log hashtbl = + let> v2 = get_v2_swaps log.dtr_logs in + let> v3 = get_v3_swaps log.dtr_logs in + add_or_replace_information hashtbl `swapV2 (`swapV2 v2) ; + add_or_replace_information hashtbl `swapV3 (`swapV3 v3) ; + Lwt.return_unit in + + let> _ = get_pair_swaps_v2_v3 transaction_trace hashtbl event in + Lwt.return hashtbl + | `sandwich_liquidity -> + Hashtbl.add hashtbl `mint (`mint []) ; + Hashtbl.add hashtbl `burn (`burn []) ; + Hashtbl.add hashtbl `collect (`collect []) ; + let event log hashtbl = + let> v3_b = get_v3_burn log.dtr_logs in + let> v3_m = get_v3_mint log.dtr_logs in + let> v3_c = get_v3_collect log.dtr_logs in + add_or_replace_information hashtbl `mint (`mint v3_m) ; + add_or_replace_information hashtbl `burn (`burn v3_b) ; + add_or_replace_information hashtbl `collect (`collect v3_c) ; + Lwt.return_unit in + + let> _ = get_pair_swaps_v2_v3 transaction_trace hashtbl event in + Lwt.return hashtbl let gen_swap_transaction (st_sender : known_address_or_address) (st_transaction_hash : b) (st_index : bint) (st_swap_list : swap list) = @@ -586,12 +598,12 @@ 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 | None -> acc_sandwich - | Some sandwich -> sandwich :: acc_sandwich) + | Some sandwich -> `sandwich sandwich :: acc_sandwich) [] sandwich_possible_list let detect_sandwich_liquidity swap_transaction_mint swap_transaction_burn @@ -710,11 +722,11 @@ let detect_sandwich_liquidity swap_transaction_mint swap_transaction_burn List.fold_left (fun result sandwich_liquidity_possible -> - let sl_burn_collect = + let sl_burn_collect, sl_profit = List.fold_left - (fun acc key_burn -> + (fun (acc_burn_collect, acc_profit) key_burn -> match Hashtbl.find_opt hashtbl_final key_burn with - | None -> acc + | None -> (acc_burn_collect, acc_profit) | Some (burn, bc_collect) -> let profit0 = { @@ -729,135 +741,182 @@ let detect_sandwich_liquidity swap_transaction_mint swap_transaction_burn tp_profit = Z.sub bc_collect.uv3c_amount1 burn.stbf_burn.uv3b_amount1; } in - - { - bc_transaction_hash = burn.stbf_transaction_hash; - bc_index = burn.stbf_index; - bc_sender = burn.stbf_sender; - bc_burn = burn.stbf_burn; - bc_collect; - bc_profit = (profit0, profit1); - } - :: acc) - [] sandwich_liquidity_possible.slp_burn in - - { sl_burn_collect; sl_mint = sandwich_liquidity_possible.slp_mint } + let profit = + match acc_profit with + | None -> Some (profit0, profit1) + | Some (p0, p1) -> + assert ( + equal_db_contract_information p0.tp_token profit0.tp_token) ; + assert ( + equal_db_contract_information p1.tp_token profit1.tp_token) ; + Some + ( { + tp_token = p0.tp_token; + tp_profit = Z.sub p0.tp_profit profit0.tp_profit; + }, + { + tp_token = p1.tp_token; + tp_profit = Z.sub p1.tp_profit profit1.tp_profit; + } ) in + + ( { + bc_transaction_hash = burn.stbf_transaction_hash; + bc_index = burn.stbf_index; + bc_sender = burn.stbf_sender; + bc_burn = burn.stbf_burn; + bc_collect; + bc_profit = (profit0, profit1); + } + :: acc_burn_collect, + profit )) + ([], None) sandwich_liquidity_possible.slp_burn in + + `sandwich_liquidity + { + sl_burn_collect; + sl_profit = Option.get sl_profit; + sl_mint = sandwich_liquidity_possible.slp_mint; + } :: result) [] sandwich_liquidity_possible_list -let detect_sandwich debug_trace_list = - let> ( swap_transaction_list, - swap_transaction_mint, - swap_transaction_burn, - swap_transaction_collect ) = - Lwt_list.fold_left_s - (fun (swap, st_v3_m, st_v3_b, st_v3_c) arg -> - let transaction_hash = arg.dt_tx_hash in - let sender = - match arg.dt_result.dtr_from_decode with - | None -> Coadr_address arg.dt_result.dtr_from - | Some known_address -> known_address in - let index = arg.dt_tx_index in - let> swapV2, swapV3, v3_mint, v3_burn, v3_collect = - let> hashtbl = get_events arg.dt_result 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 - - let v3_mint = - let tmp = Hashtbl.find hashtbl `mint in - match tmp with - | `mint m -> m - | _ -> assert false in - - let v3_burn = - let tmp = Hashtbl.find hashtbl `burn in - match tmp with - | `burn b -> b - | _ -> assert false in - - let v3_collect = - let tmp = Hashtbl.find hashtbl `collect in - match tmp with - | `collect c -> c - | _ -> assert false in - - Lwt.return (swapV2, swapV3, v3_burn, v3_mint, v3_collect) in - - let swap_list = - let v2 = List.map general_swapV2 swapV2 in - let v3 = List.filter_map general_swapV3 swapV3 in - concat_list v2 v3 in - - let> swap = - match swap_list with - | [] -> Lwt.return swap - | swap_list -> - Lwt.return - @@ gen_swap_transaction sender transaction_hash index swap_list - :: swap in - - let swap_transaction_mint = - match v3_mint with - | [] -> st_v3_m - | v3_mint -> - { - stm_transaction_hash = transaction_hash; - stm_index = index; - stm_sender = sender; - stm_mint_list = v3_mint; - } - :: st_v3_m in +let detect_sandwich debug_trace_list sandwich = + 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) in + + let get_sanwdich_data arg transaction_hash sender index swap = + let> swapV2, swapV3 = + let> hashtbl = get_event arg.dt_result `sandwich 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 - let swap_transaction_burn = - match v3_burn with - | [] -> st_v3_b - | v3_burn -> - { - stb_transaction_hash = transaction_hash; - stb_index = index; - stb_sender = sender; - stb_burn_list = v3_burn; - } - :: st_v3_b in + Lwt.return (swapV2, swapV3) in - let swap_transaction_collect = - match v3_collect 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 in + let swap_list = + let v2 = List.map general_swapV2 swapV2 in + let v3 = List.filter_map general_swapV3 swapV3 in + concat_list v2 v3 in + let> swap = + match swap_list with + | [] -> Lwt.return swap + | swap_list -> Lwt.return - ( swap, - swap_transaction_mint, - swap_transaction_burn, - swap_transaction_collect )) - ([], [], [], []) debug_trace_list in - let detect_fr_br = detect_fr_br swap_transaction_list in - let sandwich_liquidity = - detect_sandwich_liquidity swap_transaction_mint swap_transaction_burn - swap_transaction_collect in - Lwt.return - @@ - match detect_fr_br with - | [] -> ([], sandwich_liquidity) - | detect_fr_br -> - ( extract_sandwich_list detect_fr_br swap_transaction_list, - sandwich_liquidity ) + @@ (gen_swap_transaction sender transaction_hash index swap_list :: swap) + in + Lwt.return swap in + + let get_sanwdich_liquidity_data arg transaction_hash sender index st_v3_m + st_v3_b st_v3_c = + let> v3_mint, v3_burn, v3_collect = + let> hashtbl = get_event arg.dt_result `sandwich_liquidity in + + let v3_mint = + let tmp = Hashtbl.find hashtbl `mint in + match tmp with + | `mint m -> m + | _ -> assert false in + + let v3_burn = + let tmp = Hashtbl.find hashtbl `burn in + match tmp with + | `burn b -> b + | _ -> assert false in + + let v3_collect = + let tmp = Hashtbl.find hashtbl `collect in + match tmp with + | `collect c -> c + | _ -> assert false in + + Lwt.return (v3_mint, v3_burn, v3_collect) in + let swap_transaction_mint = + match v3_mint with + | [] -> st_v3_m + | v3_mint -> + { + stm_transaction_hash = transaction_hash; + stm_index = index; + stm_sender = sender; + stm_mint_list = v3_mint; + } + :: st_v3_m in + + let swap_transaction_burn = + match v3_burn with + | [] -> st_v3_b + | v3_burn -> + { + stb_transaction_hash = transaction_hash; + stb_index = index; + stb_sender = sender; + stb_burn_list = v3_burn; + } + :: st_v3_b in + + let swap_transaction_collect = + match v3_collect 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 in + + Lwt.return + (swap_transaction_mint, swap_transaction_burn, swap_transaction_collect) + in + + match sandwich with + | `sandwich -> ( + let> swap_transaction_list = + Lwt_list.fold_left_s + (fun swap arg -> + let transaction_hash, sender, index = + get_transaction_information arg in + get_sanwdich_data arg transaction_hash sender index swap) + [] debug_trace_list in + + let detect_fr_br = detect_fr_br swap_transaction_list in + Lwt.return + @@ + match detect_fr_br with + | [] -> [] + | detect_fr_br -> extract_sandwich_list detect_fr_br swap_transaction_list) + | `sandwich_liquidity -> + let> swap_transaction_mint, swap_transaction_burn, swap_transaction_collect + = + Lwt_list.fold_left_s + (fun (st_v3_m, st_v3_b, st_v3_c) arg -> + let transaction_hash, sender, index = + get_transaction_information arg in + get_sanwdich_liquidity_data arg transaction_hash sender index st_v3_m + st_v3_b st_v3_c) + ([], [], []) debug_trace_list in + let sandwich_liquidity = + detect_sandwich_liquidity swap_transaction_mint swap_transaction_burn + swap_transaction_collect in + + Lwt.return @@ sandwich_liquidity + let merge_sandwich sandwich_liquidity_list = let hashtbl_add_to_list hashtbl key arg = let content = -- GitLab From f9aded99c5fda4196ac7e943035c50548ef6f275 Mon Sep 17 00:00:00 2001 From: "rayane.lattari" Date: Tue, 22 Jul 2025 15:40:51 +0200 Subject: [PATCH 11/33] [Ethereum-analysis-sandwich-liquidity] add merge and sandwich type, and add profit sandwich_liquidity --- src/common/types.ml | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/common/types.ml b/src/common/types.ml index 538bbaf4..8e32a8de 100644 --- a/src/common/types.ml +++ b/src/common/types.ml @@ -613,6 +613,7 @@ module Ethereum_analysis = struct type sandwich_liquidity = { sl_mint : swap_transaction_mint_final; sl_burn_collect : burn_collect list; + sl_profit : token_profit * token_profit; } [@@deriving encoding { camel; remove_prefix = "sl_" }, eq] @@ -866,6 +867,24 @@ 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 : Ethereum_indexer.db_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 : Ethereum_indexer.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 = -- GitLab From 71d87f867e5e97dd786618cca4809f3c6c56ee76 Mon Sep 17 00:00:00 2001 From: "rayane.lattari" Date: Fri, 25 Jul 2025 15:04:24 +0200 Subject: [PATCH 12/33] [Ethereum-analysis-sandwich-liquidity] Fix handling of sandwich liquidity --- src/common/types.ml | 2 ++ src/ethereum_analysis/eth_analyse.ml | 1 - .../eth_analysis_sandwich.mli | 11 ++++++-- .../eth_analysis_tools/eth_tools.ml | 1 + .../eth_db_psql/eth_db_psql_analysis.ml | 28 +++++++++++++++---- 5 files changed, 35 insertions(+), 8 deletions(-) diff --git a/src/common/types.ml b/src/common/types.ml index 8e32a8de..dab7e462 100644 --- a/src/common/types.ml +++ b/src/common/types.ml @@ -891,6 +891,7 @@ module Ethereum_analysis = struct [ `liquidate | `flashloan | `sandwich + | `sandwich_liquidity | `transfer | `arbitrage | `bottom of bottom [@encoding.skip] ] @@ -977,6 +978,7 @@ 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/eth_analyse.ml b/src/ethereum_analysis/eth_analyse.ml index 914fc70c..bc4b3c34 100644 --- a/src/ethereum_analysis/eth_analyse.ml +++ b/src/ethereum_analysis/eth_analyse.ml @@ -16,7 +16,6 @@ let get_event_information (evt : 'a) | `flashloan -> `flashloan evt_dcd | _ -> Log.log_error_fail ~here:[%here] "ERROR : No implemented" | `bottom (_ : bottom) -> . - let dir_map_hashes = Common.Env.get_env Common.Env.hashes_var let tag_map = ref Tag_map.empty diff --git a/src/ethereum_analysis/eth_analysis_sandwich.mli b/src/ethereum_analysis/eth_analysis_sandwich.mli index 3d76bb86..abb92d83 100644 --- a/src/ethereum_analysis/eth_analysis_sandwich.mli +++ b/src/ethereum_analysis/eth_analysis_sandwich.mli @@ -15,5 +15,12 @@ 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_analysis.sandwich_output list + ( Common.Types.Ethereum_decode.known_address_or_address, + Common.Types.Ethereum_decode.known_address_or_evm_value ) + Olympus.Types.debug_trace + list -> + [< `sandwich | `sandwich_liquidity] -> + [> `sandwich of Common.Types.Ethereum_analysis.sandwich_output + | `sandwich_liquidity of Common.Types.Ethereum_analysis.sandwich_liquidity ] + list + Lwt.t diff --git a/src/ethereum_analysis/eth_analysis_tools/eth_tools.ml b/src/ethereum_analysis/eth_analysis_tools/eth_tools.ml index 994865ef..4167204f 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" 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 b3579283..73c0aee9 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,10 @@ 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 +339,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 +352,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,9 +376,13 @@ 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 = { liquidate_all with @@ -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 -- GitLab From 70d27aa86288643123f290de635378c76b18b665 Mon Sep 17 00:00:00 2001 From: "rayane.lattari" Date: Fri, 25 Jul 2025 15:09:15 +0200 Subject: [PATCH 13/33] [Ethereum-analysis-sandwich-liquidity] Add merge, first version --- .../eth_analysis_sandwich.ml | 69 ++++++++++++++++++- 1 file changed, 68 insertions(+), 1 deletion(-) diff --git a/src/ethereum_analysis/eth_analysis_sandwich.ml b/src/ethereum_analysis/eth_analysis_sandwich.ml index 11c31386..9cf1b032 100644 --- a/src/ethereum_analysis/eth_analysis_sandwich.ml +++ b/src/ethereum_analysis/eth_analysis_sandwich.ml @@ -914,7 +914,6 @@ let detect_sandwich debug_trace_list sandwich = let sandwich_liquidity = detect_sandwich_liquidity swap_transaction_mint swap_transaction_burn swap_transaction_collect in - Lwt.return @@ sandwich_liquidity let merge_sandwich sandwich_liquidity_list = @@ -984,3 +983,71 @@ let merge_sandwich sandwich_liquidity_list = (fun mpu_sender arg acc -> { mpu_sender; mpu_per_contract = merge_sandwich_per_contract arg } :: acc) hashtbl_sandwich_per_user [] + +let merge_sandwich_with_liquidity sandwich sandwich_liquidity_list = + let hashtbl_add_to_list hashtbl key arg = + let content = + match Hashtbl.find_opt hashtbl key with + | None -> [arg] + | Some l -> arg :: l in + Hashtbl.replace hashtbl key content in + + let add_profit hashtbl_token_profit key_token profit = + let content = + match Hashtbl.find_opt hashtbl_token_profit key_token with + | None -> profit + | Some z -> Z.add z profit in + Hashtbl.replace hashtbl_token_profit key_token content in + + let create_type mpc_contract mpc_sandwich_list = + let hashtbl_token_profit = Hashtbl.create 7 in + List.iter + (fun sandwich -> + match sandwich with + | `sandwich sandwich -> + add_profit hashtbl_token_profit sandwich.sdo_profit.tp_token + sandwich.sdo_profit.tp_profit + | `sandwich_liquidity sandwich -> + let token_profit1, token_profit2 = sandwich.sl_profit in + add_profit hashtbl_token_profit token_profit1.tp_token + token_profit1.tp_profit ; + add_profit hashtbl_token_profit token_profit2.tp_token + token_profit2.tp_profit) + mpc_sandwich_list ; + + let mpc_profit = + Hashtbl.fold + (fun tp_token tp_profit acc -> { tp_token; tp_profit } :: acc) + hashtbl_token_profit [] in + { mpc_contract; mpc_sandwich_list; mpc_profit } in + + let merge_sandwich_per_contract sandwich_list = + let hashtbl_sandwich_per_contract = Hashtbl.create 7 in + List.iter + (fun sandwich -> + let contract = + match sandwich with + | `sandwich sandwich -> + (List.hd sandwich.sdo_front_run.sto_swap_list).s_contract + | `sandwich_liquidity sandwich -> + sandwich.sl_mint.stmf_mint.uv3m_contract in + + hashtbl_add_to_list hashtbl_sandwich_per_contract contract sandwich) + sandwich_list ; + Hashtbl.fold + (fun key arg acc -> create_type key arg :: acc) + hashtbl_sandwich_per_contract [] in + + let hashtbl_sandwich_per_user = Hashtbl.create 7 in + List.iter + (fun sandwich -> + let sender = + match sandwich with + | `sandwich sandwich -> sandwich.sdo_front_run.sto_sender + | `sandwich_liquidity sandwich -> sandwich.sl_mint.stmf_sender in + hashtbl_add_to_list hashtbl_sandwich_per_user sender sandwich) + sandwich_liquidity_list ; + Hashtbl.fold + (fun mpu_sender arg acc -> + { mpu_sender; mpu_per_contract = merge_sandwich_per_contract arg } :: acc) + hashtbl_sandwich_per_user [] -- GitLab From 937117b39dfbe4465fa147047bcfd29278347d59 Mon Sep 17 00:00:00 2001 From: "rayane.lattari" Date: Fri, 25 Jul 2025 15:53:24 +0200 Subject: [PATCH 14/33] [Ethereum-analysis-sandwich-liquidity] merge of sandwich liquidity --- src/ethereum_analysis/eth_analysis_sandwich.ml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ethereum_analysis/eth_analysis_sandwich.ml b/src/ethereum_analysis/eth_analysis_sandwich.ml index 9cf1b032..5172b18a 100644 --- a/src/ethereum_analysis/eth_analysis_sandwich.ml +++ b/src/ethereum_analysis/eth_analysis_sandwich.ml @@ -983,7 +983,7 @@ let merge_sandwich sandwich_liquidity_list = (fun mpu_sender arg acc -> { mpu_sender; mpu_per_contract = merge_sandwich_per_contract arg } :: acc) hashtbl_sandwich_per_user [] - +(* let merge_sandwich_with_liquidity sandwich sandwich_liquidity_list = let hashtbl_add_to_list hashtbl key arg = let content = @@ -1050,4 +1050,4 @@ let merge_sandwich_with_liquidity sandwich sandwich_liquidity_list = Hashtbl.fold (fun mpu_sender arg acc -> { mpu_sender; mpu_per_contract = merge_sandwich_per_contract arg } :: acc) - hashtbl_sandwich_per_user [] + hashtbl_sandwich_per_user [] *) -- GitLab From b3302cb0013a1996cff6f798b3c7011459d136cf Mon Sep 17 00:00:00 2001 From: "rayane.lattari" Date: Fri, 12 Sep 2025 14:21:38 +0200 Subject: [PATCH 15/33] [Ethereum-analysis-sandwich-liquidity] Fix types in types.ml --- src/common/types.ml | 86 +++++++++++++++++++++------------------------ 1 file changed, 40 insertions(+), 46 deletions(-) diff --git a/src/common/types.ml b/src/common/types.ml index dab7e462..ad1b07de 100644 --- a/src/common/types.ml +++ b/src/common/types.ml @@ -424,10 +424,10 @@ module Ethereum_analysis = struct 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 : contract_information; @@ -455,12 +455,12 @@ module Ethereum_analysis = struct 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_" }, eq] @@ -468,22 +468,22 @@ module Ethereum_analysis = struct 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_" }, eq] type uni_v3_mint = { - uv3m_contract : Ethereum_indexer.db_contract_information; - uv3m_sender : Ethereum_indexer.known_address_or_address; - uv3m_owner : Ethereum_indexer.known_address_or_address; - uv3m_token0 : Ethereum_indexer.db_contract_information; - uv3m_token1 : Ethereum_indexer.db_contract_information; + 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; @@ -493,28 +493,28 @@ module Ethereum_analysis = struct [@@deriving encoding { camel; remove_prefix = "uv3m_" }, eq] type uni_v3_burn = { - uv3b_contract : Ethereum_indexer.db_contract_information; - uv3b_owner : Ethereum_indexer.known_address_or_address; + 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 : Ethereum_indexer.db_contract_information; - uv3b_token1 : Ethereum_indexer.db_contract_information; + uv3b_token0 : contract_information; + uv3b_token1 : contract_information; } [@@deriving encoding { camel; remove_prefix = "uv3b_" }, eq] type uni_v3_collect = { - uv3c_contract : Ethereum_indexer.db_contract_information; - uv3c_recipient : Ethereum_indexer.known_address_or_address; - uv3c_owner : Ethereum_indexer.known_address_or_address; + 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 : Ethereum_indexer.db_contract_information; - uv3c_token1 : Ethereum_indexer.db_contract_information; + uv3c_token0 : contract_information; + uv3c_token1 : contract_information; } [@@deriving encoding { camel; remove_prefix = "uv3c_" }, eq] @@ -525,7 +525,7 @@ module Ethereum_analysis = struct [@@deriving encoding { camel; remove_prefix = "trs_" }, eq] type token_profit = { - tp_token : Ethereum_indexer.db_contract_information; + tp_token : contract_information; tp_profit : z; } [@@deriving encoding { camel; remove_prefix = "tp_" }, eq] @@ -533,7 +533,7 @@ module Ethereum_analysis = struct type swap_transaction_mint = { stm_transaction_hash : b; stm_index : int; - stm_sender : Ethereum_indexer.known_address_or_address; + stm_sender : known_address_or_address; stm_mint_list : uni_v3_mint list; } [@@deriving encoding { camel; remove_prefix = "stm_" }, eq] @@ -541,7 +541,7 @@ module Ethereum_analysis = struct type swap_transaction_burn = { stb_transaction_hash : b; stb_index : int; - stb_sender : Ethereum_indexer.known_address_or_address; + stb_sender : known_address_or_address; stb_burn_list : uni_v3_burn list; } [@@deriving encoding { camel; remove_prefix = "stb_" }, eq] @@ -549,7 +549,7 @@ module Ethereum_analysis = struct type swap_transaction_collect = { stc_transaction_hash : b; stc_index : int; - stc_sender : Ethereum_indexer.known_address_or_address; + stc_sender : known_address_or_address; stc_collect_list : uni_v3_collect list; } [@@deriving encoding { camel; remove_prefix = "stc_" }, eq] @@ -557,7 +557,7 @@ module Ethereum_analysis = struct type swap_transaction_mint_final = { stmf_transaction_hash : b; stmf_index : int; - stmf_sender : Ethereum_indexer.known_address_or_address; + stmf_sender : known_address_or_address; stmf_mint : uni_v3_mint; } [@@deriving encoding { camel; remove_prefix = "stmf_" }, eq] @@ -565,7 +565,7 @@ module Ethereum_analysis = struct type swap_transaction_burn_final = { stbf_transaction_hash : b; stbf_index : int; - stbf_sender : Ethereum_indexer.known_address_or_address; + stbf_sender : known_address_or_address; stbf_burn : uni_v3_burn; } [@@deriving encoding { camel; remove_prefix = "stbf_" }, eq] @@ -573,7 +573,7 @@ module Ethereum_analysis = struct type swap_transaction_collect_final = { stcf_transaction_hash : b; stcf_index : int; - stcf_sender : Ethereum_indexer.known_address_or_address; + stcf_sender : known_address_or_address; stcf_collect : uni_v3_collect; } [@@deriving encoding { camel; remove_prefix = "stcf_" }, eq] @@ -588,7 +588,7 @@ module Ethereum_analysis = struct type key_burn = { kb_transaction_hash : b; - kb_contract : Ethereum_indexer.db_contract_information; + kb_contract : contract_information; kb_tick_lower : z; kb_tick_upper : z; } @@ -597,7 +597,7 @@ module Ethereum_analysis = struct type burn_collect = { bc_transaction_hash : b; bc_index : int; - bc_sender : Ethereum_indexer.known_address_or_address; + bc_sender : known_address_or_address; bc_burn : uni_v3_burn; bc_collect : uni_v3_collect; bc_profit : token_profit * token_profit; @@ -636,12 +636,6 @@ module Ethereum_analysis = struct 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; @@ -873,14 +867,14 @@ module Ethereum_analysis = struct [@@deriving encoding { camel }, eq] type merged_per_contract = { - mpc_contract : Ethereum_indexer.db_contract_information; + 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 : Ethereum_indexer.known_address_or_address; + mpu_sender : known_address_or_address; mpu_per_contract : merged_per_contract list; } [@@deriving encoding { camel; remove_prefix = "mpu_" }, eq] -- GitLab From 8f3c3354569bbd92b570d49a368be01c775eb5d5 Mon Sep 17 00:00:00 2001 From: "rayane.lattari" Date: Fri, 18 Jul 2025 11:27:13 +0200 Subject: [PATCH 16/33] remove dune ignore warnings --- src/ethereum_analysis/dune | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/ethereum_analysis/dune b/src/ethereum_analysis/dune index 984f0ff3..82ae478d 100644 --- a/src/ethereum_analysis/dune +++ b/src/ethereum_analysis/dune @@ -1,3 +1,8 @@ +(env + (_ + (flags + (:standard -w +a-4-41-44-45-48-70 -warn-error -a)))) + (library (name eth_analysis) (modules -- GitLab From 03d7001f61763e808019222f30b10cbc84beb609 Mon Sep 17 00:00:00 2001 From: "rayane.lattari" Date: Fri, 12 Sep 2025 17:11:40 +0200 Subject: [PATCH 17/33] [Ethereum-analysis-sandwich-liquidity] sandwich liquidity handling --- src/api/api_handlers_common.ml | 15 + src/api/api_handlers_db.ml | 1 + src/api/api_handlers_node.ml | 111 ++++++++ src/common/types.ml | 8 +- src/ethereum_analysis/eth_analyse.ml | 1 + .../eth_analysis_detect_pool_creation.ml | 1 + .../eth_analysis_sandwich.ml | 260 +++++++----------- src/ethereum_decode/eth_decode.ml | 1 + .../eth_db_psql/eth_db_psql_analysis.ml | 6 +- test/test_common/test_common.ml | 3 + 10 files changed, 233 insertions(+), 174 deletions(-) diff --git a/src/api/api_handlers_common.ml b/src/api/api_handlers_common.ml index fe9604df..1b3acad5 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 2f923f00..0a6b5331 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/api/api_handlers_node.ml b/src/api/api_handlers_node.ml index d1b83f52..1ee95575 100644 --- a/src/api/api_handlers_node.ml +++ b/src/api/api_handlers_node.ml @@ -450,7 +450,118 @@ let get_transaction_tags_node_eth (_, hash) _ = (fun exn -> EzAPIServer.return ~code:400 (Result.Error +<<<<<<< HEAD { op_error = __FUNCTION__; op_exception = Printexc.to_string exn })) +======= + { + op_error = "get_transaction_tags_node_eth"; + op_exception = EzEncoding.construct Eth.error_enc e; + }) + | Ok traces -> ( + let> transaction_r = Node_eth.get_transaction_by_hash (b hash) in + match transaction_r with + | Error e -> + EzAPIServer.return ~code:400 + (Result.Error + { + op_error = "get_transaction_tags_node_eth"; + op_exception = EzEncoding.construct Eth.error_enc e; + }) + | Ok transacion -> ( + let> block_r = Node_eth.get_block_by_number receipt.r_block_number in + match block_r with + | Error e -> + EzAPIServer.return ~code:400 + (Result.Error + { + op_error = "get_transaction_tags_node_eth"; + op_exception = EzEncoding.construct Eth.error_enc e; + }) + | Ok block -> + let> traces_decode = Lwt_list.map_s Decode_eth.decode_traces traces in + let> receipt_decode = Decode_eth.decode_transaction_receipt receipt in + let> analyses = + Lwt_list.fold_left_s + (fun acc analysis -> + match analysis with + | (`flashloan | `liquidate) as a -> + let> r = + Eth_analysis.Eth_analyse.tx_detect + (`tx_receipt receipt_decode) a in + Lwt.return @@ (r :: acc) + | `sandwich -> + let> sandwitches = + Eth_analysis.Eth_analysis_sandwich.detect_sandwich + traces_decode `sandwich in + let include_tx_s = + List.map (fun sandwich -> + match sandwich with + | `sandwich sandwich -> + Option.some + @@ A_Sandwich + { + bao_block_number = receipt.r_block_number; + bao_resume = sandwich; + } + | _ -> + Log.log_error_fail ~here:[%here] + "Expect Sandwich, got wrong type") + @@ List.filter + (fun sandwich -> + match sandwich with + | `sandwich sandwich -> + String.equal + (sandwich.sdo_front_run.sto_transaction_hash + :> string) + hash + || String.equal + (sandwich.sdo_back_run.sto_transaction_hash + :> string) + hash + || List.exists + (fun vitim -> + String.equal + (vitim.sto_transaction_hash :> string) + hash) + sandwich.sdo_victims + | _ -> + Log.log_error_fail ~here:[%here] + "Expect Sandwich, got wrong type") + sandwitches in + Lwt.return @@ Tools.concat_list include_tx_s acc + | `sandwich_liquidity -> + Log.log_error_lwt_fail ~here:[%here] "TODO Sandwich Liquidity" + | `transfer -> Lwt.return acc + | `arbitrage -> + let> miner = + Common.lift_lwt_opt + @@ Option.map + (fun x -> Decode_eth.decode_address x) + block.miner in + let cft_input = + { + cfti_block_number = block.number; + cfti_block_builder = miner; + cfti_base_fee = block.base_fee; + cfti_effective_price = receipt.r_effective_gas_price; + cfti_max_fee_per_gas = transacion.max_fee_per_gas; + cfti_gas_used = receipt.r_gas_used; + } in + let trace_of_tx = + List.find + (fun (trace : ('a, 'b) Olympus.Types.debug_trace) -> + String.equal (trace.dt_tx_hash :> string) hash) + traces_decode in + let> arbi_analyse = + Eth_analysis.Eth_analyse.tx_detect + (`tx_trace_with_bb_cost (trace_of_tx, cft_input)) + `arbitrage in + Lwt.return @@ (arbi_analyse :: acc) + | `bottom (_ : Types.Ethereum_analysis.bottom) -> .) + [] Api_handlers_common.available_analyses in + EzAPIServer.return_ok @@ filter_analyses + @@ List.filter_map Fun.id analyses))) +>>>>>>> a99234a ([Ethereum-analysis-sandwich-liquidity] sandwich liquidity handling) [@@service Api_services_node.get_transaction_tags_node_eth] let get_analyses_week_node_eth _ _ = diff --git a/src/common/types.ml b/src/common/types.ml index ad1b07de..b62d5dad 100644 --- a/src/common/types.ml +++ b/src/common/types.ml @@ -410,7 +410,7 @@ end module Ethereum_analysis = struct open Ethereum_decode - + type z = Z.t let z_enc = @@ -431,10 +431,9 @@ module Ethereum_analysis = struct s_sender : known_address_or_address; s_recipient : known_address_or_address; s_contract : contract_information; - } + } [@@deriving encoding { camel; remove_prefix = "s_" }, eq] - type token_swapped = { ts_in_token : int; ts_out_token : int; @@ -972,7 +971,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"] + | 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/eth_analyse.ml b/src/ethereum_analysis/eth_analyse.ml index bc4b3c34..914fc70c 100644 --- a/src/ethereum_analysis/eth_analyse.ml +++ b/src/ethereum_analysis/eth_analyse.ml @@ -16,6 +16,7 @@ let get_event_information (evt : 'a) | `flashloan -> `flashloan evt_dcd | _ -> Log.log_error_fail ~here:[%here] "ERROR : No implemented" | `bottom (_ : bottom) -> . + let dir_map_hashes = Common.Env.get_env Common.Env.hashes_var let tag_map = ref Tag_map.empty diff --git a/src/ethereum_analysis/eth_analysis_detect_pool_creation.ml b/src/ethereum_analysis/eth_analysis_detect_pool_creation.ml index 2edc352d..265c6b2a 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 5172b18a..ad3c305b 100644 --- a/src/ethereum_analysis/eth_analysis_sandwich.ml +++ b/src/ethereum_analysis/eth_analysis_sandwich.ml @@ -1,13 +1,13 @@ (* open Lwt *) open Eth -open Common +open Common open Types.Ethereum_analysis open Types.Ethereum_decode -open Types.Ethereum_indexer + +(* open Types.Ethereum_indexer *) open Olympus.Types open Tools - let eq_inverse_token_swapped ts1 ts2 = ts1.ts_out_token = ts2.ts_in_token && ts1.ts_in_token = ts2.ts_out_token @@ -62,56 +62,29 @@ let get_int_from_evmvalue (contract_or_evm_value : known_address_or_evm_value) = @@ 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 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 decode_abi_mint_v3 log abi = try let inputs = @@ -333,51 +306,33 @@ let decode_abi_v3 event_hash abi s_contract = Log.log @@ Printexc.to_string e ; None -let get_event_generic (signature : b) decode log = - Lwt.return - @@ - match log.dtrl_decode_signature with - | None -> None - | Some s -> ( - match String.equal (s.evdi_hash :> string) (signature :> string) with - | false -> None - | true -> ( - let event_decode = s.evdi_decode in - 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_v3_swaps tx_logs = - Lwt_list.filter_map_s (get_event_generic v3_event_id decode_abi_v3) tx_logs - -let get_v3_mint tx_logs = - Lwt_list.filter_map_s - (get_event_generic v3_event_mint_id decode_abi_mint_v3) - tx_logs -let get_v3_burn tx_logs = +let get_swap tx_logs (event_id : b) decode = Lwt_list.filter_map_s - (get_event_generic v3_event_burn_id decode_abi_burn_v3) + (fun log -> + match log.dtrl_decode_signature with + | None -> Lwt.return_none + | Some s -> ( + match String.equal (s.evdi_hash :> string) (event_id :> string) with + | false -> Lwt.return_none + | true -> ( + let event_decode = s.evdi_decode in + match event_decode with + | Event_known_contract_abi abi -> + Lwt.return @@ decode log abi.evdka_inputs + | _ -> Lwt.return_none))) tx_logs +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 = - Lwt_list.filter_map_s - (get_event_generic v3_event_collect_id decode_abi_collect_v3) - tx_logs + get_swap tx_logs v3_event_collect_id decode_abi_collect_v3 let general_swapV3 (swap : uni_v3_swap) = try @@ -419,7 +374,7 @@ let general_swapV3 (swap : uni_v3_swap) = Log.log @@ Printexc.to_string e ; None -let get_event (transaction_trace : debug_trace_result) sandwich = +let get_event transaction_trace sandwich = let add_or_replace_information hashtbl key arg = let adding = match Hashtbl.find_opt hashtbl key with @@ -433,8 +388,7 @@ let get_event (transaction_trace : debug_trace_result) sandwich = | `collect l1, `collect l2 -> `collect (concat_list l1 l2) | _ -> assert false) in Hashtbl.replace hashtbl key adding in - let rec get_pair_swaps_v2_v3 (log : debug_trace_result) hashtbl - events_function = + let rec get_pair_swaps_v2_v3 log hashtbl events_function = let> _ = events_function log hashtbl in let> _ = Lwt_list.iter_s @@ -654,7 +608,7 @@ let detect_sandwich_liquidity swap_transaction_mint swap_transaction_burn let filterer_uni_v3_burn_list = List.find_all (fun sb -> - equal_db_contract_information sb.uv3b_contract + equal_contract_information sb.uv3b_contract stm.uv3m_contract && equal_z sb.uv3b_tick_upper stm.uv3m_tick_upper && equal_z sb.uv3b_tick_lower stm.uv3m_tick_lower) @@ -746,9 +700,9 @@ let detect_sandwich_liquidity swap_transaction_mint swap_transaction_burn | None -> Some (profit0, profit1) | Some (p0, p1) -> assert ( - equal_db_contract_information p0.tp_token profit0.tp_token) ; + equal_contract_information p0.tp_token profit0.tp_token) ; assert ( - equal_db_contract_information p1.tp_token profit1.tp_token) ; + equal_contract_information p1.tp_token profit1.tp_token) ; Some ( { tp_token = p0.tp_token; @@ -983,71 +937,43 @@ let merge_sandwich sandwich_liquidity_list = (fun mpu_sender arg acc -> { mpu_sender; mpu_per_contract = merge_sandwich_per_contract arg } :: acc) hashtbl_sandwich_per_user [] -(* -let merge_sandwich_with_liquidity sandwich sandwich_liquidity_list = - let hashtbl_add_to_list hashtbl key arg = - let content = - match Hashtbl.find_opt hashtbl key with - | None -> [arg] - | Some l -> arg :: l in - Hashtbl.replace hashtbl key content in - - let add_profit hashtbl_token_profit key_token profit = - let content = - match Hashtbl.find_opt hashtbl_token_profit key_token with - | None -> profit - | Some z -> Z.add z profit in - Hashtbl.replace hashtbl_token_profit key_token content in - - let create_type mpc_contract mpc_sandwich_list = - let hashtbl_token_profit = Hashtbl.create 7 in - List.iter - (fun sandwich -> - match sandwich with - | `sandwich sandwich -> - add_profit hashtbl_token_profit sandwich.sdo_profit.tp_token - sandwich.sdo_profit.tp_profit - | `sandwich_liquidity sandwich -> - let token_profit1, token_profit2 = sandwich.sl_profit in - add_profit hashtbl_token_profit token_profit1.tp_token - token_profit1.tp_profit ; - add_profit hashtbl_token_profit token_profit2.tp_token - token_profit2.tp_profit) - mpc_sandwich_list ; - - let mpc_profit = - Hashtbl.fold - (fun tp_token tp_profit acc -> { tp_token; tp_profit } :: acc) - hashtbl_token_profit [] in - { mpc_contract; mpc_sandwich_list; mpc_profit } in - - let merge_sandwich_per_contract sandwich_list = - let hashtbl_sandwich_per_contract = Hashtbl.create 7 in - List.iter - (fun sandwich -> - let contract = - match sandwich with - | `sandwich sandwich -> - (List.hd sandwich.sdo_front_run.sto_swap_list).s_contract - | `sandwich_liquidity sandwich -> - sandwich.sl_mint.stmf_mint.uv3m_contract in - - hashtbl_add_to_list hashtbl_sandwich_per_contract contract sandwich) - sandwich_list ; - Hashtbl.fold - (fun key arg acc -> create_type key arg :: acc) - hashtbl_sandwich_per_contract [] in - - let hashtbl_sandwich_per_user = Hashtbl.create 7 in - List.iter - (fun sandwich -> - let sender = - match sandwich with - | `sandwich sandwich -> sandwich.sdo_front_run.sto_sender - | `sandwich_liquidity sandwich -> sandwich.sl_mint.stmf_sender in - hashtbl_add_to_list hashtbl_sandwich_per_user sender sandwich) - sandwich_liquidity_list ; - Hashtbl.fold - (fun mpu_sender arg acc -> - { mpu_sender; mpu_per_contract = merge_sandwich_per_contract arg } :: acc) - hashtbl_sandwich_per_user [] *) +(* let merge_sandwich_with_liquidity sandwich sandwich_liquidity_list = let + hashtbl_add_to_list hashtbl key arg = let content = match Hashtbl.find_opt + hashtbl key with | None -> [arg] | Some l -> arg :: l in Hashtbl.replace + hashtbl key content in + + let add_profit hashtbl_token_profit key_token profit = let content = match + Hashtbl.find_opt hashtbl_token_profit key_token with | None -> profit | Some + z -> Z.add z profit in Hashtbl.replace hashtbl_token_profit key_token content + in + + let create_type mpc_contract mpc_sandwich_list = let hashtbl_token_profit = + Hashtbl.create 7 in List.iter (fun sandwich -> match sandwich with | + `sandwich sandwich -> add_profit hashtbl_token_profit + sandwich.sdo_profit.tp_token sandwich.sdo_profit.tp_profit | + `sandwich_liquidity sandwich -> let token_profit1, token_profit2 = + sandwich.sl_profit in add_profit hashtbl_token_profit token_profit1.tp_token + token_profit1.tp_profit ; add_profit hashtbl_token_profit + token_profit2.tp_token token_profit2.tp_profit) mpc_sandwich_list ; + + let mpc_profit = Hashtbl.fold (fun tp_token tp_profit acc -> { tp_token; + tp_profit } :: acc) hashtbl_token_profit [] in { mpc_contract; + mpc_sandwich_list; mpc_profit } in + + let merge_sandwich_per_contract sandwich_list = let + hashtbl_sandwich_per_contract = Hashtbl.create 7 in List.iter (fun sandwich + -> let contract = match sandwich with | `sandwich sandwich -> (List.hd + sandwich.sdo_front_run.sto_swap_list).s_contract | `sandwich_liquidity + sandwich -> sandwich.sl_mint.stmf_mint.uv3m_contract in + + hashtbl_add_to_list hashtbl_sandwich_per_contract contract sandwich) + sandwich_list ; Hashtbl.fold (fun key arg acc -> create_type key arg :: acc) + hashtbl_sandwich_per_contract [] in + + let hashtbl_sandwich_per_user = Hashtbl.create 7 in List.iter (fun sandwich + -> let sender = match sandwich with | `sandwich sandwich -> + sandwich.sdo_front_run.sto_sender | `sandwich_liquidity sandwich -> + sandwich.sl_mint.stmf_sender in hashtbl_add_to_list hashtbl_sandwich_per_user + sender sandwich) sandwich_liquidity_list ; Hashtbl.fold (fun mpu_sender arg + acc -> { mpu_sender; mpu_per_contract = merge_sandwich_per_contract arg } :: + acc) hashtbl_sandwich_per_user [] *) diff --git a/src/ethereum_decode/eth_decode.ml b/src/ethereum_decode/eth_decode.ml index 911742bd..8fe0a411 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 73c0aee9..bc9b5335 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 @@ -321,9 +321,10 @@ VALUES (decode($tx_hash,'hex'),${block_number},${analysis_type_integer},${tag_id | A_Arbitrage _trace_analysis -> Log.log_warning_fail ~here:[%here] "Arbitrage analysis not yet stored in the db" - | A_Sandwich_liquidity _sandwich_liquidity -> (* TODO *) + | A_Sandwich_liquidity _sandwich_liquidity -> + (* TODO *) Log.log_warning_fail ~here:[%here] - "Sandwich liquidity analysis not yet stored in the db" + "Sandwich liquidity analysis not yet stored in the db" module Sandwich_fetched_id = Set.Make (Int64) module Analysis_map = Map.Make (Int) @@ -382,7 +383,6 @@ let build_tx_output_list (analyses : 'a analysis list) = | A_Transfer _ | A_Arbitrage _ -> Log.log_error_fail ~here:[%here] "Bottom" | A_Liquidate liquidate_all -> - let result = { liquidate_all with diff --git a/test/test_common/test_common.ml b/test/test_common/test_common.ml index b702809b..26544234 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." -- GitLab From 3c3bc079feffef7564f311402653ac1e04d2b989 Mon Sep 17 00:00:00 2001 From: "rayane.lattari" Date: Thu, 18 Sep 2025 09:45:35 +0200 Subject: [PATCH 18/33] [Ethereum-analysis-sandwich-liquidity] add file sandwich liquidity --- src/ethereum_analysis/ethereum_analysis_sandwich_liquidity.ml | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 src/ethereum_analysis/ethereum_analysis_sandwich_liquidity.ml diff --git a/src/ethereum_analysis/ethereum_analysis_sandwich_liquidity.ml b/src/ethereum_analysis/ethereum_analysis_sandwich_liquidity.ml new file mode 100644 index 00000000..e69de29b -- GitLab From 12d9a4ad3981fceae4df6251f1c644a79a136747 Mon Sep 17 00:00:00 2001 From: "rayane.lattari" Date: Thu, 18 Sep 2025 09:47:20 +0200 Subject: [PATCH 19/33] [Ethereum-analysis-sandwich-liquidity] add JIT sandwich types, and remove unused --- src/common/types.ml | 141 +++++++++++++++++++++++++++----------------- 1 file changed, 88 insertions(+), 53 deletions(-) diff --git a/src/common/types.ml b/src/common/types.ml index b62d5dad..91e83a58 100644 --- a/src/common/types.ml +++ b/src/common/types.ml @@ -451,6 +451,7 @@ 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; @@ -464,6 +465,7 @@ module Ethereum_analysis = struct [@@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; @@ -478,6 +480,8 @@ module Ethereum_analysis = struct [@@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; @@ -492,6 +496,8 @@ module Ethereum_analysis = struct [@@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; @@ -505,6 +511,7 @@ module Ethereum_analysis = struct [@@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; @@ -529,11 +536,20 @@ module Ethereum_analysis = struct } [@@deriving encoding { camel; remove_prefix = "tp_" }, eq] + type swap_transaction_output = { + sto_transaction_hash : b; + 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] @@ -542,6 +558,7 @@ module Ethereum_analysis = struct 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] @@ -553,30 +570,6 @@ module Ethereum_analysis = struct } [@@deriving encoding { camel; remove_prefix = "stc_" }, eq] - type swap_transaction_mint_final = { - stmf_transaction_hash : b; - stmf_index : int; - stmf_sender : known_address_or_address; - stmf_mint : uni_v3_mint; - } - [@@deriving encoding { camel; remove_prefix = "stmf_" }, eq] - - type swap_transaction_burn_final = { - stbf_transaction_hash : b; - stbf_index : int; - stbf_sender : known_address_or_address; - stbf_burn : uni_v3_burn; - } - [@@deriving encoding { camel; remove_prefix = "stbf_" }, eq] - - type swap_transaction_collect_final = { - stcf_transaction_hash : b; - stcf_index : int; - stcf_sender : known_address_or_address; - stcf_collect : uni_v3_collect; - } - [@@deriving encoding { camel; remove_prefix = "stcf_" }, eq] - type events = [ `swapV2 of uni_v2_swap list | `swapV3 of uni_v3_swap list @@ -585,44 +578,86 @@ module Ethereum_analysis = struct | `collect of uni_v3_collect list ] [@@deriving encoding { camel }] - type key_burn = { - kb_transaction_hash : b; - kb_contract : contract_information; - kb_tick_lower : z; - kb_tick_upper : z; + 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 = "kb_" }, eq] + [@@deriving encoding { camel; remove_prefix = "slp_" }, eq] - type burn_collect = { - bc_transaction_hash : b; - bc_index : int; - bc_sender : known_address_or_address; - bc_burn : uni_v3_burn; - bc_collect : uni_v3_collect; - bc_profit : token_profit * token_profit; + type token_liquidity = { + tl_amount0 : z; + tl_amount1 : z; + tl_token0 : contract_information; + tl_token1 : contract_information; } - [@@deriving encoding { camel; remove_prefix = "bc_" }, eq] + [@@deriving encoding { camel; remove_prefix = "tl_" }, eq] - type sandwich_liquidity_possible = { - slp_mint : swap_transaction_mint_final; - slp_burn : key_burn list; + type token_liquidity_pool = { + tlp_amount : z; + tlp_liquidity : token_liquidity; } - [@@deriving encoding { camel; remove_prefix = "slp_" }, eq] + [@@deriving encoding { camel; remove_prefix = "tlp_" }, eq] - type sandwich_liquidity = { - sl_mint : swap_transaction_mint_final; - sl_burn_collect : burn_collect list; - sl_profit : token_profit * token_profit; + type sandwich_liquidity_burn_collect = { + slbc_burn : token_liquidity_pool; + slbc_collect : token_liquidity; } - [@@deriving encoding { camel; remove_prefix = "sl_" }, eq] + [@@deriving encoding { camel; remove_prefix = "slbc_" }, eq] - type swap_transaction_output = { - sto_transaction_hash : b; - sto_index : int; - sto_sender : known_address_or_address; - sto_swap_list : swap list; + 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 = "sto_" }, eq] + [@@deriving encoding { camel; remove_prefix = "sl_" }, eq] type sandwich_possible = { sdp_front_run : swap_transaction_output; -- GitLab From b2d4e51463dc148cea8f00674794a93fae87c565 Mon Sep 17 00:00:00 2001 From: "rayane.lattari" Date: Thu, 18 Sep 2025 09:48:17 +0200 Subject: [PATCH 20/33] [Ethereum-analysis-sandwich-liquidity] move general_swapV2 --- .../eth_analysis_sandwich.ml | 46 +++++++++---------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/src/ethereum_analysis/eth_analysis_sandwich.ml b/src/ethereum_analysis/eth_analysis_sandwich.ml index ad3c305b..c8a84382 100644 --- a/src/ethereum_analysis/eth_analysis_sandwich.ml +++ b/src/ethereum_analysis/eth_analysis_sandwich.ml @@ -62,29 +62,6 @@ let get_int_from_evmvalue (contract_or_evm_value : known_address_or_evm_value) = @@ 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 decode_abi_mint_v3 log abi = try let inputs = @@ -334,6 +311,29 @@ 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 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 = -- GitLab From 5f3b4dc5cd2e7cd79ddfcc1a99c8337697bcd573 Mon Sep 17 00:00:00 2001 From: "rayane.lattari" Date: Thu, 18 Sep 2025 09:50:21 +0200 Subject: [PATCH 21/33] [Ethereum-analysis-sandwich-liquidity] fix imports, and fix decode --- src/ethereum_analysis/eth_analysis_sandwich.ml | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/ethereum_analysis/eth_analysis_sandwich.ml b/src/ethereum_analysis/eth_analysis_sandwich.ml index c8a84382..6df22f78 100644 --- a/src/ethereum_analysis/eth_analysis_sandwich.ml +++ b/src/ethereum_analysis/eth_analysis_sandwich.ml @@ -1,10 +1,7 @@ -(* open Lwt *) open Eth open Common open Types.Ethereum_analysis open Types.Ethereum_decode - -(* open Types.Ethereum_indexer *) open Olympus.Types open Tools @@ -93,6 +90,8 @@ let decode_abi_mint_v3 log abi = get_int_from_evmvalue tmp.pda_data in Some { + uv3m_victim = []; + uv3m_index = log.dtrl_index; uv3m_contract; uv3m_token0; uv3m_token1; @@ -137,6 +136,8 @@ let decode_abi_burn_v3 log abi = Some { + uv3b_victim = []; + uv3b_index = log.dtrl_index; uv3b_contract; uv3b_token0; uv3b_token1; @@ -180,6 +181,7 @@ let decode_abi_collect_v3 log abi = Some { + uv3c_index = log.dtrl_index; uv3c_contract; uv3c_owner; uv3c_recipient; @@ -222,6 +224,7 @@ let decode_abi_v2 log abi = 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; @@ -268,6 +271,7 @@ let decode_abi_v3 event_hash abi s_contract = Some { + uv3s_index = log.dtrl_index; uv3s_contract; uv3s_sender; uv3s_recipient; @@ -283,7 +287,6 @@ let decode_abi_v3 event_hash abi s_contract = Log.log @@ Printexc.to_string e ; None - let get_swap tx_logs (event_id : b) decode = Lwt_list.filter_map_s (fun log -> -- GitLab From 469bf20846e56995ba36f05676e64e024892c02b Mon Sep 17 00:00:00 2001 From: "rayane.lattari" Date: Thu, 18 Sep 2025 09:51:15 +0200 Subject: [PATCH 22/33] [Ethereum-analysis-sandwich-liquidity] comment merging --- .../eth_analysis_sandwich.ml | 106 +++++++----------- 1 file changed, 39 insertions(+), 67 deletions(-) diff --git a/src/ethereum_analysis/eth_analysis_sandwich.ml b/src/ethereum_analysis/eth_analysis_sandwich.ml index 6df22f78..7e68e3b6 100644 --- a/src/ethereum_analysis/eth_analysis_sandwich.ml +++ b/src/ethereum_analysis/eth_analysis_sandwich.ml @@ -872,74 +872,46 @@ let detect_sandwich debug_trace_list sandwich = detect_sandwich_liquidity swap_transaction_mint swap_transaction_burn swap_transaction_collect in Lwt.return @@ sandwich_liquidity +(* let merge_sandwich sandwich_liquidity_list = let hashtbl_add_to_list hashtbl + key arg = let content = match Hashtbl.find_opt hashtbl key with | None -> + [arg] | Some l -> arg :: l in Hashtbl.replace hashtbl key content in + + let add_profit hashtbl_token_profit key_token profit = let content = match + Hashtbl.find_opt hashtbl_token_profit key_token with | None -> profit | Some + z -> Z.add z profit in Hashtbl.replace hashtbl_token_profit key_token content + in + + let create_type mpc_contract mpc_sandwich_list = let hashtbl_token_profit = + Hashtbl.create 7 in List.iter (fun sandwich -> match sandwich with | + `sandwich sandwich -> add_profit hashtbl_token_profit + sandwich.sdo_profit.tp_token sandwich.sdo_profit.tp_profit | + `sandwich_liquidity sandwich -> let token_profit1, token_profit2 = + sandwich.sl_profit in add_profit hashtbl_token_profit token_profit1.tp_token + token_profit1.tp_profit ; add_profit hashtbl_token_profit + token_profit2.tp_token token_profit2.tp_profit) mpc_sandwich_list ; + + let mpc_profit = Hashtbl.fold (fun tp_token tp_profit acc -> { tp_token; + tp_profit } :: acc) hashtbl_token_profit [] in { mpc_contract; + mpc_sandwich_list; mpc_profit } in + + let merge_sandwich_per_contract sandwich_list = let + hashtbl_sandwich_per_contract = Hashtbl.create 7 in List.iter (fun sandwich + -> let contract = match sandwich with | `sandwich sandwich -> (List.hd + sandwich.sdo_front_run.sto_swap_list).s_contract | `sandwich_liquidity + sandwich -> sandwich.sl_mint.stmf_mint.uv3m_contract in + + hashtbl_add_to_list hashtbl_sandwich_per_contract contract sandwich) + sandwich_list ; Hashtbl.fold (fun key arg acc -> create_type key arg :: acc) + hashtbl_sandwich_per_contract [] in + + let hashtbl_sandwich_per_user = Hashtbl.create 7 in List.iter (fun sandwich + -> let sender = match sandwich with | `sandwich sandwich -> + sandwich.sdo_front_run.sto_sender | `sandwich_liquidity sandwich -> + sandwich.sl_mint.stmf_sender in hashtbl_add_to_list hashtbl_sandwich_per_user + sender sandwich) sandwich_liquidity_list ; Hashtbl.fold (fun mpu_sender arg + acc -> { mpu_sender; mpu_per_contract = merge_sandwich_per_contract arg } :: + acc) hashtbl_sandwich_per_user [] *) -let merge_sandwich sandwich_liquidity_list = - let hashtbl_add_to_list hashtbl key arg = - let content = - match Hashtbl.find_opt hashtbl key with - | None -> [arg] - | Some l -> arg :: l in - Hashtbl.replace hashtbl key content in - - let add_profit hashtbl_token_profit key_token profit = - let content = - match Hashtbl.find_opt hashtbl_token_profit key_token with - | None -> profit - | Some z -> Z.add z profit in - Hashtbl.replace hashtbl_token_profit key_token content in - - let create_type mpc_contract mpc_sandwich_list = - let hashtbl_token_profit = Hashtbl.create 7 in - List.iter - (fun sandwich -> - match sandwich with - | `sandwich sandwich -> - add_profit hashtbl_token_profit sandwich.sdo_profit.tp_token - sandwich.sdo_profit.tp_profit - | `sandwich_liquidity sandwich -> - let token_profit1, token_profit2 = sandwich.sl_profit in - add_profit hashtbl_token_profit token_profit1.tp_token - token_profit1.tp_profit ; - add_profit hashtbl_token_profit token_profit2.tp_token - token_profit2.tp_profit) - mpc_sandwich_list ; - - let mpc_profit = - Hashtbl.fold - (fun tp_token tp_profit acc -> { tp_token; tp_profit } :: acc) - hashtbl_token_profit [] in - { mpc_contract; mpc_sandwich_list; mpc_profit } in - - let merge_sandwich_per_contract sandwich_list = - let hashtbl_sandwich_per_contract = Hashtbl.create 7 in - List.iter - (fun sandwich -> - let contract = - match sandwich with - | `sandwich sandwich -> - (List.hd sandwich.sdo_front_run.sto_swap_list).s_contract - | `sandwich_liquidity sandwich -> - sandwich.sl_mint.stmf_mint.uv3m_contract in - - hashtbl_add_to_list hashtbl_sandwich_per_contract contract sandwich) - sandwich_list ; - Hashtbl.fold - (fun key arg acc -> create_type key arg :: acc) - hashtbl_sandwich_per_contract [] in - - let hashtbl_sandwich_per_user = Hashtbl.create 7 in - List.iter - (fun sandwich -> - let sender = - match sandwich with - | `sandwich sandwich -> sandwich.sdo_front_run.sto_sender - | `sandwich_liquidity sandwich -> sandwich.sl_mint.stmf_sender in - hashtbl_add_to_list hashtbl_sandwich_per_user sender sandwich) - sandwich_liquidity_list ; - Hashtbl.fold - (fun mpu_sender arg acc -> - { mpu_sender; mpu_per_contract = merge_sandwich_per_contract arg } :: acc) - hashtbl_sandwich_per_user [] (* let merge_sandwich_with_liquidity sandwich sandwich_liquidity_list = let hashtbl_add_to_list hashtbl key arg = let content = match Hashtbl.find_opt hashtbl key with | None -> [arg] | Some l -> arg :: l in Hashtbl.replace -- GitLab From ac4d794325732b5aa7c5754060eaa42a7ca1008c Mon Sep 17 00:00:00 2001 From: "rayane.lattari" Date: Thu, 18 Sep 2025 15:10:06 +0200 Subject: [PATCH 23/33] [Ethereum-analysis-sandwich-liquidity] update Dune, fix imports --- src/ethereum_analysis/dune | 1 + src/ethereum_analysis/eth_analysis_sandwich.ml | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ethereum_analysis/dune b/src/ethereum_analysis/dune index 82ae478d..e4749444 100644 --- a/src/ethereum_analysis/dune +++ b/src/ethereum_analysis/dune @@ -7,6 +7,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_sandwich.ml b/src/ethereum_analysis/eth_analysis_sandwich.ml index 7e68e3b6..9a993e34 100644 --- a/src/ethereum_analysis/eth_analysis_sandwich.ml +++ b/src/ethereum_analysis/eth_analysis_sandwich.ml @@ -1,7 +1,6 @@ open Eth open Common open Types.Ethereum_analysis -open Types.Ethereum_decode open Olympus.Types open Tools -- GitLab From 04e8a1aa807ebb774e8af64ca0b34ef9bfcee835 Mon Sep 17 00:00:00 2001 From: "rayane.lattari" Date: Thu, 18 Sep 2025 15:11:15 +0200 Subject: [PATCH 24/33] [Ethereum-analysis-sandwich-liquidity] change file name, and add sandwich liquidity functions --- .../eth_analysis_sandwich_liquidity.ml | 527 ++++++++++++++++++ .../ethereum_analysis_sandwich_liquidity.ml | 0 2 files changed, 527 insertions(+) create mode 100644 src/ethereum_analysis/eth_analysis_sandwich_liquidity.ml delete mode 100644 src/ethereum_analysis/ethereum_analysis_sandwich_liquidity.ml 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 00000000..06839701 --- /dev/null +++ b/src/ethereum_analysis/eth_analysis_sandwich_liquidity.ml @@ -0,0 +1,527 @@ +open Eth +open Common +open Types.Ethereum_analysis +open Types.Ethereum_decode +open Olympus.Types +open Tools +open Eth_analysis_utils + +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) ; + Lwt.return_unit + | 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 + Lwt.return_unit in + + let> _ = + Eth_tools.get_pair_swaps_v2_v3 events_function_sandwich_liquidity + transaction_trace hashtbl in + Lwt.return 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 + `sandwich_liquidity + { + 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 ) = + Lwt_list.fold_left_s + (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 + + Lwt.return (v3_mint, v3_burn, v3_collect, v3_swap)) + ([], [], [], []) debug_trace_list in + + Lwt.return + ( 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 + Lwt.return sandwich_liquidity diff --git a/src/ethereum_analysis/ethereum_analysis_sandwich_liquidity.ml b/src/ethereum_analysis/ethereum_analysis_sandwich_liquidity.ml deleted file mode 100644 index e69de29b..00000000 -- GitLab From b92d357f544338709b3ef2a5cc320fb9e8c1e195 Mon Sep 17 00:00:00 2001 From: "rayane.lattari" Date: Thu, 18 Sep 2025 15:12:15 +0200 Subject: [PATCH 25/33] [Ethereum-analysis-sandwich-liquidity] move common functions to eth_tools --- .../eth_analysis_sandwich.ml | 806 ++---------------- .../eth_analysis_tools/eth_tools.ml | 526 ++++++++++++ 2 files changed, 586 insertions(+), 746 deletions(-) diff --git a/src/ethereum_analysis/eth_analysis_sandwich.ml b/src/ethereum_analysis/eth_analysis_sandwich.ml index 9a993e34..4725673c 100644 --- a/src/ethereum_analysis/eth_analysis_sandwich.ml +++ b/src/ethereum_analysis/eth_analysis_sandwich.ml @@ -3,450 +3,21 @@ open Common open Types.Ethereum_analysis open Olympus.Types open Tools - -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 v3_event_mint_id = - b "7a53080ba414158be7ec69b987b5fb7d07dee101fe85488f0853ae16239d0bde" - -let v3_event_burn_id = - b "0c396cd989a39f4459b5fa1aed6a9a8dcdbc45908acfd67e028cd568da98982c" - -let v3_event_collect_id = - b "70935338e69775456a85ddef226c395fb668b63fa0115f5f20610b388e6ca9c0" - -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_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 event_hash abi s_contract = - 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 get_swap tx_logs (event_id : b) decode = - Lwt_list.filter_map_s - (fun log -> - match log.dtrl_decode_signature with - | None -> Lwt.return_none - | Some s -> ( - match String.equal (s.evdi_hash :> string) (event_id :> string) with - | false -> Lwt.return_none - | true -> ( - let event_decode = s.evdi_decode in - match event_decode with - | Event_known_contract_abi abi -> - Lwt.return @@ decode log abi.evdka_inputs - | _ -> Lwt.return_none))) - tx_logs - -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 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_event transaction_trace sandwich = - 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) - | _ -> assert false) in - Hashtbl.replace hashtbl key adding in - let rec get_pair_swaps_v2_v3 log hashtbl events_function = - let> _ = events_function log hashtbl in - let> _ = - Lwt_list.iter_s - (fun arg -> get_pair_swaps_v2_v3 arg hashtbl events_function) - log.dtr_calls in - +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) ; Lwt.return_unit in - - let hashtbl = Hashtbl.create 7 in - - match sandwich with - | `sandwich -> - Hashtbl.add hashtbl `swapV2 (`swapV2 []) ; - Hashtbl.add hashtbl `swapV3 (`swapV3 []) ; - let event log hashtbl = - let> v2 = get_v2_swaps log.dtr_logs in - let> v3 = get_v3_swaps log.dtr_logs in - add_or_replace_information hashtbl `swapV2 (`swapV2 v2) ; - add_or_replace_information hashtbl `swapV3 (`swapV3 v3) ; - Lwt.return_unit in - - let> _ = get_pair_swaps_v2_v3 transaction_trace hashtbl event in - Lwt.return hashtbl - | `sandwich_liquidity -> - Hashtbl.add hashtbl `mint (`mint []) ; - Hashtbl.add hashtbl `burn (`burn []) ; - Hashtbl.add hashtbl `collect (`collect []) ; - let event log hashtbl = - let> v3_b = get_v3_burn log.dtr_logs in - let> v3_m = get_v3_mint log.dtr_logs in - let> v3_c = get_v3_collect log.dtr_logs in - add_or_replace_information hashtbl `mint (`mint v3_m) ; - add_or_replace_information hashtbl `burn (`burn v3_b) ; - add_or_replace_information hashtbl `collect (`collect v3_c) ; - Lwt.return_unit in - - let> _ = get_pair_swaps_v2_v3 transaction_trace hashtbl event in - Lwt.return hashtbl - -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> _ = + Eth_tools.get_pair_swaps_v2_v3 events_function_sandwich transaction_trace + hashtbl in + Lwt.return hashtbl let detect_fr_br (swap_transaction_list : swap_transaction list) = let rec inter (swap_transaction_list : swap_transaction list) acc_sandwich = @@ -468,7 +39,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 @@ -479,7 +50,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 = @@ -489,7 +60,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 = @@ -524,7 +95,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 @@ -534,7 +105,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; @@ -562,294 +133,49 @@ let extract_sandwich_list (sandwich_possible_list : sandwich_possible list) | Some sandwich -> `sandwich sandwich :: acc_sandwich) [] sandwich_possible_list -let detect_sandwich_liquidity swap_transaction_mint swap_transaction_burn - swap_transaction_collect = - let rec find_opt_final burn uni_v3_collect_list = - match uni_v3_collect_list with - | [] -> None - | c :: uv3cl -> - if - not - (c.uv3c_contract = burn.stbf_burn.uv3b_contract - && Z.equal burn.stbf_burn.uv3b_tick_upper c.uv3c_tick_upper - && Z.equal burn.stbf_burn.uv3b_tick_lower c.uv3c_tick_lower) - then - find_opt_final burn uv3cl - else - Some c in - let find_collect hashtbl sort_c = - let final_hashtbl = Hashtbl.create 7 in - Hashtbl.iter - (fun key (burn, _) -> - let collect_swap_transac = - List.find_opt - (fun c -> burn.stbf_transaction_hash = c.stc_transaction_hash) - sort_c in - match collect_swap_transac with - | None -> () - | Some collect_swap_transac -> ( - match find_opt_final burn collect_swap_transac.stc_collect_list with - | None -> () - | Some collect -> Hashtbl.replace final_hashtbl key (burn, collect))) - hashtbl ; - final_hashtbl in - - let aux (sort_m : swap_transaction_mint) sort_b hashtbl = - List.fold_left - (fun acc_slp sort_b -> - if - not - (sort_b.stb_index > sort_m.stm_index - && equal_known_address_or_address sort_b.stb_sender - sort_m.stm_sender) - then - acc_slp - else - List.fold_left - (fun acc_slp stm -> - let filterer_uni_v3_burn_list = - List.find_all - (fun sb -> - equal_contract_information sb.uv3b_contract - stm.uv3m_contract - && equal_z sb.uv3b_tick_upper stm.uv3m_tick_upper - && equal_z sb.uv3b_tick_lower stm.uv3m_tick_lower) - sort_b.stb_burn_list in - - let kb_list = - List.fold_left - (fun acc_kb arg -> - let kb = - { - kb_transaction_hash = sort_b.stb_transaction_hash; - kb_contract = arg.uv3b_contract; - kb_tick_lower = arg.uv3b_tick_lower; - kb_tick_upper = arg.uv3b_tick_upper; - } in - let swap_transaction_burn_final = - { - stbf_transaction_hash = sort_b.stb_transaction_hash; - stbf_index = sort_b.stb_index; - stbf_sender = sort_b.stb_sender; - stbf_burn = arg; - } in - - Hashtbl.replace hashtbl kb - (swap_transaction_burn_final, None) ; - kb :: acc_kb) - [] filterer_uni_v3_burn_list in - match kb_list with - | [] -> acc_slp - | slp_burn -> - let slp_mint = - { - stmf_transaction_hash = sort_m.stm_transaction_hash; - stmf_index = sort_m.stm_index; - stmf_sender = sort_m.stm_sender; - stmf_mint = stm; - } in - { slp_mint; slp_burn } :: acc_slp) - acc_slp sort_m.stm_mint_list) - [] sort_b in - - let sort_m = - List.sort - (fun m1 m2 -> compare m1.stm_index m2.stm_index) - swap_transaction_mint in - let sort_b = - List.sort - (fun m1 m2 -> compare m1.stb_index m2.stb_index) - swap_transaction_burn in - let sort_c = - List.sort - (fun m1 m2 -> compare m1.stc_index m2.stc_index) - swap_transaction_collect in - - let hashtbl = Hashtbl.create 7 in - let sandwich_liquidity_possible_list = - List.fold_left - (fun acc slp_mint -> - match aux slp_mint sort_b hashtbl with - | [] -> acc - | slp -> List.fold_left (fun acc arg -> arg :: acc) acc slp) - [] sort_m in - - let hashtbl_final = find_collect hashtbl sort_c in - - List.fold_left - (fun result sandwich_liquidity_possible -> - let sl_burn_collect, sl_profit = - List.fold_left - (fun (acc_burn_collect, acc_profit) key_burn -> - match Hashtbl.find_opt hashtbl_final key_burn with - | None -> (acc_burn_collect, acc_profit) - | Some (burn, bc_collect) -> - let profit0 = - { - tp_token = bc_collect.uv3c_token0; - tp_profit = - Z.sub bc_collect.uv3c_amount0 burn.stbf_burn.uv3b_amount0; - } in - - let profit1 = - { - tp_token = bc_collect.uv3c_token1; - tp_profit = - Z.sub bc_collect.uv3c_amount1 burn.stbf_burn.uv3b_amount1; - } in - let profit = - match acc_profit with - | None -> Some (profit0, profit1) - | Some (p0, p1) -> - assert ( - equal_contract_information p0.tp_token profit0.tp_token) ; - assert ( - equal_contract_information p1.tp_token profit1.tp_token) ; - Some - ( { - tp_token = p0.tp_token; - tp_profit = Z.sub p0.tp_profit profit0.tp_profit; - }, - { - tp_token = p1.tp_token; - tp_profit = Z.sub p1.tp_profit profit1.tp_profit; - } ) in - - ( { - bc_transaction_hash = burn.stbf_transaction_hash; - bc_index = burn.stbf_index; - bc_sender = burn.stbf_sender; - bc_burn = burn.stbf_burn; - bc_collect; - bc_profit = (profit0, profit1); - } - :: acc_burn_collect, - profit )) - ([], None) sandwich_liquidity_possible.slp_burn in - - `sandwich_liquidity - { - sl_burn_collect; - sl_profit = Option.get sl_profit; - sl_mint = sandwich_liquidity_possible.slp_mint; - } - :: result) - [] sandwich_liquidity_possible_list - let detect_sandwich debug_trace_list sandwich = - 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) in - - let get_sanwdich_data arg transaction_hash sender index swap = - let> swapV2, swapV3 = - let> hashtbl = get_event arg.dt_result `sandwich 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 - - Lwt.return (swapV2, swapV3) in - - let swap_list = - let v2 = List.map general_swapV2 swapV2 in - let v3 = List.filter_map general_swapV3 swapV3 in - concat_list v2 v3 in - - let> swap = - match swap_list with - | [] -> Lwt.return swap - | swap_list -> - Lwt.return - @@ (gen_swap_transaction sender transaction_hash index swap_list :: swap) - in - Lwt.return swap in - - let get_sanwdich_liquidity_data arg transaction_hash sender index st_v3_m - st_v3_b st_v3_c = - let> v3_mint, v3_burn, v3_collect = - let> hashtbl = get_event arg.dt_result `sandwich_liquidity in - - let v3_mint = - let tmp = Hashtbl.find hashtbl `mint in - match tmp with - | `mint m -> m - | _ -> assert false in - - let v3_burn = - let tmp = Hashtbl.find hashtbl `burn in - match tmp with - | `burn b -> b - | _ -> assert false in - - let v3_collect = - let tmp = Hashtbl.find hashtbl `collect in - match tmp with - | `collect c -> c - | _ -> assert false in - - Lwt.return (v3_mint, v3_burn, v3_collect) in - let swap_transaction_mint = - match v3_mint with - | [] -> st_v3_m - | v3_mint -> - { - stm_transaction_hash = transaction_hash; - stm_index = index; - stm_sender = sender; - stm_mint_list = v3_mint; - } - :: st_v3_m in - - let swap_transaction_burn = - match v3_burn with - | [] -> st_v3_b - | v3_burn -> - { - stb_transaction_hash = transaction_hash; - stb_index = index; - stb_sender = sender; - stb_burn_list = v3_burn; - } - :: st_v3_b in - - let swap_transaction_collect = - match v3_collect 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 in - - Lwt.return - (swap_transaction_mint, swap_transaction_burn, swap_transaction_collect) - in + let get_sandwich_data debug_trace_list = + Lwt_list.fold_left_s + (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 + + Lwt.return (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 + | [] -> Lwt.return swap + | swap_list -> + Lwt.return + @@ Eth_tools.gen_swap_transaction sender transaction_hash index + swap_list + :: swap in + Lwt.return swap) + [] debug_trace_list in match sandwich with | `sandwich -> ( - let> swap_transaction_list = - Lwt_list.fold_left_s - (fun swap arg -> - let transaction_hash, sender, index = - get_transaction_information arg in - get_sanwdich_data arg transaction_hash sender index 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 Lwt.return @@ -857,20 +183,8 @@ let detect_sandwich debug_trace_list sandwich = match detect_fr_br with | [] -> [] | detect_fr_br -> extract_sandwich_list detect_fr_br swap_transaction_list) - | `sandwich_liquidity -> - let> swap_transaction_mint, swap_transaction_burn, swap_transaction_collect - = - Lwt_list.fold_left_s - (fun (st_v3_m, st_v3_b, st_v3_c) arg -> - let transaction_hash, sender, index = - get_transaction_information arg in - get_sanwdich_liquidity_data arg transaction_hash sender index st_v3_m - st_v3_b st_v3_c) - ([], [], []) debug_trace_list in - let sandwich_liquidity = - detect_sandwich_liquidity swap_transaction_mint swap_transaction_burn - swap_transaction_collect in - Lwt.return @@ sandwich_liquidity + | `sandwich_liquidity -> Lwt.return [] + (* let merge_sandwich sandwich_liquidity_list = let hashtbl_add_to_list hashtbl key arg = let content = match Hashtbl.find_opt hashtbl key with | None -> [arg] | Some l -> arg :: l in Hashtbl.replace hashtbl key content in diff --git a/src/ethereum_analysis/eth_analysis_tools/eth_tools.ml b/src/ethereum_analysis/eth_analysis_tools/eth_tools.ml index 4167204f..ea86d50e 100644 --- a/src/ethereum_analysis/eth_analysis_tools/eth_tools.ml +++ b/src/ethereum_analysis/eth_analysis_tools/eth_tools.ml @@ -1073,3 +1073,529 @@ 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 = + Lwt_list.filter_map_s + (fun log -> + match log.dtrl_decode_signature with + | None -> Lwt.return_none + | Some s -> ( + match String.equal (s.evdi_hash :> string) (event_id :> string) with + | false -> Lwt.return_none + | true -> ( + let event_decode = s.evdi_decode in + match event_decode with + | Event_known_contract_abi abi -> + Lwt.return @@ decode log abi.evdka_inputs + | _ -> Lwt.return_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> _ = + Lwt_list.iter_s + (fun arg -> get_pair_swaps_v2_v3 events_function arg hashtbl) + log.dtr_calls in + Lwt.return_unit -- GitLab From 9aee83be5362e879aac533e473dc48bf84e63403 Mon Sep 17 00:00:00 2001 From: "rayane.lattari" Date: Thu, 18 Sep 2025 15:12:41 +0200 Subject: [PATCH 26/33] [Ethereum-analysis-sandwich-liquidity] update eth_tools.mli with new functions --- .../eth_analysis_tools/eth_tools.mli | 182 ++++++++++++++++++ 1 file changed, 182 insertions(+) diff --git a/src/ethereum_analysis/eth_analysis_tools/eth_tools.mli b/src/ethereum_analysis/eth_analysis_tools/eth_tools.mli index a5bbc48d..1fda0c1a 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 Lwt.t) -> + ('a, 'b) Olympus.Types.debug_trace_result -> + 'c -> + unit Lwt.t + +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 Lwt.t + +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 Lwt.t + +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 Lwt.t + +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 Lwt.t + +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 Lwt.t + +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 -- GitLab From 69cdc70171ade11d676d5160e4ac3bae728aa3e9 Mon Sep 17 00:00:00 2001 From: "rayane.lattari" Date: Thu, 18 Sep 2025 16:23:21 +0200 Subject: [PATCH 27/33] [Ethereum-analysis-sandwich-liquidity] fix imports --- src/ethereum_analysis/eth_analysis_sandwich_liquidity.ml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ethereum_analysis/eth_analysis_sandwich_liquidity.ml b/src/ethereum_analysis/eth_analysis_sandwich_liquidity.ml index 06839701..6a5117cf 100644 --- a/src/ethereum_analysis/eth_analysis_sandwich_liquidity.ml +++ b/src/ethereum_analysis/eth_analysis_sandwich_liquidity.ml @@ -1,10 +1,10 @@ open Eth +open Eth_analysis_utils open Common +open Tools +open Olympus.Types open Types.Ethereum_analysis open Types.Ethereum_decode -open Olympus.Types -open Tools -open Eth_analysis_utils let get_event_sandwich transaction_trace hashtbl = Hashtbl.add hashtbl `swapV3 (`swapV3 []) ; -- GitLab From 9edd20352f16471086b36348f6df04bdaec9489a Mon Sep 17 00:00:00 2001 From: "rayane.lattari" Date: Thu, 18 Sep 2025 16:24:30 +0200 Subject: [PATCH 28/33] [Ethereum-analysis-sandwich-liquidity] remove Lwt usage, and update mli --- .../eth_analysis_sandwich.ml | 54 +++++++---------- .../eth_analysis_sandwich_liquidity.ml | 60 +++++++++---------- .../eth_analysis_tools/eth_tools.ml | 19 +++--- .../eth_analysis_tools/eth_tools.mli | 14 ++--- 4 files changed, 67 insertions(+), 80 deletions(-) diff --git a/src/ethereum_analysis/eth_analysis_sandwich.ml b/src/ethereum_analysis/eth_analysis_sandwich.ml index 4725673c..6d5a229d 100644 --- a/src/ethereum_analysis/eth_analysis_sandwich.ml +++ b/src/ethereum_analysis/eth_analysis_sandwich.ml @@ -1,4 +1,3 @@ -open Eth open Common open Types.Ethereum_analysis open Olympus.Types @@ -9,15 +8,14 @@ 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 + 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) ; - Lwt.return_unit in - let> _ = + 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 - Lwt.return hashtbl + hashtbl let detect_fr_br (swap_transaction_list : swap_transaction list) = let rec inter (swap_transaction_list : swap_transaction list) acc_sandwich = @@ -130,19 +128,19 @@ let extract_sandwich_list (sandwich_possible_list : sandwich_possible list) (fun acc_sandwich sandwich_possible -> match find_victim sandwich_possible swap_list with | None -> acc_sandwich - | Some sandwich -> `sandwich sandwich :: acc_sandwich) + | Some sandwich -> sandwich :: acc_sandwich) [] sandwich_possible_list -let detect_sandwich debug_trace_list sandwich = +let detect_sandwich debug_trace_list = let get_sandwich_data debug_trace_list = - Lwt_list.fold_left_s + List.fold_left (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, swapV3 = + let hashtbl = get_event_sandwich arg.dt_result hashtbl in let swapV2 = let tmp = Hashtbl.find hashtbl `swapV2 in @@ -155,35 +153,27 @@ let detect_sandwich debug_trace_list sandwich = | `swapV3 v3 -> v3 | _ -> assert false in - Lwt.return (swapV2, swapV3) 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 = + let swap = match swap_list with - | [] -> Lwt.return swap + | [] -> swap | swap_list -> - Lwt.return - @@ Eth_tools.gen_swap_transaction sender transaction_hash index - swap_list - :: swap in - Lwt.return swap) + Eth_tools.gen_swap_transaction sender transaction_hash index + swap_list + :: swap in + swap) [] debug_trace_list in - - match sandwich with - | `sandwich -> ( - let> swap_transaction_list = get_sandwich_data debug_trace_list in - - let detect_fr_br = detect_fr_br swap_transaction_list in - Lwt.return - @@ - match detect_fr_br with - | [] -> [] - | detect_fr_br -> extract_sandwich_list detect_fr_br swap_transaction_list) - | `sandwich_liquidity -> Lwt.return [] + 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 + | [] -> [] + | detect_fr_br -> extract_sandwich_list detect_fr_br swap_transaction_list (* let merge_sandwich sandwich_liquidity_list = let hashtbl_add_to_list hashtbl key arg = let content = match Hashtbl.find_opt hashtbl key with | None -> diff --git a/src/ethereum_analysis/eth_analysis_sandwich_liquidity.ml b/src/ethereum_analysis/eth_analysis_sandwich_liquidity.ml index 6a5117cf..4352ee40 100644 --- a/src/ethereum_analysis/eth_analysis_sandwich_liquidity.ml +++ b/src/ethereum_analysis/eth_analysis_sandwich_liquidity.ml @@ -12,18 +12,17 @@ let get_event_sandwich transaction_trace hashtbl = 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 + 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) ; - Lwt.return_unit + Eth_tools.add_or_replace_information hashtbl `burn (`burn v3_b) | l_v3_swap -> let _ = match (v3_m, v3_b) with @@ -55,12 +54,13 @@ let get_event_sandwich transaction_trace hashtbl = Hashtbl.replace hashtbl `burn (`burn burn_swap) ; Eth_tools.add_or_replace_information hashtbl `swapV3 (`swapV3 add_swap_list) in - Lwt.return_unit in - let> _ = + () in + + let _ = Eth_tools.get_pair_swaps_v2_v3 events_function_sandwich_liquidity transaction_trace hashtbl in - Lwt.return hashtbl + hashtbl let get_sandwich_liquidity_possible swap_transaction_mint swap_transaction_burn swap_transaction_collect = @@ -386,31 +386,30 @@ let detect_sandwich_liquidity swap_transaction_v3 tl_amount1 = Z.add burn_amount1 sl_mint_liquidity.tlp_liquidity.tl_amount1; } in - `sandwich_liquidity - { - 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; - } + { + 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 ) = - Lwt_list.fold_left_s + 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 hashtbl = get_event_sandwich arg.dt_result hashtbl in let v3_mint = let tmp = Hashtbl.find hashtbl `mint in @@ -513,15 +512,14 @@ let detect_sandwich debug_trace_list = swap_transaction_list :: st_v3_s) | _ -> Log.log_error_fail ~here:[%here] "couldn't find swap" in - Lwt.return (v3_mint, v3_burn, v3_collect, v3_swap)) + (v3_mint, v3_burn, v3_collect, v3_swap)) ([], [], [], []) debug_trace_list in - Lwt.return - ( swap_transaction_v3, - get_sandwich_liquidity_possible swap_transaction_mint - swap_transaction_burn swap_transaction_collect ) in - let> swap_transaction_v3, sandwich_possible = + ( 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 - Lwt.return sandwich_liquidity + sandwich_liquidity diff --git a/src/ethereum_analysis/eth_analysis_tools/eth_tools.ml b/src/ethereum_analysis/eth_analysis_tools/eth_tools.ml index ea86d50e..fc9e9e45 100644 --- a/src/ethereum_analysis/eth_analysis_tools/eth_tools.ml +++ b/src/ethereum_analysis/eth_analysis_tools/eth_tools.ml @@ -1122,19 +1122,18 @@ let get_burn_swap l_v3_b l_v3_swap hashtbl = ([], []) l_v3_swap let get_swap tx_logs (event_id : b) decode = - Lwt_list.filter_map_s + List.filter_map (fun log -> match log.dtrl_decode_signature with - | None -> Lwt.return_none + | None -> None | Some s -> ( match String.equal (s.evdi_hash :> string) (event_id :> string) with - | false -> Lwt.return_none + | false -> None | true -> ( let event_decode = s.evdi_decode in match event_decode with - | Event_known_contract_abi abi -> - Lwt.return @@ decode log abi.evdka_inputs - | _ -> Lwt.return_none))) + | Event_known_contract_abi abi -> decode log abi.evdka_inputs + | _ -> None))) tx_logs let eq_inverse_token_swapped ts1 ts2 = @@ -1593,9 +1592,9 @@ let add_or_replace_information hashtbl key arg = Hashtbl.replace hashtbl key adding let rec get_pair_swaps_v2_v3 events_function log hashtbl = - let> _ = events_function log hashtbl in - let> _ = - Lwt_list.iter_s + let _ = events_function log hashtbl in + let _ = + List.iter (fun arg -> get_pair_swaps_v2_v3 events_function arg hashtbl) log.dtr_calls in - Lwt.return_unit + () diff --git a/src/ethereum_analysis/eth_analysis_tools/eth_tools.mli b/src/ethereum_analysis/eth_analysis_tools/eth_tools.mli index 1fda0c1a..2a1e149f 100644 --- a/src/ethereum_analysis/eth_analysis_tools/eth_tools.mli +++ b/src/ethereum_analysis/eth_analysis_tools/eth_tools.mli @@ -340,10 +340,10 @@ val get_transaction_information : * Olympus.Types.index_or_position val get_pair_swaps_v2_v3 : - (('a, 'b) Olympus.Types.debug_trace_result -> 'c -> 'd Lwt.t) -> + (('a, 'b) Olympus.Types.debug_trace_result -> 'c -> 'd) -> ('a, 'b) Olympus.Types.debug_trace_result -> 'c -> - unit Lwt.t + unit val add_or_replace_information : ( 'a, @@ -366,35 +366,35 @@ val get_v3_collect : Common.Types.Ethereum_decode.known_address_or_evm_value ) Olympus.Types.debug_trace_result_log list -> - Common.Types.Ethereum_analysis.uni_v3_collect list Lwt.t + 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 Lwt.t + 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 Lwt.t + 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 Lwt.t + 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 Lwt.t + Common.Types.Ethereum_analysis.uni_v2_swap list val v3_event_collect_id : Eth.b -- GitLab From f7b664980e0ed6cc1ae0aa3a0f7630f90c66ec0d Mon Sep 17 00:00:00 2001 From: "rayane.lattari" Date: Thu, 18 Sep 2025 16:25:03 +0200 Subject: [PATCH 29/33] [Ethereum-analysis-sandwich-liquidity] add and update both sandwich mli --- .../eth_analysis_sandwich.mli | 6 +---- .../eth_analysis_sandwich_liquidity.mli | 25 +++++++++++++++++++ 2 files changed, 26 insertions(+), 5 deletions(-) create mode 100644 src/ethereum_analysis/eth_analysis_sandwich_liquidity.mli diff --git a/src/ethereum_analysis/eth_analysis_sandwich.mli b/src/ethereum_analysis/eth_analysis_sandwich.mli index abb92d83..1e17b9c5 100644 --- a/src/ethereum_analysis/eth_analysis_sandwich.mli +++ b/src/ethereum_analysis/eth_analysis_sandwich.mli @@ -19,8 +19,4 @@ val detect_sandwich : Common.Types.Ethereum_decode.known_address_or_evm_value ) Olympus.Types.debug_trace list -> - [< `sandwich | `sandwich_liquidity] -> - [> `sandwich of Common.Types.Ethereum_analysis.sandwich_output - | `sandwich_liquidity of Common.Types.Ethereum_analysis.sandwich_liquidity ] - list - Lwt.t + Common.Types.Ethereum_analysis.sandwich_output list 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 00000000..02c606fd --- /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 -- GitLab From abb81cdf155591f7aebea11825bc9267ab24a647 Mon Sep 17 00:00:00 2001 From: "rayane.lattari" Date: Thu, 18 Sep 2025 16:25:21 +0200 Subject: [PATCH 30/33] [Ethereum-analysis-sandwich-liquidity] fix with new sandwich types --- src/api/api_handlers_node.ml | 54 +++++++++++++++--------------------- 1 file changed, 22 insertions(+), 32 deletions(-) diff --git a/src/api/api_handlers_node.ml b/src/api/api_handlers_node.ml index 1ee95575..d203afd0 100644 --- a/src/api/api_handlers_node.ml +++ b/src/api/api_handlers_node.ml @@ -490,43 +490,33 @@ let get_transaction_tags_node_eth (_, hash) _ = (`tx_receipt receipt_decode) a in Lwt.return @@ (r :: acc) | `sandwich -> - let> sandwitches = + let sandwitches = Eth_analysis.Eth_analysis_sandwich.detect_sandwich - traces_decode `sandwich in + traces_decode in let include_tx_s = List.map (fun sandwich -> - match sandwich with - | `sandwich sandwich -> - Option.some - @@ A_Sandwich - { - bao_block_number = receipt.r_block_number; - bao_resume = sandwich; - } - | _ -> - Log.log_error_fail ~here:[%here] - "Expect Sandwich, got wrong type") + Some + (A_Sandwich + { + bao_block_number = receipt.r_block_number; + bao_resume = sandwich; + })) @@ List.filter (fun sandwich -> - match sandwich with - | `sandwich sandwich -> - String.equal - (sandwich.sdo_front_run.sto_transaction_hash - :> string) - hash - || String.equal - (sandwich.sdo_back_run.sto_transaction_hash - :> string) - hash - || List.exists - (fun vitim -> - String.equal - (vitim.sto_transaction_hash :> string) - hash) - sandwich.sdo_victims - | _ -> - Log.log_error_fail ~here:[%here] - "Expect Sandwich, got wrong type") + String.equal + (sandwich.sdo_front_run.sto_transaction_hash + :> string) + hash + || String.equal + (sandwich.sdo_back_run.sto_transaction_hash + :> string) + hash + || List.exists + (fun vitim -> + String.equal + (vitim.sto_transaction_hash :> string) + hash) + sandwich.sdo_victims) sandwitches in Lwt.return @@ Tools.concat_list include_tx_s acc | `sandwich_liquidity -> -- GitLab From 3a485df731ae8a6098881da97939ac74fb567809 Mon Sep 17 00:00:00 2001 From: "rayane.lattari" Date: Fri, 19 Sep 2025 14:18:51 +0200 Subject: [PATCH 31/33] [Ethereum-analysis-sandwich-liquidity] remove warnings, and comments --- src/ethereum_analysis/dune | 5 -- .../eth_analysis_sandwich.ml | 81 ------------------- 2 files changed, 86 deletions(-) diff --git a/src/ethereum_analysis/dune b/src/ethereum_analysis/dune index e4749444..e582269f 100644 --- a/src/ethereum_analysis/dune +++ b/src/ethereum_analysis/dune @@ -1,8 +1,3 @@ -(env - (_ - (flags - (:standard -w +a-4-41-44-45-48-70 -warn-error -a)))) - (library (name eth_analysis) (modules diff --git a/src/ethereum_analysis/eth_analysis_sandwich.ml b/src/ethereum_analysis/eth_analysis_sandwich.ml index 6d5a229d..a6a661ff 100644 --- a/src/ethereum_analysis/eth_analysis_sandwich.ml +++ b/src/ethereum_analysis/eth_analysis_sandwich.ml @@ -174,84 +174,3 @@ let detect_sandwich debug_trace_list = match detect_fr_br with | [] -> [] | detect_fr_br -> extract_sandwich_list detect_fr_br swap_transaction_list - -(* let merge_sandwich sandwich_liquidity_list = let hashtbl_add_to_list hashtbl - key arg = let content = match Hashtbl.find_opt hashtbl key with | None -> - [arg] | Some l -> arg :: l in Hashtbl.replace hashtbl key content in - - let add_profit hashtbl_token_profit key_token profit = let content = match - Hashtbl.find_opt hashtbl_token_profit key_token with | None -> profit | Some - z -> Z.add z profit in Hashtbl.replace hashtbl_token_profit key_token content - in - - let create_type mpc_contract mpc_sandwich_list = let hashtbl_token_profit = - Hashtbl.create 7 in List.iter (fun sandwich -> match sandwich with | - `sandwich sandwich -> add_profit hashtbl_token_profit - sandwich.sdo_profit.tp_token sandwich.sdo_profit.tp_profit | - `sandwich_liquidity sandwich -> let token_profit1, token_profit2 = - sandwich.sl_profit in add_profit hashtbl_token_profit token_profit1.tp_token - token_profit1.tp_profit ; add_profit hashtbl_token_profit - token_profit2.tp_token token_profit2.tp_profit) mpc_sandwich_list ; - - let mpc_profit = Hashtbl.fold (fun tp_token tp_profit acc -> { tp_token; - tp_profit } :: acc) hashtbl_token_profit [] in { mpc_contract; - mpc_sandwich_list; mpc_profit } in - - let merge_sandwich_per_contract sandwich_list = let - hashtbl_sandwich_per_contract = Hashtbl.create 7 in List.iter (fun sandwich - -> let contract = match sandwich with | `sandwich sandwich -> (List.hd - sandwich.sdo_front_run.sto_swap_list).s_contract | `sandwich_liquidity - sandwich -> sandwich.sl_mint.stmf_mint.uv3m_contract in - - hashtbl_add_to_list hashtbl_sandwich_per_contract contract sandwich) - sandwich_list ; Hashtbl.fold (fun key arg acc -> create_type key arg :: acc) - hashtbl_sandwich_per_contract [] in - - let hashtbl_sandwich_per_user = Hashtbl.create 7 in List.iter (fun sandwich - -> let sender = match sandwich with | `sandwich sandwich -> - sandwich.sdo_front_run.sto_sender | `sandwich_liquidity sandwich -> - sandwich.sl_mint.stmf_sender in hashtbl_add_to_list hashtbl_sandwich_per_user - sender sandwich) sandwich_liquidity_list ; Hashtbl.fold (fun mpu_sender arg - acc -> { mpu_sender; mpu_per_contract = merge_sandwich_per_contract arg } :: - acc) hashtbl_sandwich_per_user [] *) - -(* let merge_sandwich_with_liquidity sandwich sandwich_liquidity_list = let - hashtbl_add_to_list hashtbl key arg = let content = match Hashtbl.find_opt - hashtbl key with | None -> [arg] | Some l -> arg :: l in Hashtbl.replace - hashtbl key content in - - let add_profit hashtbl_token_profit key_token profit = let content = match - Hashtbl.find_opt hashtbl_token_profit key_token with | None -> profit | Some - z -> Z.add z profit in Hashtbl.replace hashtbl_token_profit key_token content - in - - let create_type mpc_contract mpc_sandwich_list = let hashtbl_token_profit = - Hashtbl.create 7 in List.iter (fun sandwich -> match sandwich with | - `sandwich sandwich -> add_profit hashtbl_token_profit - sandwich.sdo_profit.tp_token sandwich.sdo_profit.tp_profit | - `sandwich_liquidity sandwich -> let token_profit1, token_profit2 = - sandwich.sl_profit in add_profit hashtbl_token_profit token_profit1.tp_token - token_profit1.tp_profit ; add_profit hashtbl_token_profit - token_profit2.tp_token token_profit2.tp_profit) mpc_sandwich_list ; - - let mpc_profit = Hashtbl.fold (fun tp_token tp_profit acc -> { tp_token; - tp_profit } :: acc) hashtbl_token_profit [] in { mpc_contract; - mpc_sandwich_list; mpc_profit } in - - let merge_sandwich_per_contract sandwich_list = let - hashtbl_sandwich_per_contract = Hashtbl.create 7 in List.iter (fun sandwich - -> let contract = match sandwich with | `sandwich sandwich -> (List.hd - sandwich.sdo_front_run.sto_swap_list).s_contract | `sandwich_liquidity - sandwich -> sandwich.sl_mint.stmf_mint.uv3m_contract in - - hashtbl_add_to_list hashtbl_sandwich_per_contract contract sandwich) - sandwich_list ; Hashtbl.fold (fun key arg acc -> create_type key arg :: acc) - hashtbl_sandwich_per_contract [] in - - let hashtbl_sandwich_per_user = Hashtbl.create 7 in List.iter (fun sandwich - -> let sender = match sandwich with | `sandwich sandwich -> - sandwich.sdo_front_run.sto_sender | `sandwich_liquidity sandwich -> - sandwich.sl_mint.stmf_sender in hashtbl_add_to_list hashtbl_sandwich_per_user - sender sandwich) sandwich_liquidity_list ; Hashtbl.fold (fun mpu_sender arg - acc -> { mpu_sender; mpu_per_contract = merge_sandwich_per_contract arg } :: - acc) hashtbl_sandwich_per_user [] *) -- GitLab From 79c07500561c2d79f1f314218fc3432a67dafaac Mon Sep 17 00:00:00 2001 From: "rayane.lattari" Date: Mon, 27 Oct 2025 10:22:36 +0100 Subject: [PATCH 32/33] [Ethereum-analysis-sandwich-liquidity] fix rebase error --- src/api/api_handlers_node.ml | 101 ----------------------------------- 1 file changed, 101 deletions(-) diff --git a/src/api/api_handlers_node.ml b/src/api/api_handlers_node.ml index d203afd0..d1b83f52 100644 --- a/src/api/api_handlers_node.ml +++ b/src/api/api_handlers_node.ml @@ -450,108 +450,7 @@ let get_transaction_tags_node_eth (_, hash) _ = (fun exn -> EzAPIServer.return ~code:400 (Result.Error -<<<<<<< HEAD { op_error = __FUNCTION__; op_exception = Printexc.to_string exn })) -======= - { - op_error = "get_transaction_tags_node_eth"; - op_exception = EzEncoding.construct Eth.error_enc e; - }) - | Ok traces -> ( - let> transaction_r = Node_eth.get_transaction_by_hash (b hash) in - match transaction_r with - | Error e -> - EzAPIServer.return ~code:400 - (Result.Error - { - op_error = "get_transaction_tags_node_eth"; - op_exception = EzEncoding.construct Eth.error_enc e; - }) - | Ok transacion -> ( - let> block_r = Node_eth.get_block_by_number receipt.r_block_number in - match block_r with - | Error e -> - EzAPIServer.return ~code:400 - (Result.Error - { - op_error = "get_transaction_tags_node_eth"; - op_exception = EzEncoding.construct Eth.error_enc e; - }) - | Ok block -> - let> traces_decode = Lwt_list.map_s Decode_eth.decode_traces traces in - let> receipt_decode = Decode_eth.decode_transaction_receipt receipt in - let> analyses = - Lwt_list.fold_left_s - (fun acc analysis -> - match analysis with - | (`flashloan | `liquidate) as a -> - let> r = - Eth_analysis.Eth_analyse.tx_detect - (`tx_receipt receipt_decode) a in - Lwt.return @@ (r :: acc) - | `sandwich -> - let sandwitches = - Eth_analysis.Eth_analysis_sandwich.detect_sandwich - traces_decode in - let include_tx_s = - List.map (fun sandwich -> - Some - (A_Sandwich - { - bao_block_number = receipt.r_block_number; - bao_resume = sandwich; - })) - @@ List.filter - (fun sandwich -> - String.equal - (sandwich.sdo_front_run.sto_transaction_hash - :> string) - hash - || String.equal - (sandwich.sdo_back_run.sto_transaction_hash - :> string) - hash - || List.exists - (fun vitim -> - String.equal - (vitim.sto_transaction_hash :> string) - hash) - sandwich.sdo_victims) - sandwitches in - Lwt.return @@ Tools.concat_list include_tx_s acc - | `sandwich_liquidity -> - Log.log_error_lwt_fail ~here:[%here] "TODO Sandwich Liquidity" - | `transfer -> Lwt.return acc - | `arbitrage -> - let> miner = - Common.lift_lwt_opt - @@ Option.map - (fun x -> Decode_eth.decode_address x) - block.miner in - let cft_input = - { - cfti_block_number = block.number; - cfti_block_builder = miner; - cfti_base_fee = block.base_fee; - cfti_effective_price = receipt.r_effective_gas_price; - cfti_max_fee_per_gas = transacion.max_fee_per_gas; - cfti_gas_used = receipt.r_gas_used; - } in - let trace_of_tx = - List.find - (fun (trace : ('a, 'b) Olympus.Types.debug_trace) -> - String.equal (trace.dt_tx_hash :> string) hash) - traces_decode in - let> arbi_analyse = - Eth_analysis.Eth_analyse.tx_detect - (`tx_trace_with_bb_cost (trace_of_tx, cft_input)) - `arbitrage in - Lwt.return @@ (arbi_analyse :: acc) - | `bottom (_ : Types.Ethereum_analysis.bottom) -> .) - [] Api_handlers_common.available_analyses in - EzAPIServer.return_ok @@ filter_analyses - @@ List.filter_map Fun.id analyses))) ->>>>>>> a99234a ([Ethereum-analysis-sandwich-liquidity] sandwich liquidity handling) [@@service Api_services_node.get_transaction_tags_node_eth] let get_analyses_week_node_eth _ _ = -- GitLab From ac7c1c06d14f8bb782ffe0e7cd448456b59b380e Mon Sep 17 00:00:00 2001 From: "rayane.lattari" Date: Fri, 31 Oct 2025 22:17:55 +0100 Subject: [PATCH 33/33] [Ethereum-analysis-sandwich-liquidity] remove empty line --- src/ethereum_analysis/eth_analysis_sandwich.ml | 1 - 1 file changed, 1 deletion(-) diff --git a/src/ethereum_analysis/eth_analysis_sandwich.ml b/src/ethereum_analysis/eth_analysis_sandwich.ml index a6a661ff..1a112aec 100644 --- a/src/ethereum_analysis/eth_analysis_sandwich.ml +++ b/src/ethereum_analysis/eth_analysis_sandwich.ml @@ -68,7 +68,6 @@ 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 -> compare swap1.st_index swap2.st_index) -- GitLab