Polymarket v2

QuantstampCode version: not recorded (see the report)

Quantstamp reviewed Polymarket (Polymarket v2) on 5 Oct 2026: 12 findings, 0 critical; 7 marked fixed, 5 acknowledged.

No open critical findings
Open the reportOpen Polymarket’s security record

Report hosted on certificate.quantstamp.com.

Trustblock does not review code.

Findings12

Statuses are the security firm’s own, from its report.

SeverityOpenAcknowledgedFixed
Critical000
High001
Medium001
Low055
Low`OptimisticOracleModule` Cannot Claim OOv2 Deferred PayoutsAcknowledged

When the OOv2 refunds on dispute to the `OptimisticOracleModule` and the direct ERC20 transfer reverts (for example the module address is blacklisted or the token is paused), `OptimisticOracleV2._transferOrDeferPayout()` falls back to recording the amount in `deferredPayouts[currency][module]` and emits `PayoutDeferred`. The only way to move those funds out is a call to `OOV2.claimDeferredPayout(currency, repaymentAddress)`, which requires `msg.sender` to equal the deferred recipient (the module itself). `OptimisticOracleModule` exposes no function that forwards a `claimDeferredPayout()` call to the OOv2, once a deferred payout is recorded against it, the funds remain trapped in the OOv2. Under normal operation with standard bond tokens the transfer to the module is expected to succeed, so the defer path is unlikely to be triggered.

Low`validateOrder()` Does Not Mirror All Order Checks, Producing Inconsistent Off-Chain ValidationAcknowledged

`validateOrder()` only validates order level state and signature data. It does not take execution specific context such as `fillAmount`, `fee`, `conditionId`, or maker side orders, so it cannot mirror the checks performed during `matchOrders()`. As a result, an order can pass `validateOrder()` but still revert during execution due to remaining capacity, fee bounds, token ID alignment, or other match specific checks.

LowDocumentation Mismatches Current Implementation DetailsAcknowledged

The documentation contains stale information compared to the actual audited implementation, likely because the code changed shortly before the audit happened without updating the docs. The verified misalignments are: 1. `src/oracle/modules/README.md` states that `onArbitrationTriggered()` reverts with `ArbitrationNotExpected` as a safety check. `OptimisticOracleModule` does not implement `onArbitrationTriggered()` and does not define `ArbitrationNotExpected`. If `OracleAggregator.disputeResult()` reaches arbitration for a request configured with `OptimisticOracleModule` as the arbitrator, the call is made through `IArbitratorModule(cfg.arbitratorModule).onArbitrationTriggered(...)`. Because the function is not implemented and there is no fallback handler, the call reverts with a generic error instead of the documented custom error. 2. `docs/position-ids.md` and `docs/position-manager.md` document `PositionManager.getPositionId(conditionId, outcomeIndex)`, but that function does not exist in `PositionManager`. 3. `docs/position-ids.md` references router callback helpers that do not exist in `Router`. 4. `docs/architecture.md` and `docs/position-manager.md` use stale PMCT and `collateralToken` naming instead of current `pUSD` and `COLLATERAL_TOKEN` naming. 5. `docs/exchange.md` documents stale order fields, EIP712 domain values, initializer, constructor, fee controls, pause controls, `matchOrders` argument order, and view names. 6. `docs/modules.md` describes an old oracle assignment model. Current modules use resolver and bridge roles. 7. `docs/modules.md` lists stale bridge and OracleModule functions, and an incomplete `convert` signature. 8. `docs/router.md` has stale merge behavior, AutoRedeemer API, and omits BridgeRouter refund forwarding. 9. `docs/oracle.md` has stale auth details, and has the wrong initializer signature. 10. `docs/auth.md` omits resolver role support, overstates `InitializableRoles`, and repeats stale Exchange and AutoRedeemer APIs. 11. `docs/bridge.md` documents module support by module id, stale message types, stale payload helpers, and incorrect duplicate result behavior. 12. `docs/migration.md` documents stale migration argument shapes, stale operator overloads, overbroad revert behavior, and internal helpers as public API.

LowSame-Block Re-Requests Can Revert Due To Duplicate UMA Request IDsAcknowledged

`OptimisticOracleModule` creates UMA requests using a request tuple that includes the module address, identifier, timestamp, and ancillary data. When `_reRequest()` is called after a dispute or through `forceRerequest()`, it reuses the same identifier and ancillary data, and relies on `block.timestamp` as the only value that changes the underlying UMA request ID. If two re-requests for the same condition are attempted in the same block with the same ancillary data, the second call attempts to create an already-existing UMA request. The underlying Optimistic Oracle rejects duplicate request IDs, causing the later request to revert. This creates a temporary griefing vector against admin `forceRerequest()` calls. A user can front-run an admin’s `forceRerequest()` by disputing the current price request. The dispute callback triggers `_reRequest()` first, creating a new request with the existing ancillary data and current block timestamp. If the admin transaction then executes in the same block with the same ancillary data, it reverts before applying the admin-selected bond token, bond amount, liveness, or zero-reward reset. The impact is temporary liveness degradation rather than permanent request blocking. The admin can retry in a later block, or use different ancillary data, but the failed force action may delay recovery and leave the condition on the automatically created re-request using the prior request parameters.

LowStandardize Access Control PatternsAcknowledged

The contracts currently use a mixture of access control abstractions, including `Auth`, `Roles`, `InitializeableRoles`, and direct inheritance from Solady’s `OwnableRoles`. This inconsistency makes it harder to determine which roles exist on each contract, what permissions they grant, and which administrative functions are actually relevant. In some cases, contracts expose role management functions for roles that are not used by that contract, increasing the administrative surface area without a corresponding need.

Contracts in scope

No deployed address is recorded for this audit.

The report names a repository or files. Open the report to see its scope.

Part of Polymarket’s security record

Polymarket
Audits7 audits by 2 firmsLast audit Oct 2026, by Quantstamp.
Critical findingsNo open critical findingsAcross the 7 reports, 2 critical findings were reported. Each is marked fixed by the firm that reported it.
Open Polymarket’s security record

Security firm

Quantstamp

236 projects reviewed · 332 reports · latest Oct 2026

See Quantstamp’s audits

Something wrong on this page? A missing report, a wrong date, a finding that was fixed since. Tell us and we check it against its source.

Tell us