Contracts: add TimeArenaFork.t.sol (TimeArenaForkTest) for optional RPC fork smoke

Summary

Add contracts/test/TimeArenaFork.t.sol with contract TimeArenaForkTest so optional MegaETH/public-RPC fork smoke matches the runbook in docs/testing/contract-fork-smoke.md.

Context: #274 (closed) updated docs to reference TimeArenaForkTest, but the test file was never added when TimeCurveFork.t.sol was removed (#241 (closed)#245 (closed)). The contract-fork-smoke workflow still runs --match-contract TimeCurveForkTest and fails if dispatched today.


Current state

Artifact Status
docs/testing/contract-fork-smoke.md Documents TimeArenaForkTest
docs/testing/ci.md Documents TimeArenaForkTest
.github/workflows/contract-fork-smoke.yml Still --match-contract TimeCurveForkTest
contracts/README.md Still cites TimeCurveFork.t.sol
contracts/.env.example Still cites TimeCurveForkTest
contracts/test/TimeArenaFork.t.sol Missing
Historical pattern 451f14aTimeCurveFork.t.sol no-oped when FORK_URL unset

Existing fork test DoubAirdropMegaethFork.t.sol is a gas probe (requires --ffi, hardcoded mainnet addresses) — not a suitable substitute for CI connectivity smoke.


1. contracts/test/TimeArenaFork.t.sol

Mirror retired TimeCurveFork.t.sol pattern:

contract TimeArenaForkTest is Test {
    function test_fork_smoke_chainIdAndBlock() public {
        string memory url = vm.envOr("FORK_URL", string(""));
        if (bytes(url).length == 0) return;
        vm.createSelectFork(url);
        assertGt(block.chainid, 0);
        assertGt(block.number, 0);
    }
}

Optional second test (when registry address non-zero): read TimeArena head state at address from env TIME_ARENA_FORK_ADDRESS or published registry JSON — e.g. paused(), lastBuyDeadline(), proxy bytecode extcodesize > 0. Skip (return early) when address unset or zero placeholder (mainnet registry currently has TimeArena: 0x000…000 until #259 (closed) deploy).

2. Wire CI + docs

3. Policy (unchanged)

  • Default unit-tests workflow: FORK_URL unset → fork test no-ops → deterministic PR gate.
  • contract-fork-smoke: workflow_dispatch only; requires FORK_URL input or secret.

Acceptance criteria

  • FOUNDRY_PROFILE=ci forge test passes with FORK_URL unset (fork test no-ops).
  • export FORK_URL=<megaeth-testnet-rpc> && FOUNDRY_PROFILE=ci forge test --match-contract TimeArenaForkTest -vv passes locally.
  • contract-fork-smoke workflow succeeds when dispatched with valid RPC.
  • No references to TimeCurveForkTest / TimeCurveFork.t.sol in workflow, contracts README, or .env.example.
  • docs/testing/contract-fork-smoke.md link target exists.

Test plan

Step Expected
CI unit job (no FORK_URL) Green; TimeArenaForkTest skipped/no-op
Local with testnet RPC Fork selects chain; block assertions pass
Dispatch contract-fork-smoke Matches new contract name
After mainnet Arena deploy Optional read test against live TimeArena proxy