use std::env; use std::fs::File; use std::io::Write; use pathfinder2::graph; use pathfinder2::io; use pathfinder2::types::Address; use pathfinder2::types::U256; const HUB_ADDRESS: &str = "0x29b9a7fBb8995b2423a71cC17cf9810798F6C543"; const TRANSFER_THROUGH_SIG: &str = "transferThrough(address[],address[],address[],uint256[])"; const RPC_URL: &str = "https://rpc.gnosischain.com"; fn main() { let (dotfile, mut args) = if env::args().len() >= 2 && env::args().nth_back(1).unwrap() == "--dot" { ( Some(env::args().last().unwrap()), env::args().rev().skip(2).rev().collect::>(), ) } else { (None, env::args().collect::>()) }; let csv = if args.get(1) == Some(&"--csv".to_string()) { args = [vec![args[0].clone()], args[2..].to_vec()].concat(); true } else { false }; if args.len() < 4 { println!("Usage: cli [--csv] [--dot ]"); println!("Usage: cli [--csv] [--dot ]"); println!( "Usage: cli [--csv] [--dot ]" ); println!("Option --csv reads edges.dat in csv format instead of binary."); return; } let mut max_hops = None; let mut max_flow = U256::MAX; let (from_str, to_str, edges_file) = (&args[1], &args[2], &args[3]); if args.len() >= 5 { max_hops = Some( args[4] .parse() .unwrap_or_else(|_| panic!("Expected number of hops, but got: {}", args[4])), ); if args.len() >= 6 { max_flow = args[5].as_str().into(); } } println!("Computing flow {from_str} -> {to_str} using {edges_file}"); let edges = (if csv { io::read_edges_csv(edges_file) } else { io::read_edges_binary(edges_file) }) .unwrap_or_else(|_| panic!("Error loading edges from file \"{edges_file}\".")); println!("Read {} edges", edges.len()); let (flow, transfers) = graph::compute_flow( &Address::from(from_str.as_str()), &Address::from(to_str.as_str()), &edges, max_flow, max_hops, ); println!("Found flow: {flow}"); //println!("{:?}", transfers); let result = json::object! { maxFlowValue: flow.to_decimal(), transferSteps: transfers.iter().enumerate().map(|(i, e)| { json::object!{ from: e.from.to_string(), to: e.to.to_string(), token: e.token.to_string(), value: e.capacity.to_decimal(), step: i, } }).collect::>() }; println!("{result}"); // let token_owners = transfers // .iter() // .map(|e| e.token.to_string()) // .collect::>() // .join(","); // let froms = transfers // .iter() // .map(|e| e.from.to_string()) // .collect::>() // .join(","); // let tos = transfers // .iter() // .map(|e| e.to.to_string()) // .collect::>() // .join(","); // let amounts = transfers // .iter() // .map(|e| e.capacity.to_decimal()) // .collect::>() // .join(","); //println!("To check, run the following command (requires foundry):"); //println!("cast call '{HUB_ADDRESS}' '{TRANSFER_THROUGH_SIG}' '[{token_owners}]' '[{froms}]' '[{tos}]' '[{amounts}]' --rpc-url {RPC_URL} --from {}", &transfers[0].from.to_string()); if let Some(dotfile) = dotfile { File::create(&dotfile) .unwrap() .write_all(graph::transfers_to_dot(&transfers).as_bytes()) .unwrap(); println!("Wrote dotfile {dotfile}."); } }