Skip to content

feat(predict): add Polymarket CLOB v2 support cp-7.73.1#29076

Merged
matallui merged 16 commits into
mainfrom
predict/clob-v2-ready
Apr 21, 2026
Merged

feat(predict): add Polymarket CLOB v2 support cp-7.73.1#29076
matallui merged 16 commits into
mainfrom
predict/clob-v2-ready

Conversation

@matallui

@matallui matallui commented Apr 20, 2026

Copy link
Copy Markdown
Contributor

Description

This PR implements Polymarket CLOB v2 support for MetaMask Predict (7.74 target), introducing a clean coexistence architecture that keeps v1 working while v2 is toggled on via predictClobV2. During the temporary CLOB host migration window, a companion rollout flag (predictClobV2UseLegacyClobHost) can force v2 traffic onto the legacy host for internal RC testing without turning v2 on for everyone by default.

The implementation is designed with deletion in mind — after the v1/v2 coexistence window, v1 can be cleanly removed with minimal surgery. The work follows the "data-first protocol definition" architecture from the internal plan: a private protocol/ module owns all version-specific differences and is resolved once at the provider entry point. Lower-level helpers never read feature flags directly.

Key design decisions:

  • Primary remote flag: predictClobV2 (default false) enables the v2 protocol
  • Temporary rollout flag: predictClobV2UseLegacyClobHost forces v2 to use https://clob-v2.polymarket.com during the migration window
  • Canonical/default v2 host remains https://clob.polymarket.com
  • Host selection is resolved once into protocol.transport.clobBaseUrl; lower-level helpers never read raw flags
  • v2 API key caching is host-aware to avoid reusing credentials across host changes
  • v2 getBalance() always returns Safe USDC.e + Safe pUSD — no branching on upgrade state
  • Trade path is relayer-only, Permit2-only fee collection on pUSD, always runs preflight before submission
  • Canonical v2 allowance requirement set defined once; inspector + compiler are small pure modules
  • Deposit/withdraw use release-time code choices for preferred vs fallback variant — not runtime flags
  • Wrap: always wraps entire current Safe USDC.e balance (never MaxUint256) when maintenance tx is already being emitted
  • Unwrap: always unwraps exact deficit needed — no over-unwrapping

Commits

This PR is now structured as 12 focused commits for easier review:

1. feat(predict): add CLOB v2 feature flag plumbing

Wires the predictClobV2 boolean through the Predict feature-flag infrastructure:

  • Adds predictClobV2Enabled to PredictFeatureFlags interface
  • Adds selectPredictClobV2EnabledFlag selector
  • Extends resolvePredictFeatureFlags to resolve the new flag
  • Refactors resolveVersionGatedBooleanFlag helper to reduce duplication across the existing flag resolution logic
  • Adds selector and resolver tests

2. feat(predict): add CLOB v2 protocol and preflight foundation

Introduces the two new private modules that everything else builds on:

protocol/ — data-first protocol definitions:

  • definitions.ts: v1 and v2 PolymarketProtocolDefinition objects; protocol resolution; builder code env config (MM_PREDICT_BUILDER_CODE); deposit/withdraw execution mode types
  • orderCodec.ts: v2-aware order build, EIP-712 typed data, and relayer payload serialization
  • transport.ts: shared CLOB transport helpers parameterized by protocol endpoint; collapses v1/v2 endpoint differences

preflight/ — private readiness inspection and Safe plan construction:

  • v2AllowanceRequirements.ts: canonical declarative list of all 10 v2 allowance requirements (7 ERC-20, 3 ERC-1155)
  • inspectMissingRequirements.ts: reads on-chain state and returns the subset of requirements not yet satisfied
  • compileRequirementTransactions.ts: compiles missing requirements into SafeTransaction[]
  • core.ts: shared raw-fact readers, wrap/unwrap builders, and signed Safe execution helpers

All modules covered by unit tests.

3. feat(predict): add CLOB v2 buy and sell flow

Implements the full v2 trade path inside PolymarketProvider:

  • preflight/trade.ts: planTradePreflight + buildTradeAllowancesTx — inspects missing v2 allowances and Safe USDC.e balance; compiles optional maintenance tx (allowances-only / wrap-only / allowances+wrap / none)
  • PolymarketProvider: placeOrder now resolves the protocol once, runs preflight under v2, builds the optional allowancesTx, and uses the protocol's orderCodec for order construction, EIP-712 signing, and relayer payload
  • utils.ts: adds encodeWrapUsdceTransaction and encodeUnwrapTransaction helpers (+ tests)
  • v2 preview keeps feeRateBps = '0' until the upstream fee endpoint is confirmed

4. feat(predict): add CLOB v2 deposit flow

Adds the v2 deposit maintenance planner and wires it into PolymarketProvider.prepareDeposit:

  • preflight/deposit.ts: planDepositMaintenance + compileDepositMaintenanceTransactions — inspects missing v2 allowances and pre-existing Safe USDC.e balance (does not incorporate the just-entered deposit amount); emits optional maintenance tx
  • PolymarketProvider.prepareDeposit: under v2, resolves protocol, runs the maintenance planner, and attaches the optional maintenance tx to the deposit plan — the 3-step shape (optional deploy → transfer funding asset → optional maintenance tx) is preserved
  • Currently wired to the usdce-transfer fallback mode; flip depositMode in the protocol definition to switch to pUSD-native when that dependency lands

5. feat(predict): add CLOB v2 withdraw flow

Adds the v2 withdraw planner and wires both the prepareWithdraw / signWithdraw contract:

  • preflight/withdraw.ts: planWithdraw — reads missing v2 allowances and Safe USDC.e balance; computes the exact USDC.e deficit; compiles the final MultiSend (allowance repair → exact-deficit unwrap-to-Safe → USDC.e transfer to EOA)
  • PolymarketProvider.signWithdraw: under v2, parses the requested amount from the original template calldata, calls planWithdraw, and returns the user-requested amount (not any larger intermediate amount)
  • safe/utils.ts: adds parseTransactionCalldata helper to extract the withdraw amount from the stored template
  • Currently wired to the usdce-deficit-unwrap fallback mode; flip withdrawMode in the protocol definition to switch to pUSD-native

6. feat(predict): add CLOB v2 claim flow

Adds the v2 claim planner and wires it into PolymarketProvider.claimWinnings:

  • preflight/claim.ts: planClaim — reads EOA USDC.e directly (not Safe balances or provider getBalance()); computes gasStationDeficit = max(0, MIN_GAS_STATION_USDCE_BALANCE - eoaUsdceBalance); proactively wraps the entire current Safe USDC.e balance; compiles the MultiSend in the required order: (1) missing allowance/operator repair, (2) wrap-all current Safe USDC.e, (3) claim subcalls, (4) optional exact-deficit unwrap to EOA
  • PolymarketProvider.claimWinnings: under v2, resolves protocol and delegates to planClaim; uses protocol-owned claim targets (pUSD as collateral) rather than per-position collateral metadata

7. test(predict): add CLOB v2 integration coverage

Adds integration-level tests and reorganizes the provider test suite:

  • preflight/workflows.test.ts: end-to-end workflow planner tests covering all four preflight paths (trade, deposit, claim, withdraw) with concrete on-chain mock scenarios — verifies MultiSend ordering invariant (approvals first, then wraps/claims/transfers)
  • PolymarketProvider.test.ts: reorganized into v1 / v2 describe blocks; covers protocol routing (flag off → v1, flag on → v2), v2 getBalance() aggregation, all four trade preflight outcomes (none / allowances-only / wrap-only / allowances+wrap), deposit/withdraw preferred vs fallback shape, and claim ordering

8. fix: preserve EIP-712 field order in buildProtocolUnsignedOrder

Fixes the v2 order-signing codec so the generated typed data preserves the expected field ordering for EIP-712 signing.

9. codex: address PR review feedback (#29076)

Addresses review feedback in the allowance inspector path:

  • forwards requirement.tokenAddress into the ERC-1155 operator approval read instead of relying on a hardcoded contract address
  • adds assertions proving the token address is propagated correctly through the read path

10. refactor(predict): tighten CLOB v2 preflight workflow seams

Addresses follow-up review feedback without changing behavior:

  • makes lower-level transaction compilers private where possible
  • keeps getClaimRequirements exported for focused pure testing
  • tightens preflight/workflows.test.ts to assert through planner entry points instead of private helpers
  • renames claim gas-top-up terminology for clarity (MIN_GAS_STATION_USDCE_BALANCE_RAW, gasStationDeficit) while keeping eoaUsdceBalance unchanged

11. feat(predict): add configurable CLOB v2 host override

Adds temporary host-migration plumbing while keeping canonical host resolution centralized:

  • introduces canonical and legacy CLOB host constants
  • resolves the selected host into protocol.transport.clobBaseUrl
  • threads the resolved host through API key creation, order-book reads, and preview flow
  • isolates API key cache entries by protocol + host + address

12. refactor(predict): gate legacy CLOB v2 host with boolean flag

Simplifies the rollout shape for internal RC testing:

  • replaces the nested raw predictClobV2.clobBaseUrl rollout control with a second version-gated boolean flag: predictClobV2UseLegacyClobHost
  • keeps the lower-level host plumbing from commit 11 intact
  • allows internal RC builds to enable the legacy host remotely while internal users locally toggle predictClobV2

Changelog

CHANGELOG entry: null

Related issues

Fixes: https://consensyssoftware.atlassian.net/browse/PRED-817

Manual testing steps

Feature: Polymarket CLOB v2 trading

  Scenario: user trades with CLOB v2 enabled
    Given the predictClobV2 remote flag is enabled
    And the user has an active Polymarket Safe

    When user places a buy or sell order
    Then the provider runs preflight and submits any needed allowance/wrap tx first
    And the order is submitted via the relayer with X-Clob-Version: 2

  Scenario: internal RC uses the legacy v2 CLOB host
    Given the predictClobV2UseLegacyClobHost remote flag is enabled
    And predictClobV2 is enabled via local override

    When user places a buy or sell order
    Then v2 CLOB requests use https://clob-v2.polymarket.com
    And API keys are cached separately from canonical-host credentials

  Scenario: user deposits with CLOB v2 enabled
    Given the predictClobV2 remote flag is enabled

    When user initiates a deposit
    Then the deposit plan includes an optional maintenance tx (allowance repair + wrap pre-existing USDC.e)
    And the newly deposited amount is not included in the wrap

  Scenario: user withdraws with CLOB v2 enabled
    Given the predictClobV2 remote flag is enabled
    And the user requests a specific withdrawal amount

    When user confirms the withdraw
    Then only the exact USDC.e deficit is unwrapped (not all pUSD)
    And the reported amount matches the user-requested amount

  Scenario: user claims winnings with CLOB v2 enabled
    Given the predictClobV2 remote flag is enabled
    And the user has winning positions

    When user claims
    Then all missing v2 allowances are repaired first
    And the entire pre-existing Safe USDC.e is wrapped
    And claim subcalls execute after
    And only the exact EOA gas-top-up deficit is unwrapped to USDC.e

  Scenario: CLOB v2 flag is disabled (v1 behavior)
    Given the predictClobV2 remote flag is disabled

    When user interacts with any predict flow
    Then v1 protocol is used unchanged

Screenshots/Recordings

https://www.loom.com/share/155120bd46c44723a8b838172b4fd45b

Pre-merge author checklist

Performance checks (if applicable)

  • I've tested on Android
    • Ideally on a mid-range device; emulator is acceptable
  • I've tested with a power user scenario
    • Use these power-user SRPs to import wallets with many accounts and tokens
  • I've instrumented key operations with Sentry traces for production performance metrics

For performance guidelines and tooling, see the Performance Guide.

Pre-merge reviewer checklist

  • I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed).
  • I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots.

Note

High Risk
High risk because it changes Polymarket trade/claim/deposit/withdraw transaction construction and relayer submission paths, including new Safe preflight logic and EIP-712 signing for a new protocol version.

Overview
Adds Polymarket CLOB v2 support behind new Predict feature flags (predictClobV2Enabled plus optional legacy host override), with host-aware API key caching and endpoint selection.

Refactors PolymarketProvider to resolve a protocol definition once and route preview + order submission through a new protocol/ module (v2 uses new order schema/typed data, zero preview fee rate, and a relayer request header for v2 routing).

Introduces a preflight/ layer that inspects on-chain allowance/approval requirements and builds signed Safe executions for trade allowances, deposit maintenance, claim, and withdraw (including wrap/unwrap flows and balance aggregation across USDC.e + pUSD for v2). Adds extensive unit/integration tests and updates Safe utilities (raw USDC amount decoding, token-address-aware approvals, Permit2 token selection) plus CI env wiring for MM_PREDICT_BUILDER_CODE.

Reviewed by Cursor Bugbot for commit 5df04d5. Bugbot is set up for automated code reviews on this repo. Configure here.

@github-actions

Copy link
Copy Markdown
Contributor

CLA Signature Action: All authors have signed the CLA. You may need to manually re-run the blocking PR check if it doesn't pass in a few minutes.

@metamaskbotv2 metamaskbotv2 Bot added the team-predict Predict team label Apr 20, 2026
Replacing spread-based object construction with explicit field-by-field
literals to guarantee correct field ordering. Using object spread caused
the 'taker' field in the v1 path to land after 'expiration' instead of
after 'signer', producing an invalid API error.

Added a comment to prevent future regressions.
@matallui matallui marked this pull request as ready for review April 20, 2026 23:34
@matallui matallui requested a review from a team as a code owner April 20, 2026 23:34
@matallui matallui changed the title feat(predict): add Polymarket CLOB v2 support feat(predict): add Polymarket CLOB v2 support cp-7.74.0 Apr 20, 2026
Pass requirement.tokenAddress to readIsApprovedForAll instead of
hardcoding the contract address inside getIsApprovedForAll. The
Erc1155OperatorRequirement type already carries tokenAddress; now
it flows all the way through to the on-chain call.

Updated safe/utils.ts to pass MATIC_CONTRACTS.conditionalTokens
explicitly, and added an assertion to each test suite to verify
the tokenAddress is forwarded correctly.
@github-actions github-actions Bot added the risk-high Extensive testing required · High bug introduction risk label Apr 20, 2026
Comment thread app/components/UI/Predict/utils/resolvePredictFeatureFlags.test.ts
caieu
caieu previously approved these changes Apr 21, 2026
matthewwalsh0
matthewwalsh0 previously approved these changes Apr 21, 2026
Comment thread app/components/UI/Predict/providers/polymarket/preflight/deposit.ts
Comment thread app/components/UI/Predict/providers/polymarket/preflight/claim.ts Outdated
Comment thread app/components/UI/Predict/providers/polymarket/preflight/claim.ts Outdated
@matallui matallui changed the title feat(predict): add Polymarket CLOB v2 support cp-7.74.0 feat(predict): add Polymarket CLOB v2 support cp-7.73.1 Apr 21, 2026
@matallui matallui dismissed stale reviews from matthewwalsh0 and caieu via cbc4736 April 21, 2026 16:28
@github-actions github-actions Bot added risk-medium Moderate testing recommended · Possible bug introduction risk and removed risk-high Extensive testing required · High bug introduction risk labels Apr 21, 2026
@github-actions github-actions Bot added risk-high Extensive testing required · High bug introduction risk and removed risk-medium Moderate testing recommended · Possible bug introduction risk labels Apr 21, 2026
Comment thread app/components/UI/Predict/providers/polymarket/PolymarketProvider.ts Outdated
@github-actions github-actions Bot added risk-high Extensive testing required · High bug introduction risk and removed risk-high Extensive testing required · High bug introduction risk labels Apr 21, 2026
@matallui matallui requested a review from a team as a code owner April 21, 2026 20:02
@matallui matallui force-pushed the predict/clob-v2-ready branch from bbc4900 to dc5e54b Compare April 21, 2026 20:03
@github-actions github-actions Bot added risk-medium Moderate testing recommended · Possible bug introduction risk and removed risk-high Extensive testing required · High bug introduction risk labels Apr 21, 2026
Comment thread builds.yml
@github-actions github-actions Bot added risk-high Extensive testing required · High bug introduction risk and removed risk-medium Moderate testing recommended · Possible bug introduction risk labels Apr 21, 2026
@github-actions github-actions Bot added risk-high Extensive testing required · High bug introduction risk and removed risk-high Extensive testing required · High bug introduction risk labels Apr 21, 2026
@github-actions

Copy link
Copy Markdown
Contributor

🔍 Smart E2E Test Selection

  • Selected E2E tags: SmokePredictions, SmokeWalletPlatform, SmokeConfirmations
  • Selected Performance tags: @PerformancePredict
  • Risk Level: high
  • AI Confidence: 92%
click to see 🤖 AI reasoning details

E2E Test Selection:
The PR introduces significant changes to the Polymarket Predictions provider, adding support for a new CLOB v2 protocol. Key changes include:

  1. PolymarketProvider.ts - Major refactoring: new #submitOrderV1/#submitOrderV2 methods, protocol-aware deposit/claim/withdraw flows, API key caching keyed by protocol+address, and restructured placeOrder(), prepareDeposit(), and claimOrder() methods. This directly impacts all core Predictions user flows.

  2. New protocol/ directory - New files for v2 protocol definitions (definitions.ts), order codec (orderCodec.ts), and transport layer (transport.ts) implementing the CLOB v2 protocol.

  3. New preflight/ directory - New transaction builders for claim, deposit, trade, and withdraw operations under the v2 protocol.

  4. flags.ts - New feature flags predictClobV2Enabled and predictClobV2ClobBaseUrl controlling v2 protocol activation.

  5. resolvePredictFeatureFlags.ts - Updated to resolve the new v2 CLOB flags.

  6. builds.yml - Added MM_PREDICT_BUILDER_CODE env var across all build configurations (production, test, e2e, dev, flask, qa).

  7. babel.config.tests.js - Added new protocol definitions files to test overrides (minor test infrastructure change).

SmokePredictions is directly required as the core Polymarket provider changes affect all prediction market flows: opening positions, cashing out, claiming winnings, and error handling.

SmokeWalletPlatform is required per SmokePredictions tag description (Predictions is a section inside the Trending tab).

SmokeConfirmations is required per SmokePredictions tag description (opening/closing positions are on-chain transactions).

Performance Test Selection:
The PolymarketProvider changes significantly refactor the core prediction market flows including order placement, deposits, and claims. The new v2 protocol adds additional preflight transactions and protocol resolution logic that could impact the performance of prediction market operations. @PerformancePredict covers prediction market list loading, position management, deposit flows, and balance display - all of which are affected by these changes.

View GitHub Actions results

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 5df04d5. Configure here.

@sonarqubecloud

Copy link
Copy Markdown

@github-actions

Copy link
Copy Markdown
Contributor

E2E Fixture Validation — Schema is up to date
12 value mismatches detected (expected — fixture represents an existing user).
View details

@matallui matallui enabled auto-merge April 21, 2026 21:18

@MarioAslau MarioAslau left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@matallui matallui added this pull request to the merge queue Apr 21, 2026
Merged via the queue into main with commit d9f38f2 Apr 21, 2026
106 of 107 checks passed
@matallui matallui deleted the predict/clob-v2-ready branch April 21, 2026 21:51
@github-actions github-actions Bot locked and limited conversation to collaborators Apr 21, 2026
@metamaskbotv2 metamaskbotv2 Bot added the release-7.75.0 Issue or pull request that will be included in release 7.75.0 label Apr 21, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

release-7.75.0 Issue or pull request that will be included in release 7.75.0 risk-high Extensive testing required · High bug introduction risk size-XL team-predict Predict team

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants