diff --git a/atp-indexer/ponder.config.ts b/atp-indexer/ponder.config.ts index 93e8880f8..fdd18ad3c 100644 --- a/atp-indexer/ponder.config.ts +++ b/atp-indexer/ponder.config.ts @@ -86,24 +86,33 @@ export default createConfig({ }, /** - * ATP Factory - MATP contract - * Issues milestone-based ATPs (MATPs) + * ATP Factory - MATP contract (milestone-based ATPs). + * + * Falls back to the zero address when `ATP_FACTORY_MATP_ADDRESS` + * isn't set (testnet doesn't deploy this factory). Ponder scans + * the zero address for events, finds none, the handler never fires + * — keeping the contract statically present in the config so the + * generated `EventNames` union includes it, which is required for + * `ponder.on("ATPFactoryMATP:…", …)` in `events/atp-factory/` to + * typecheck on every env regardless of which factories are deployed. */ ATPFactoryMATP: { chain: config.networkName, abi: ATP_ABI, - address: config.ATP_FACTORY_MATP_ADDRESS as `0x${string}`, + address: (config.ATP_FACTORY_MATP_ADDRESS + ?? "0x0000000000000000000000000000000000000000") as `0x${string}`, startBlock: FACTORY_START_BLOCKS.matp, }, /** - * ATP Factory - LATP contract - * Issues linear vesting ATPs (LATPs) and MATPs + * ATP Factory - LATP contract (linear-vesting ATPs). + * Same zero-address fallback pattern as MATP above. */ ATPFactoryLATP: { chain: config.networkName, abi: ATP_ABI, - address: config.ATP_FACTORY_LATP_ADDRESS as `0x${string}`, + address: (config.ATP_FACTORY_LATP_ADDRESS + ?? "0x0000000000000000000000000000000000000000") as `0x${string}`, startBlock: FACTORY_START_BLOCKS.latp, }, @@ -156,8 +165,11 @@ export default createConfig({ /** * Dynamic ATP Contracts - * Created by factory events, tracks operator updates - * Uses factory pattern to only index ATPs created by all 4 factories + * Created by factory events, tracks operator updates. + * Unset MATP / LATP factory env vars resolve to the zero address; + * `eth_getLogs(zeroAddress)` returns empty, so the factory pattern + * just sees no `ATPCreated` events from those slots on envs where + * those factories aren't deployed. */ ATP: { chain: config.networkName, @@ -166,8 +178,10 @@ export default createConfig({ address: [ config.ATP_FACTORY_ADDRESS as `0x${string}`, config.ATP_FACTORY_AUCTION_ADDRESS as `0x${string}`, - config.ATP_FACTORY_MATP_ADDRESS as `0x${string}`, - config.ATP_FACTORY_LATP_ADDRESS as `0x${string}`, + (config.ATP_FACTORY_MATP_ADDRESS + ?? "0x0000000000000000000000000000000000000000") as `0x${string}`, + (config.ATP_FACTORY_LATP_ADDRESS + ?? "0x0000000000000000000000000000000000000000") as `0x${string}`, ], event: ATPCreatedEvent, parameter: "atp", diff --git a/atp-indexer/src/config/index.ts b/atp-indexer/src/config/index.ts index 5a28ee5ab..5fc333e7e 100644 --- a/atp-indexer/src/config/index.ts +++ b/atp-indexer/src/config/index.ts @@ -48,8 +48,12 @@ const configSchema = z.object({ // Contract addresses ATP_FACTORY_ADDRESS: z.string().regex(/^0x[a-fA-F0-9]{40}$/, 'Invalid Ethereum address format'), ATP_FACTORY_AUCTION_ADDRESS: z.string().regex(/^0x[a-fA-F0-9]{40}$/, 'Invalid Ethereum address format'), - ATP_FACTORY_MATP_ADDRESS: z.string().regex(/^0x[a-fA-F0-9]{40}$/, 'Invalid Ethereum address format'), - ATP_FACTORY_LATP_ADDRESS: z.string().regex(/^0x[a-fA-F0-9]{40}$/, 'Invalid Ethereum address format'), + // MATP/LATP factories aren't deployed in every environment (e.g., + // testnet has only genesis + auction). Treat them as optional and + // gate their contract registration + handlers in ponder.config.ts / + // events/atp-factory so the indexer boots cleanly on lighter envs. + ATP_FACTORY_MATP_ADDRESS: z.string().regex(/^0x[a-fA-F0-9]{40}$/, 'Invalid Ethereum address format').optional(), + ATP_FACTORY_LATP_ADDRESS: z.string().regex(/^0x[a-fA-F0-9]{40}$/, 'Invalid Ethereum address format').optional(), STAKING_REGISTRY_ADDRESS: z.string().regex(/^0x[a-fA-F0-9]{40}$/, 'Invalid Ethereum address format'), REGISTRY_ADDRESS: z.string().regex(/^0x[a-fA-F0-9]{40}$/, 'Invalid Ethereum address format'), diff --git a/atp-indexer/src/events/atp-factory/atp-created.ts b/atp-indexer/src/events/atp-factory/atp-created.ts index c08a52678..e929bd73e 100644 --- a/atp-indexer/src/events/atp-factory/atp-created.ts +++ b/atp-indexer/src/events/atp-factory/atp-created.ts @@ -90,6 +90,11 @@ ponder.on("ATPFactoryAuction:ATPCreated", async (params) => { await handleATPCreated(params, "auction"); }); +// MATP / LATP handlers stay registered on every env. When their env +// vars aren't set, ponder.config.ts falls those contracts back to the +// zero address — `eth_getLogs(0x0)` returns no events, so these +// handlers simply never fire on envs (e.g., testnet) where the +// factories aren't deployed. ponder.on("ATPFactoryMATP:ATPCreated", async (params) => { await handleATPCreated(params, "matp"); });