The Uniswap-vs-PancakeSwap gotcha
contracts/lib/v2-core and v2-periphery are vendored as git submodules, and the READMEs in this repo call them “Uniswap/Pancake V2.” They are, in fact, the genuine upstream Uniswap repositories (confirmed via their git remotes), not PancakeSwap forks.
Why that matters
UniswapV2Library.pairFor() helper that computes a pair's address offline, using a hardcoded init-code-hash. That hash is specific to Uniswap's factory bytecode. PancakeSwap's factory has different bytecode and therefore a different init-code-hash. Using Uniswap's helper against BSC would silently compute the wrong pair address.How FeeRouter avoids it entirely
Rather than importing Uniswap's periphery source (and its hardcoded hash) or trying to find/verify PancakeSwap's exact hash, FeeRouter doesn't compute pair addresses at all. It calls the real, already-deployed PancakeSwap router by address and asks it for everything:
router.factory()androuter.WETH()are read once in the constructor, directly from the live router, never hardcoded twice.- Swaps go through
router.swapExactTokensForETHSupportingFeeOnTransferTokensandswapExactETHForTokensSupportingFeeOnTransferTokens, the fee-on-transfer-safe variants, since $SNOWBALL itself has a transfer fee. contracts/src/interfaces/IPancakeRouter02.solis a minimal, hand-written interface (ABI-identical to Uniswap's, since PancakeSwap V2 is a straight fork), just enough function signatures to call the real router, with zero dependency on the vendored periphery library.
The mainnet router address
Verified directly on BscScan before writing any code against it:
This is the default in Deploy.s.sol (overridable via the PANCAKE_ROUTER env var for testing against a different router if ever needed).