Skip to content

🟠 One VITE_FACTORY_CONTRACT_ID shared across the network switcher β€” mainnet/testnet toggle points both networks at the same contractΒ #1016

Description

@Ejirowebfi

Severity: High Β· Area: Frontend (frontend/src/config/env.ts, frontend/src/config/stellar.ts, frontend/src/context/NetworkContext.tsx)

Description

The app ships a runtime network switcher. NetworkProvider persists the selected network in localStorage under stellarforge_network (NetworkContext.tsx:8, :49-52) and NetworkSwitcher.tsx exposes it in the UI. But the contract identity is not per-network:

// config/env.ts:5-6
factoryContractId: import.meta.env.VITE_FACTORY_CONTRACT_ID ?? '',
tokenWasmHash: import.meta.env.VITE_TOKEN_WASM_HASH ?? '',

// config/stellar.ts:43-48 β€” one contract id, spread alongside every network's config
export const STELLAR_CONFIG = {
  network: resolvedNetwork,
  factoryContractId: ENV.factoryContractId,
  tokenWasmHash: ENV.tokenWasmHash,
  ...NETWORK_CONFIGS,
}

NETWORK_CONFIGS (stellar.ts:12-27) correctly varies passphrase, Horizon URL, and RPC URL per network. The factory contract ID and token WASM hash do not vary at all. Switching a testnet-configured build to mainnet keeps sending the testnet contract ID to mainnet RPC. The benign outcome is a confusing "contract not found". The dangerous outcome is that an unrelated contract happens to occupy that ID on mainnet, and the user is prompted to sign a real-XLM transaction against it β€” the switcher gives no indication that anything is wrong, because from the app's point of view nothing is.

Two compounding problems in the same area:

  • Mainnet is broken at the transport layer regardless. sorobanRpcUrl: 'https://soroban-mainnet.stellar.org' (stellar.ts:21) is hardcoded, and SDF operates no public mainnet Soroban RPC at that address. Mainnet mode cannot work as shipped even with a correct contract ID.
  • Caches are keyed on the shared ID. useTokens.ts:44 and :54 build cache keys as ${network}:${factoryContractId}:…. Including the network keeps entries from colliding outright, but the ID component is constant across networks, so the key carries no information about which contract the cached rows came from.

Tasks

  • Introduce per-network configuration (VITE_FACTORY_CONTRACT_ID_TESTNET / _MAINNET, and the same for the WASM hash), resolved through NETWORK_CONFIGS so a network and its contract identity are always read together and cannot be mismatched by construction.
  • Make the network switcher refuse to select a network with no configured contract ID, showing which env var is missing, instead of switching into a silently broken state.
  • Make the mainnet Soroban RPC URL a required explicit env var (VITE_SOROBAN_RPC_URL_MAINNET) with provider guidance in the docs, and surface an unreachable endpoint rather than failing silently.
  • Key the service-worker cache and useTokens cache on the resolved per-network contract ID.
  • Update .env.example, vercel.json env documentation, the README, and the Vercel deploy-button parameters.
  • Tests: switching network resolves that network's contract ID; an unconfigured network cannot be selected; the misconfiguration screen names the exact missing variable.

Acceptance Criteria

  • Each network resolves its own factory contract ID, token WASM hash, and RPC endpoint, proven by tests.
  • It is not possible, via the switcher, to sign a transaction on one network against another network's (or a missing) contract ID.
  • Mainnet mode has an explicitly configured, documented RPC endpoint β€” no hardcoded non-existent default.

Re-verified on 2026-08-19 during the 30-issue codebase audit tracked in ISSUES.md.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    GrantFox OSSIssue tracked in GrantFox OSSMaybe RewardedIssue may be eligible for a GrantFox rewardOfficial Campaign | FWC26Campaign: Official Campaign | FWC26Third CampaignCampaign: Third CampaignauditFrom the ISSUES.md codebase auditfrontendseverity: highBroken core feature or exploitable abuse vector

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions