Skip to content

[config] half-wired config (flashloan present + liquidator absent, or vice versa) passes Config::load silently — bot skips all liquidations with no diagnostic #243

@obchain

Description

@obchain

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).

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workinglayer:rustRust crates (core / scanner / protocols / executor / cli)priority:p0-blockerBlocks the critical pathstatus:readyScoped and ready to pick up

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions