Custom Algorithm
The Algorithm trait
Algorithm traitImplement the trait
/// A naive algorithm that finds a direct pool between two tokens.
///
/// This iterates through all edges in the routing graph, finds one that
/// connects `token_in` to `token_out`, simulates the swap, and returns
/// the first successful result. It only supports single-hop (direct) routes.
struct DirectPoolAlgorithm {
timeout: Duration,
}
impl DirectPoolAlgorithm {
fn new(_config: fynd_core::AlgorithmConfig) -> Self {
Self { timeout: Duration::from_millis(100) }
}
}
impl Algorithm for DirectPoolAlgorithm {
// Reuse the built-in petgraph manager — it handles graph initialization and
// market event updates automatically. We just need a simple graph with no
// edge weights (unit `()` type).
type GraphType = StableDiGraph<()>;
type GraphManager = PetgraphStableDiGraphManager<()>;
fn name(&self) -> &str {
"direct_pool"
}
async fn find_best_route(
&self,
graph: &Self::GraphType,
market: SharedMarketDataRef,
_derived: Option<SharedDerivedDataRef>,
order: &Order,
) -> Result<RouteResult, AlgorithmError> {
let market = market.read().await;
let gas_price = market
.gas_price()
.ok_or(AlgorithmError::Other("gas price not available".to_string()))?
.effective_gas_price()
.clone();
// Walk every edge looking for one that goes token_in → token_out.
for edge_idx in graph.edge_indices() {
let Some((src_idx, dst_idx)) = graph.edge_endpoints(edge_idx) else {
continue;
};
let (src_addr, dst_addr) = (&graph[src_idx], &graph[dst_idx]);
if src_addr != order.token_in() || dst_addr != order.token_out() {
continue;
}
let component_id = &graph
.edge_weight(edge_idx)
.expect("edge exists")
.component_id;
// Look up component metadata and simulation state.
let Some(component) = market.get_component(component_id) else {
continue;
};
let Some(state) = market.get_simulation_state(component_id) else {
continue;
};
let Some(token_in) = market.get_token(order.token_in()) else {
continue;
};
let Some(token_out) = market.get_token(order.token_out()) else {
continue;
};
// Simulate the swap.
let result = match state.get_amount_out(order.amount().clone(), token_in, token_out) {
Ok(r) => r,
Err(_) => continue,
};
let swap = Swap::new(
component_id.clone(),
component.protocol_system.clone(),
token_in.address.clone(),
token_out.address.clone(),
order.amount().clone(),
result.amount.clone(),
result.gas,
component.clone(),
state.clone_box(),
);
let route = Route::new(vec![swap]);
let net_amount_out = BigInt::from(result.amount);
return Ok(RouteResult::new(route, net_amount_out, gas_price));
}
Err(AlgorithmError::Other(format!(
"no direct pool from {:?} to {:?}",
order.token_in(),
order.token_out()
)))
}
fn computation_requirements(&self) -> ComputationRequirements {
ComputationRequirements::default()
}
fn timeout(&self) -> Duration {
self.timeout
}
}Wire it up
Run the example
Prerequisites
Run
Last updated