PR: #51 (feat/23-testnet-config)
File: crates/charon-core/src/config.rs (Config, Config::load)
Refs #51
Problem
The PR adds #[serde(default)] to both flashloan: HashMap<String, FlashLoanConfig> and liquidator: HashMap<String, LiquidatorConfig> so each defaults to an empty map when absent. No Config::validate() method exists to reject the half-wired state where exactly one of the two maps is non-empty.
A mainnet operator who accidentally omits [liquidator.bnb] (e.g. after a key rotation or migration) gets:
- Startup succeeds (flashloan non-empty, liquidator empty).
- Read-only gate: condition may treat this as partially-wired and silently skip all liquidations.
- No error, no warning, no log distinguishing accidental omission from intentional read-only.
The reverse (liquidator present, flashloan absent) is equally dangerous: Aave pool is known but no flash-loan route exists; liquidation attempts will panic or revert with no advance diagnostic.
Impact
Silent loss of all liquidation revenue on mainnet if either section is accidentally omitted. No operator signal of the misconfiguration. Indistinguishable from intentional read-only.
Fix
Add Config::validate(&self) -> Result<(), ConfigError> called at the end of Config::load(). Validation rule: for each chain, either both a flashloan entry and a liquidator entry must be present, or both must be absent. A partial combination must return a typed error:
for (name, fl) in &self.flashloan {
if !self.liquidator.values().any(|l| l.chain == fl.chain) {
return Err(ConfigError::HalfWired { chain: fl.chain.clone() });
}
}
for (name, liq) in &self.liquidator {
if !self.flashloan.values().any(|f| f.chain == liq.chain) {
return Err(ConfigError::HalfWired { chain: liq.chain.clone() });
}
}
Requires ConfigError typed enum (see related issue on anyhow-vs-thiserror in config.rs).
PR: #51 (feat/23-testnet-config)
File: crates/charon-core/src/config.rs (Config, Config::load)
Refs #51
Problem
The PR adds
#[serde(default)]to bothflashloan: HashMap<String, FlashLoanConfig>andliquidator: HashMap<String, LiquidatorConfig>so each defaults to an empty map when absent. NoConfig::validate()method exists to reject the half-wired state where exactly one of the two maps is non-empty.A mainnet operator who accidentally omits
[liquidator.bnb](e.g. after a key rotation or migration) gets:The reverse (liquidator present, flashloan absent) is equally dangerous: Aave pool is known but no flash-loan route exists; liquidation attempts will panic or revert with no advance diagnostic.
Impact
Silent loss of all liquidation revenue on mainnet if either section is accidentally omitted. No operator signal of the misconfiguration. Indistinguishable from intentional read-only.
Fix
Add
Config::validate(&self) -> Result<(), ConfigError>called at the end ofConfig::load(). Validation rule: for each chain, either both aflashloanentry and aliquidatorentry must be present, or both must be absent. A partial combination must return a typed error:Requires
ConfigErrortyped enum (see related issue on anyhow-vs-thiserror in config.rs).