nautechsystems/nautilus_trader · critical
Router {} reports an unexpected wrapped native contract
Error message
Router {} reports an unexpected wrapped native contract What it means
Raised during pre-trade router verification when the router's WETH9() call returns a wrapped-native token address different from plan.weth. This ensures the router's wrapped native token matches the plan before quoting or swapping. A mismatch means the plan's wrapped-native assumption is wrong for this deployment.
Source
Thrown at crates/adapters/blockchain/src/execution/client.rs:5066
let weth_call = UniswapV3RouterState::WETH9Call.abi_encode();
let router_weth = required_verification(
executor
.verification
.verify_decoded_call(
None,
&plan.router,
U256::ZERO,
&weth_call,
block,
|result| {
UniswapV3RouterState::WETH9Call::abi_decode_returns(result).map_err(Into::into)
},
)
.await,
"swap router wrapped native",
)?;
anyhow::ensure!(
router_weth.value == plan.weth,
"Router {} reports an unexpected wrapped native contract",
plan.router,
);
decisions.push(verification_decision(
&router_weth,
Some(block),
Some(block),
));
let pool_call = UniswapV3Factory::getPoolCall {
tokenA: plan.token_in,
tokenB: plan.token_out,
fee: plan.fee,
}
.abi_encode();
let registered_pool = required_verification(
executorView on GitHub (pinned to 18893faf8b)
Solutions
- Rebuild the execution plan so plan.weth matches the router's current WETH9() return value
- Verify plan.weth is the wrapped-native contract for the target chain (e.g. WETH on mainnet, WETH on Arbitrum)
- Check the plan was generated for the same chain ID the trade is executing on
- Use canonical per-chain wrapped-native addresses from the deployment manifest
Example fix
// before plan.weth = "0x4200000000000000000000000000000000000006"; // base WETH // after plan.weth = "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"; // mainnet WETH matching router.WETH9()
Defensive patterns
Strategy: validation
Validate before calling
async fn check_router_weth(router: Address, plan_weth: Address) -> Result<(), String> {
let actual: Address = call_router_weth9(router).await?;
if actual != plan_weth { return Err(format!("router {router} weth mismatch: {actual} != {plan_weth}")); }
Ok(())
} Type guard
fn weth_matches(plan_weth: Address, actual: Address) -> bool { plan_weth == actual } Prevention
- Use canonical wrapped-native addresses per chain, keyed by chain ID
- Regenerate plans on network change instead of reusing them
- Cross-check plan.weth against chainlist/official token registries
- Log chain ID alongside plan metadata to catch cross-chain replays
When it happens
Trigger: Executing a swap plan where on-chain router.WETH9() != plan.weth, typically when the plan references a WETH/WBNB/WMATIC address from a different chain or deployment.
Common situations: Plan built on Ethereum mainnet and replayed on an L2 or sidechain with different wrapped-native addresses, weth address typo'd in the plan config, or stale plan after network migration.
Understand the failure class
Background: "invalid response format", "malformed payload", "missing data field": when an API returns 200 but the response shape is wrong — this error's family across 23 libraries.
Related errors
- Router {} reports an unexpected factory
- Factory resolves an unexpected pool for the swap token pair
- Finalized transaction {} emitted {} Swap logs; expected exac
- Missing tickLower in topic2 when parsing burn event
- Missing tickUpper in topic3 when parsing burn event
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/876fa7d5a1696104.
Report an issue: GitHub.