AntFleet

Anatomy · 57e5c9ae-0

Watcher always uses testnet chain config regardless of CONFIG.chainId

highbugclosed in 3c10efc
repo bf0d040b·PR #3·reviewed 1 month ago·closed 1 month ago

The vulnerable code

agent/src/watcher.ts:1-22

1import { createPublicClient, http } from 'viem';
2import { xLayerTestnetChain } from './scorer';
3import { CONFIG, loadOperationalConfig } from './config';
4import { scoreStrategies } from './scorer';
5import { checkPromotions, tickActiveAge } from './promoter';
6import { consolidate } from './consolidator';
7import { runOrchestrator } from './orchestrator';
8import { runApprover } from './approver';
9import { syncFromBrain } from './store';
10import { runDeployer } from './deployer';
11
12export async function startWatcher() {
13 const isMainnet = CONFIG.chainId === 196;
14 console.log(`[Watcher] Connecting to X Layer ${isMainnet ? 'mainnet' : 'testnet'} at ${CONFIG.rpcUrl}...`);
15
16 const publicClient = createPublicClient({
17 chain: xLayerTestnetChain,
18 transport: http(),
19 });
20
21 let lastBlock = 0n;
22 try {

The reasoning

Opus

Watcher always uses testnet chain config regardless of CONFIG.chainId

highbughigh
  • agent/src/watcher.ts:1-22
startWatcher detects mainnet via CONFIG.chainId === 196 and logs accordingly, but unconditionally constructs the viem PublicClient with xLayerTestnetChain. The transport uses http() without an explicit URL, so it falls back to chain.rpcUrls.default. On mainnet this means the watcher polls the testnet chain rather than the configured CONFIG.rpcUrl/mainnet, causing the agent's epoch loop, scoring, and promotions to read state from the wrong chain. README claims the agent runs against the mainnet vault/hook addresses, but this code path can never reach mainnet block state.

Recommendation

Pick chain (and transport URL) based on CONFIG.chainId: e.g. chain: isMainnet ? xLayerMainnetChain : xLayerTestnetChain, and pass transport: http(CONFIG.rpcUrl). Add a regression test that verifies the client points at the configured RPC.

GPT-5

Watcher always uses testnet chain and ignores configured RPC URL, breaking mainnet operation

mediumbughigh
  • agent/src/watcher.ts:9-20
When running on mainnet, the client is still instantiated with the testnet chain and without the configured RPC URL, which can cause chain-id mismatches and connection failures. The code logs mainnet vs testnet, but the instantiated client doesn’t match that branch.

Recommendation

Instantiate viem with the correct chain per CONFIG.chainId and pass http(CONFIG.rpcUrl). For example: chain: isMainnet ? xLayerMainnetChain : xLayerTestnetChain, transport: http(CONFIG.rpcUrl).

The agreement

Both frontier models flagged this within the same line range. AntFleet's unanimous gate fired — the finding posted on the PR. Closed in 3c10efc.

The fix

1import { createPublicClient, http } from 'viem';
2import { xLayerTestnetChain } from './scorer';
3import { CONFIG, loadOperationalConfig } from './config';
4import { scoreStrategies } from './scorer';
5import { checkPromotions, tickActiveAge } from './promoter';
6import { consolidate } from './consolidator';
7import { runOrchestrator } from './orchestrator';
8import { runApprover } from './approver';
9import { syncFromBrain } from './store';
10import { runDeployer } from './deployer';
11
12export async function startWatcher() {
13 const isMainnet = CONFIG.chainId === 196;
14 console.log(`[Watcher] Connecting to X Layer ${isMainnet ? 'mainnet' : 'testnet'} at ${CONFIG.rpcUrl}...`);
15
16 const publicClient = createPublicClient({
17 chain: xLayerTestnetChain,
18 transport: http(),
19 });
20
21 let lastBlock = 0n;
22 try {

Closure

Closed 1 month ago

SHA: 3c10efc6038bc5ab182e8b192224745b99bcf729

View closure receipt on GitHub →

Tweet thread template

tweet 1 of 8149 / 280

Two frontier models reviewed PR #3 on bf0d040b. Both found this bug: high bug: Watcher always uses testnet chain config regardless of CONFIG.chainId

tweet 2 of 8111 / 280

The vulnerable code (agent/src/watcher.ts:1-22): (full snippet at https://www.antfleet.dev/anatomy/57e5c9ae-0)

tweet 3 of 8280 / 280

What Opus saw: "startWatcher detects mainnet via CONFIG.chainId === 196 and logs accordingly, but unconditionally constructs the viem PublicClient with xLayerTestnetChain. The transport uses http() without an explicit URL, so it falls back to chain.rpcUrls.default. On mainnet t…

tweet 4 of 8280 / 280

What GPT-5 saw: "When running on mainnet, the client is still instantiated with the testnet chain and without the configured RPC URL, which can cause chain-id mismatches and connection failures. The code logs mainnet vs testnet, but the instantiated client doesn’t match that br…

tweet 5 of 897 / 280

Both flagged the same line range. AntFleet's unanimous gate fired — the finding posted on the PR.

tweet 6 of 893 / 280

The fix landed in commit 3c10efc: (view diff at https://www.antfleet.dev/anatomy/57e5c9ae-0)

tweet 7 of 881 / 280

AntFleet reviews every PR with two frontier models. Only unanimous findings post.

tweet 8 of 877 / 280

Full anatomy + reasoning + diffs: https://www.antfleet.dev/anatomy/57e5c9ae-0

Paste into X composer one tweet at a time. X has no multi-tweet intent API.