Notes / Audits and snapshots

Batch 12 (2026-09-18): PancakeSwap AMM (V2) Factory, plus a crossExposure correction on PancakeSwap V3 Factory it exposed

Audits and snapshots2026-09-18Chain: Robinhood ChainLinked: PancakeSwap AMM (V2) Factory, Spark Liquidity Layer (ALMProxy)

Source: data/scored_targets_2026-09-18-batch12.md in the repository, rendered here without changes. Source file

Scouted from DefiLlama's live protocol list (https://api.llama.fi/protocols, chains containing "Robinhood Chain", 176 protocols listed), sorted by Robinhood Chain TVL, skipping protocols already tracked directly or through a shared authority chain (Morpho Blue, LayerZero V2, Spark Savings, PancakeSwap AMM V3, Uniswap V2/V3/V4, Curve DEX, Pendle V2 were all already covered).

PancakeSwap AMM (V2) Factory -- 2/100#

FieldValue
Target0x02a84c1b3BBD7401a5f7fa98a384EBC70bB5749E
TVL~$1.9B across all chains DefiLlama attributes to "PancakeSwap AMM" (pancakeswap-amm); no per-chain Robinhood Chain breakdown published, but allPairsLength() = 156 real pairs confirms real usage, not a stub deployment
adminKeyScore5
multisigScore0
timelockScore0
oracleAuthorityScore100
crossExposureScore80
compositeScore2

Address source#

DefiLlama's official adapter DefiLlama-Adapters/projects/pancake-swap/index.js, robinhood: defaultExport where defaultExport = { tvl: getUniTVL({ factory: '0x02a84c1b3BBD7401a5f7fa98a384EBC70bB5749E', ... }) } -- the same deterministic factory address PancakeSwap reuses on Polygon zkEVM, Linea, op_bnb, Arbitrum, Base and Monad (the same "identical address on many chains" convention already used for score_pancakeswap_v3_factory(), which is a separate, already-tracked pancakeswap-amm-v3 protocol/factory, not this one). Confirmed live on two independent RPCs (rpc.mainnet.chain.robinhood.com, robinhood.drpc.org): 14,075 bytes of deployed code, allPairsLength() = 156, identical on both.

Authority chain#

V2-style factories have no owner()/proxy admin -- pairs are immutable, the only privileged surface is fee routing:

CallResult (both RPCs identical)
factory.feeTo()0xe4C0172679a2475e7De25bf1B933f774641388f4 (has code, not an EOA)
factory.feeToSetter()0xD099…48D4
feeToSetter: bare EOATrue (0 bytecode)
feeToSetter: getOwners()/getThreshold() (Safe check)both revert -- not a Safe
feeToSetter nonce (eth_getTransactionCount)20 -- real, actively-used key, not vanity/dormant

One bare EOA can call setFeeTo/setFeeToSetter with zero delay. Pools are not upgradeable and this address does not custody pooled funds, so the blast radius is fee-routing control, not a rug -- same shape and same scoring convention as score_curve_dex() (Curve DEX, 1/100).

crossExposureScore finding: this key also sits on PancakeSwap V3's Safe#

Before wiring this target into scripts/lib/signer_overlap.py, its feeToSetter EOA was grepped against every already-tracked group's known_eoa/safes lists in the whole repo -- no textual match. But compute_cross_exposure()'s own live Safe-owner re-derivation (the authoritative check, not the grep) found a real overlap once both groups existed together:

CheckResult (both RPCs identical)
PancakeSwap V3's governing Safe 0xfa20…6047 getOwners()6 owners: 0xD099…48D4, 0x27cb…9006, 0x08fc…56B2, 0x897f…059B, 0xb30d…B900, 0x7766…9a75
getThreshold()3
V2 feeToSetter is owner #1 of that SafeTrue

The same PancakeSwap-controlled key that alone sets V2's protocol fee is also one of 3-of-6 required signers on the Safe that owns V3's factory-owner wrapper. That is a real, live signer overlap between two tracked groups -- compute_cross_exposure() correctly drops both to crossExposureScore = 80 (max(0, 100 - 20*1), one shared group each).

PancakeSwap V3 Factory's published crossExposureScore was still 100 (last pushed batch 9, before the V2 factory existed as a tracked target to overlap with) -- corrected to 80 in the same transaction as this batch's new target. Its other four fields (adminKeyScore=65, multisigScore=60, timelockScore=0, compositeScore=44) are unchanged; only crossExposureScore moved, and only because a new group now exists to overlap with, not because anything about V3's own Safe changed.

Scoring#

Wired into scripts/lib/scorers.py::score_pancakeswap_v2_factory() (added to SIMPLE_SCORERS) and into scripts/lib/signer_overlap.py as group pancakeswap_v2. _composite(5, 0, 0) = floor(0.4*5 + 0.5) = floor(2.5) = 2. scripts/lib/tests (187 tests) and scripts/tests (5 tests) both pass unchanged after this edit (python3 -m unittest discover, no live RPC needed for those).

Pushed to the testnet oracle in one updateScores() call covering both addresses (the new V2 factory and the corrected V3 factory), restricted to exactly those two -- no other tracked target touched. A run-local helper outside the repo called this repo's own score_pancakeswap_v2_factory() / score_pancakeswap_v3_factory() / compute_cross_exposure() live immediately before sending, and refused to send if the re-derivation differed from the hand-verified values above. Transaction 0xeab5d37ea9f16645c2638da19cc36cea332925ba57fb195441b88ca13277ca28 (testnet block 121239380, status 1). trackedTargetsCount() went from 45 to 46; getScore() re-reads (5, 0, 0, 100, 80, 2) for the new V2 factory and (65, 60, 0, 100, 80, 44) for V3.

Credible leads found but not pursued this run (budget/time)#

Not forced to a round number -- these are disclosed as open, not silently skipped:

Not an audit, not investment advice. All notes · Back to the dashboard.