Smart Contract Testnet Testing
Local tests on Foundry anvil or Hardhat node pass — the team considers the contract ready for mainnet. But testnets regularly catch what local environments miss: ABI correctness when interacting through real wallets, behavior of third-party oracles and protocols, transaction delays, edge cases with gas estimation.
Selecting a Testnet
For Ethereum-compatible chains, current testnets in 2024-2025:
| Testnet | Chain | Features |
|---|---|---|
| Sepolia | Ethereum L1 | Main ETH testnet, PoS consensus |
| Holesky | Ethereum L1 | Larger validator set, better for staking |
| Amoy | Polygon | Replaced Mumbai |
| Arbitrum Sepolia | Arbitrum L2 | ArbOS, L1→L2 messaging |
| Optimism Sepolia | Optimism L2 | OP Stack, fault proofs |
| Base Sepolia | Base L2 | OP Stack, Coinbase |
| BSC Testnet | BNB Chain | Parallel to mainnet |
Goerli is deprecated. Don't deploy anything new there.
What to Check on Testnet
Verification via Etherscan. Deployment without verification is a red flag for users and auditors. Use forge verify-contract or hardhat verify. Important: compiler version and optimization settings in the config must exactly match those at deployment. One setting difference — won't verify.
Interaction via Metamask/Safe. Function signatures, readable parameter names in Etherscan UI, correct events in Transaction logs. Verify ABI is published and frontend correctly decodes responses.
Gas estimation. Real gas on testnet vs local. Foundry gas_price in anvil defaults to 1 wei — testnets have floating base fee. A test with vm.txGasPrice() doesn't always reflect reality.
Integration with protocols. If the contract uses Chainlink Price Feeds, Uniswap, Aave — deploy their addresses on testnet or use official testnet deployments. Chainlink provides Price Feeds on Sepolia. Uniswap V3 is deployed on Sepolia. Aave V3 — on Sepolia.
Testing Process
Testnet testing phase begins after passing unit and fuzz tests in Foundry. Run deployment script (forge script) with --rpc-url sepolia --broadcast. After deployment check:
- Constructor initialized variables correctly (read via cast call)
- Access restrictions work (attempt to call owner-functions from another address)
- Happy path of main functions
- Edge cases: zero amounts, max uint256 values
- Events are emitted and decoded correctly
For contracts with upgradeability — test the full upgrade cycle on testnet before mainnet.
Deployment Scripts and cast
cast from the Foundry package — the main tool for manual interaction:
# Read variable
cast call $CONTRACT "owner()" --rpc-url sepolia
# Send transaction
cast send $CONTRACT "setLimit(uint256)" 1000000 \
--rpc-url sepolia --private-key $PRIVATE_KEY
# Decode transaction calldata
cast 4byte-decode 0xa9059cbb...
For complex scenarios — write a Forge Script with vm.broadcast() instead of sequential cast calls.
Timelines
Typical testnet testing of a new medium-sized contract: 1-3 days. Includes deployment, verification, manual scenario testing, and fixing found issues. For contracts with many integrations — up to a week.
Cost depends on contract complexity and the number of test scenarios.







