Skip to content

Commit 4665387

Browse files
committed
fix: Complete oracle refactor and fix config parsing
1 parent 64b01da commit 4665387

14 files changed

Lines changed: 206 additions & 72 deletions

File tree

config/demo.toml

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,8 +82,21 @@ address = "0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0"
8282
[settlement.implementations.direct]
8383
order = "eip7683"
8484
network_ids = [31337, 31338]
85-
oracle_addresses = { 31337 = "0xDc64a140Aa3E981100a9becA4E685f962f0cF6C9", 31338 = "0xDc64a140Aa3E981100a9becA4E685f962f0cF6C9" }
8685
dispute_period_seconds = 1
86+
# Oracle selection strategy when multiple oracles are available (First, RoundRobin, Random)
87+
oracle_selection_strategy = "First"
88+
89+
# Oracle configuration with multiple oracle support
90+
[settlement.implementations.direct.oracles]
91+
# Input oracles (on origin chains)
92+
input = { 31337 = ["0xDc64a140Aa3E981100a9becA4E685f962f0cF6C9"], 31338 = ["0xDc64a140Aa3E981100a9becA4E685f962f0cF6C9"] }
93+
# Output oracles (on destination chains)
94+
output = { 31337 = ["0xDc64a140Aa3E981100a9becA4E685f962f0cF6C9"], 31338 = ["0xDc64a140Aa3E981100a9becA4E685f962f0cF6C9"] }
95+
96+
# Valid routes: from origin chain -> to destination chains
97+
[settlement.implementations.direct.routes]
98+
31337 = [31338] # Can go from chain 31337 to chain 31338
99+
31338 = [31337] # Can go from chain 31338 to chain 31337
87100

88101

89102
# ============================================================================

config/example.toml

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,8 +122,21 @@ address = "0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0"
122122
[settlement.implementations.direct]
123123
order = "eip7683"
124124
network_ids = [31337, 31338]
125-
oracle_addresses = { 31337 = "0xDc64a140Aa3E981100a9becA4E685f962f0cF6C9", 31338 = "0xDc64a140Aa3E981100a9becA4E685f962f0cF6C9" }
126125
dispute_period_seconds = 1
126+
# Oracle selection strategy when multiple oracles are available (First, RoundRobin, Random)
127+
oracle_selection_strategy = "First"
128+
129+
# Oracle configuration with multiple oracle support
130+
[settlement.implementations.direct.oracles]
131+
# Input oracles (on origin chains) - can specify multiple oracles per chain
132+
input = { 31337 = ["0xDc64a140Aa3E981100a9becA4E685f962f0cF6C9"], 31338 = ["0xDc64a140Aa3E981100a9becA4E685f962f0cF6C9"] }
133+
# Output oracles (on destination chains) - can specify multiple oracles per chain
134+
output = { 31337 = ["0xDc64a140Aa3E981100a9becA4E685f962f0cF6C9"], 31338 = ["0xDc64a140Aa3E981100a9becA4E685f962f0cF6C9"] }
135+
136+
# Valid routes: from origin chain -> to destination chains
137+
[settlement.implementations.direct.routes]
138+
31337 = [31338] # Can go from chain 31337 to chain 31338
139+
31338 = [31337] # Can go from chain 31338 to chain 31337
127140

128141
# ============================================================================
129142
# API SERVER

crates/solver-core/src/builder/mod.rs

Lines changed: 42 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ impl SolverBuilder {
7676
OF: Fn(
7777
&toml::Value,
7878
&solver_types::NetworksConfig,
79+
&solver_types::oracle::OracleRoutes,
7980
) -> Result<Box<dyn OrderInterface>, OrderError>,
8081
SEF: Fn(
8182
&toml::Value,
@@ -313,11 +314,50 @@ impl SolverBuilder {
313314

314315
let discovery = Arc::new(DiscoveryService::new(discovery_implementations));
315316

316-
// Create order implementations
317+
// Create settlement implementations first (needed for oracle routes)
318+
let mut settlement_impls = HashMap::new();
319+
for (name, config) in &self.config.settlement.implementations {
320+
if let Some(factory) = factories.settlement_factories.get(name) {
321+
match factory(config, &self.config.networks) {
322+
Ok(implementation) => {
323+
// Validation already happened in the factory
324+
settlement_impls.insert(name.clone(), implementation);
325+
tracing::info!(component = "settlement", implementation = %name, "Loaded");
326+
},
327+
Err(e) => {
328+
tracing::error!(
329+
component = "settlement",
330+
implementation = %name,
331+
error = %e,
332+
"Failed to create settlement implementation"
333+
);
334+
return Err(BuilderError::Config(format!(
335+
"Failed to create settlement implementation '{}': {}",
336+
name, e
337+
)));
338+
},
339+
}
340+
}
341+
}
342+
343+
if settlement_impls.is_empty() {
344+
tracing::warn!("No settlement implementations available - solver will not be able to monitor and claim settlements");
345+
}
346+
347+
let settlement = Arc::new(SettlementService::new(settlement_impls));
348+
349+
// Build oracle routes from settlement implementations
350+
let oracle_routes = settlement.build_oracle_routes();
351+
tracing::info!(
352+
oracle_routes = %oracle_routes.supported_routes.len(),
353+
"Built oracle routes from settlement implementations"
354+
);
355+
356+
// Create order implementations (now with oracle routes)
317357
let mut order_impls = HashMap::new();
318358
for (name, config) in &self.config.order.implementations {
319359
if let Some(factory) = factories.order_factories.get(name) {
320-
match factory(config, &self.config.networks) {
360+
match factory(config, &self.config.networks, &oracle_routes) {
321361
Ok(implementation) => {
322362
// Validation already happened in the factory
323363
order_impls.insert(name.clone(), implementation);
@@ -386,38 +426,6 @@ impl SolverBuilder {
386426

387427
let order = Arc::new(OrderService::new(order_impls, strategy));
388428

389-
// Create settlement implementations
390-
let mut settlement_impls = HashMap::new();
391-
for (name, config) in &self.config.settlement.implementations {
392-
if let Some(factory) = factories.settlement_factories.get(name) {
393-
match factory(config, &self.config.networks) {
394-
Ok(implementation) => {
395-
// Validation already happened in the factory
396-
settlement_impls.insert(name.clone(), implementation);
397-
tracing::info!(component = "settlement", implementation = %name, "Loaded");
398-
},
399-
Err(e) => {
400-
tracing::error!(
401-
component = "settlement",
402-
implementation = %name,
403-
error = %e,
404-
"Failed to create settlement implementation"
405-
);
406-
return Err(BuilderError::Config(format!(
407-
"Failed to create settlement implementation '{}': {}",
408-
name, e
409-
)));
410-
},
411-
}
412-
}
413-
}
414-
415-
if settlement_impls.is_empty() {
416-
tracing::warn!("No settlement implementations available - solver will not be able to monitor and claim settlements");
417-
}
418-
419-
let settlement = Arc::new(SettlementService::new(settlement_impls));
420-
421429
// Create and initialize the TokenManager
422430
let token_manager = Arc::new(crate::engine::token_manager::TokenManager::new(
423431
self.config.networks.clone(),

crates/solver-order/src/implementations/standards/_7683.rs

Lines changed: 87 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ use alloy_primitives::{Address as AlloyAddress, FixedBytes, U256};
99
use alloy_sol_types::{sol, SolCall, SolValue};
1010
use async_trait::async_trait;
1111
use solver_types::{
12-
Address, ConfigSchema, Eip7683OrderData, ExecutionParams, FillProof, Intent, NetworksConfig,
13-
Order, OrderStatus, Schema, Transaction,
12+
oracle::OracleRoutes, Address, ConfigSchema, Eip7683OrderData, ExecutionParams, FillProof,
13+
Intent, NetworksConfig, Order, OrderStatus, Schema, Transaction,
1414
};
1515

1616
// Solidity type definitions for EIP-7683 contract interactions.
@@ -82,9 +82,12 @@ sol! {
8282
/// # Fields
8383
///
8484
/// * `networks` - Networks configuration containing settler addresses for each chain
85+
/// * `oracle_routes` - Oracle routes for validation of input/output oracle compatibility
8586
pub struct Eip7683OrderImpl {
8687
/// Networks configuration for dynamic settler address lookups.
8788
networks: NetworksConfig,
89+
/// Oracle routes for validation of input/output oracle compatibility.
90+
oracle_routes: OracleRoutes,
8891
}
8992

9093
impl Eip7683OrderImpl {
@@ -93,15 +96,19 @@ impl Eip7683OrderImpl {
9396
/// # Arguments
9497
///
9598
/// * `networks` - Networks configuration with settler addresses
96-
pub fn new(networks: NetworksConfig) -> Result<Self, OrderError> {
99+
/// * `oracle_routes` - Oracle routes for validation
100+
pub fn new(networks: NetworksConfig, oracle_routes: OracleRoutes) -> Result<Self, OrderError> {
97101
// Validate that networks config has at least 2 networks
98102
if networks.len() < 2 {
99103
return Err(OrderError::ValidationFailed(
100104
"At least 2 networks must be configured".to_string(),
101105
));
102106
}
103107

104-
Ok(Self { networks })
108+
Ok(Self {
109+
networks,
110+
oracle_routes,
111+
})
105112
}
106113
}
107114

@@ -197,8 +204,81 @@ impl OrderInterface for Eip7683OrderImpl {
197204
return Err(OrderError::ValidationFailed("Order expired".to_string()));
198205
}
199206

207+
// Validate oracle routes
208+
let origin_chain = order_data.origin_chain_id.to::<u64>();
209+
let input_oracle = solver_types::utils::parse_address(&order_data.input_oracle)
210+
.map_err(|e| OrderError::ValidationFailed(format!("Invalid input oracle: {}", e)))?;
211+
212+
let input_info = solver_types::oracle::OracleInfo {
213+
chain_id: origin_chain,
214+
oracle: input_oracle,
215+
};
216+
217+
// Check if the input oracle is supported
218+
if !self
219+
.oracle_routes
220+
.supported_routes
221+
.contains_key(&input_info)
222+
{
223+
return Err(OrderError::ValidationFailed(format!(
224+
"Input oracle {} on chain {} is not supported",
225+
order_data.input_oracle, origin_chain
226+
)));
227+
}
228+
229+
// Get supported output oracles for this input oracle
230+
let supported_outputs = self
231+
.oracle_routes
232+
.supported_routes
233+
.get(&input_info)
234+
.ok_or_else(|| {
235+
OrderError::ValidationFailed(format!(
236+
"No routes configured for input oracle {} on chain {}",
237+
order_data.input_oracle, origin_chain
238+
))
239+
})?;
240+
241+
// Validate each output oracle
242+
for output in &order_data.outputs {
243+
let dest_chain = output.chain_id.to::<u64>();
244+
245+
// If output oracle is zero (0x00...), it means use any compatible oracle
246+
// Otherwise, validate the specific oracle
247+
if output.oracle != [0u8; 32] {
248+
// Parse the oracle address from bytes32 (take first 20 bytes)
249+
let output_oracle_bytes = &output.oracle[12..32]; // Last 20 bytes for address
250+
let output_oracle = Address(output_oracle_bytes.into());
251+
252+
let output_info = solver_types::oracle::OracleInfo {
253+
chain_id: dest_chain,
254+
oracle: output_oracle,
255+
};
256+
257+
// Check if this output oracle is in the supported list
258+
if !supported_outputs.contains(&output_info) {
259+
return Err(OrderError::ValidationFailed(format!(
260+
"Output oracle {:?} on chain {} is not compatible with input oracle {} on chain {}",
261+
output.oracle, dest_chain, order_data.input_oracle, origin_chain
262+
)));
263+
}
264+
} else {
265+
// Zero oracle means any oracle on the destination chain is acceptable
266+
// Just verify that there's at least one oracle for this destination
267+
let has_route_to_dest = supported_outputs
268+
.iter()
269+
.any(|info| info.chain_id == dest_chain);
270+
271+
if !has_route_to_dest {
272+
return Err(OrderError::ValidationFailed(format!(
273+
"No route available from chain {} to chain {}",
274+
origin_chain, dest_chain
275+
)));
276+
}
277+
}
278+
}
279+
200280
// Extract chain IDs
201-
let input_chain_ids = vec![order_data.origin_chain_id.to::<u64>()];
281+
let input_chain_ids = vec![origin_chain];
202282
let output_chain_ids = order_data
203283
.outputs
204284
.iter()
@@ -623,12 +703,13 @@ impl OrderInterface for Eip7683OrderImpl {
623703
pub fn create_order_impl(
624704
config: &toml::Value,
625705
networks: &NetworksConfig,
706+
oracle_routes: &solver_types::oracle::OracleRoutes,
626707
) -> Result<Box<dyn OrderInterface>, OrderError> {
627708
// Validate configuration first
628709
Eip7683OrderSchema::validate_config(config)
629710
.map_err(|e| OrderError::InvalidOrder(format!("Invalid configuration: {}", e)))?;
630711

631-
let order_impl = Eip7683OrderImpl::new(networks.clone())?;
712+
let order_impl = Eip7683OrderImpl::new(networks.clone(), oracle_routes.clone())?;
632713
Ok(Box::new(order_impl))
633714
}
634715

crates/solver-order/src/lib.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -145,8 +145,11 @@ pub trait ExecutionStrategy: Send + Sync {
145145
///
146146
/// This is the function signature that all order implementations must provide
147147
/// to create instances of their order interface.
148-
pub type OrderFactory =
149-
fn(&toml::Value, &NetworksConfig) -> Result<Box<dyn OrderInterface>, OrderError>;
148+
pub type OrderFactory = fn(
149+
&toml::Value,
150+
&NetworksConfig,
151+
&solver_types::oracle::OracleRoutes,
152+
) -> Result<Box<dyn OrderInterface>, OrderError>;
150153

151154
/// Type alias for strategy factory functions.
152155
///

crates/solver-service/src/apis/quote/generation.rs

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -177,12 +177,17 @@ impl QuoteGenerator {
177177
let (settlement, selected_oracle) = self
178178
.settlement_service
179179
.get_any_settlement_for_chain(chain_id)
180-
.ok_or_else(|| QuoteError::InvalidRequest(
181-
format!("No settlement available for chain {}", chain_id)
182-
))?;
180+
.ok_or_else(|| {
181+
QuoteError::InvalidRequest(format!(
182+
"No settlement available for chain {}",
183+
chain_id
184+
))
185+
})?;
183186

184187
match escrow_kind {
185-
EscrowKind::Permit2 => self.generate_permit2_order(request, config, settlement, selected_oracle),
188+
EscrowKind::Permit2 => {
189+
self.generate_permit2_order(request, config, settlement, selected_oracle)
190+
},
186191
EscrowKind::Erc3009 => self.generate_erc3009_order(request, config),
187192
}
188193
}

crates/solver-service/src/factory_registry.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,11 @@ pub type DeliveryFactory = fn(
2626
) -> Result<Box<dyn DeliveryInterface>, DeliveryError>;
2727
pub type DiscoveryFactory =
2828
fn(&toml::Value, &NetworksConfig) -> Result<Box<dyn DiscoveryInterface>, DiscoveryError>;
29-
pub type OrderFactory =
30-
fn(&toml::Value, &NetworksConfig) -> Result<Box<dyn OrderInterface>, OrderError>;
29+
pub type OrderFactory = fn(
30+
&toml::Value,
31+
&NetworksConfig,
32+
&solver_types::oracle::OracleRoutes,
33+
) -> Result<Box<dyn OrderInterface>, OrderError>;
3134
pub type SettlementFactory =
3235
fn(&toml::Value, &NetworksConfig) -> Result<Box<dyn SettlementInterface>, SettlementError>;
3336
pub type StrategyFactory = fn(&toml::Value) -> Result<Box<dyn ExecutionStrategy>, StrategyError>;

crates/solver-settlement/src/implementations/direct.rs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,7 @@
55
//! readiness checks using simple transaction receipt verification without
66
//! complex attestation mechanisms.
77
8-
use crate::{
9-
utils::parse_oracle_config, OracleConfig, SettlementError,
10-
SettlementInterface,
11-
};
8+
use crate::{utils::parse_oracle_config, OracleConfig, SettlementError, SettlementInterface};
129
use alloy_primitives::{hex, FixedBytes};
1310
use alloy_provider::{Provider, RootProvider};
1411
use alloy_rpc_types::BlockTransactionsKind;

0 commit comments

Comments
 (0)