Skip to content

fix(create-market): preserve Hyperp oracle mode on mainnet - #2434

Open
Bayyan16 wants to merge 1 commit into
dcccrypto:playgroundfrom
Bayyan16:security/hyperp-oracle-mode-regression
Open

fix(create-market): preserve Hyperp oracle mode on mainnet#2434
Bayyan16 wants to merge 1 commit into
dcccrypto:playgroundfrom
Bayyan16:security/hyperp-oracle-mode-regression

Conversation

@Bayyan16

Copy link
Copy Markdown
Contributor

Fixes #2433

Summary

This PR fixes a deterministic oracle-mode integrity issue in the market-creation flow where a user-selected hyperp oracle mode could be silently downgraded to admin on mainnet.

The affected logic treated the presence of params.mainnetCA as sufficient proof that the market was a devnet mirror:

const isDevnetMirror = !!params.mainnetCA;

However, the create-market wizard populates mainnetCA during normal mainnet market creation as token metadata:

mainnetCA: wizard.mintAddress,

As a result, the following valid mainnet configuration:

network       = mainnet
requestedMode = hyperp
mainnetCA     = present

was incorrectly resolved as:

effectiveMode = admin

instead of:

effectiveMode = hyperp

This PR makes the fallback network-aware so that Hyperp is downgraded to Admin only for an actual devnet mirror.


Root Cause

The issue was caused by using mainnetCA as a proxy for the active runtime network.

The previous implementation effectively used:

const isDevnetMirror = !!params.mainnetCA;

const oracleMode =
  resolvedOracleMode === "hyperp" && isDevnetMirror
    ? "admin"
    : resolvedOracleMode;

This assumption is unsafe because mainnetCA represents token metadata and may be populated on both mainnet and devnet.

The code therefore conflated two separate concepts:

mainnetCA present

and:

runtime network is devnet

The intended fallback is valid only when both of the following are true:

runtime network is devnet
AND
mainnetCA is present for a mirrored token

Security Invariant

The effective oracle mode used during market creation must match the mode selected by the creator, except for the intentional devnet-mirror compatibility fallback.

This PR enforces the following invariant matrix:

mainnet + Hyperp + mainnetCA present
    => Hyperp

devnet + Hyperp + mainnetCA present
    => Admin fallback

devnet + Hyperp + mainnetCA absent
    => Hyperp

Pyth
    => unchanged

Admin
    => unchanged

Keeper
    => unchanged

In particular, mainnetCA must not independently determine the active runtime network.


Changes

1. Added a pure oracle-mode resolver

A new helper was added:

app/lib/resolveMarketOracleMode.ts

The resolver explicitly receives:

  • requested oracle mode;
  • runtime network;
  • whether mainnetCA is present.

The corrected condition is:

const isDevnetMirror =
  input.network === "devnet" &&
  input.hasMainnetCA;

Hyperp falls back to Admin only when the request is running on devnet and represents a mirrored mainnet token.


2. Updated the production market-creation flow

app/hooks/useCreateMarket.ts now resolves the runtime network before selecting the effective oracle mode:

const network = getNetwork();
const isDevnetEnv = network === "devnet";

const resolvedOracleMode =
  params.oracleMode ??
  (
    params.oracleFeed === ALL_ZEROS_FEED
      ? "admin"
      : "pyth"
  );

const oracleMode = resolveMarketOracleMode({
  requestedMode: resolvedOracleMode,
  network,
  hasMainnetCA: !!params.mainnetCA,
});

This removes the incorrect assumption that the presence of mainnetCA alone indicates a devnet mirror.

The existing isDevnetEnv value continues to be used by the remaining devnet-specific market-creation logic.


3. Added regression coverage

A focused regression suite was added:

app/__tests__/lib/resolveMarketOracleMode.test.ts

The tests cover:

mainnet + Hyperp + mainnetCA
    => Hyperp

devnet + Hyperp + mainnetCA
    => Admin

devnet + Hyperp + no mainnetCA
    => Hyperp

Pyth
    => unchanged

Admin
    => unchanged

Keeper
    => unchanged

Before This Fix

The affected implementation treated mainnetCA as proof of a devnet mirror:

const isDevnetMirror = input.hasMainnetCA;

The mainnet regression test failed with:

Expected: "hyperp"
Received: "admin"

Test Files  1 failed
Tests       1 failed | 5 passed
Exit status: 1

The failure was isolated to this invariant:

mainnet + Hyperp + mainnetCA

The other five control cases continued to pass.


After This Fix

The resolver now requires an actual devnet runtime:

const isDevnetMirror =
  input.network === "devnet" &&
  input.hasMainnetCA;

The same regression suite now passes:

Test Files  1 passed
Tests       6 passed

Verified behavior:

PASS mainnet + Hyperp + mainnetCA    => Hyperp
PASS devnet + Hyperp + mainnetCA     => Admin
PASS devnet + Hyperp + no mainnetCA  => Hyperp
PASS Pyth                             => unchanged
PASS Admin                            => unchanged
PASS Keeper                           => unchanged

Validation

Targeted oracle regression tests

Command:

pnpm --filter @percolator/app exec vitest run \
  __tests__/lib/resolveMarketOracleMode.test.ts \
  __tests__/lib/oraclePrice.test.ts \
  --reporter=verbose

Result:

Test Files  2 passed
Tests       35 passed

This includes the new resolver tests and the existing oracle-price regression suite.


TypeScript validation

Command:

pnpm --filter @percolator/app exec tsc \
  --noEmit \
  -p tsconfig.json

Result:

PASS
No TypeScript errors.

Production build

Command:

pnpm --filter @percolator/app build

Result:

PASS
Next.js production build completed successfully.
Static page generation completed successfully.
Page optimization completed successfully.

The build emitted existing non-fatal warnings related to Next.js configuration deprecations, middleware migration, module.register(), and the BigInt native-binding fallback. None were introduced by this PR and none caused the build to fail.


Patch integrity

Commands:

git diff --check
git diff upstream/playground...HEAD --check

Result:

PASS
No whitespace errors.

Files Changed

app/hooks/useCreateMarket.ts
app/lib/resolveMarketOracleMode.ts
app/__tests__/lib/resolveMarketOracleMode.test.ts

Patch statistics:

3 files changed
110 insertions
14 deletions

Scope

This PR is intentionally limited to the oracle-mode resolution defect reported in #2433.

It does not modify:

  • transaction signing;
  • wallet authorization;
  • RPC behavior;
  • API routes;
  • account ownership validation;
  • on-chain instruction encoding;
  • market authorities;
  • deployed Solana program logic;
  • oracle price calculation or sanitization.

No unrelated refactoring is included.


Impact of the Fix

After this change:

  • valid mainnet Hyperp requests remain Hyperp;
  • the intentional devnet mirror fallback remains intact;
  • Hyperp-specific DEX pool processing is no longer skipped on mainnet because of mainnetCA;
  • Pyth, Admin, and Keeper behavior remains unchanged;
  • the effective oracle mode remains consistent with the creator's explicit selection.

Review Notes

The most important review points are:

  1. getNetwork() is evaluated before oracle-mode resolution.

  2. mainnetCA is treated as metadata rather than a standalone network signal.

  3. Hyperp falls back to Admin only for:

    network === "devnet" && hasMainnetCA === true
    
  4. Existing oracle modes are not altered.

  5. Regression tests explicitly cover both the affected mainnet condition and the intended devnet fallback.


Commit

50c9d3ae fix(create-market): preserve Hyperp oracle mode on mainnet

@Bayyan16
Bayyan16 requested a review from dcccrypto as a code owner July 17, 2026 04:54
@vercel

vercel Bot commented Jul 17, 2026

Copy link
Copy Markdown

@Bayyan16 is attempting to deploy a commit to the Khubair Nasir's projects Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai

coderabbitai Bot commented Jul 17, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 56997aee-d369-4555-9cca-671d4dae6184

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@dcccrypto

Copy link
Copy Markdown
Owner

Independent verification — not an approval (QA/Security own that), just evidence for whoever reviews. Verdict: the fix is right and the test binds.

Method: faithfully restored #2433 on the PR branch — mainnetCA alone treated as proof of devnet:

const isDevnetMirror =
  input.network === "devnet" && input.hasMainnetCA;
        
const isDevnetMirror = input.hasMainnetCA;

Result:

fix present → 6 passed
bug restored → 1 FAILED
  × preserves Hyperp mode on mainnet when mainnetCA is present

That's precisely the reported defect, and it's the only test that moves — the other five cover combinations that legitimately shouldn't change. Clean, well-targeted matrix, and extracting the rule into a pure function is the right shape for something this easy to get subtly wrong.

One thing worth a look before merge

The coverage is entirely on the helper; nothing exercises useCreateMarket.ts:1390, the only call site. I checked the inputs rather than just flagging it generically:

const network = getNetwork();          // :1380 — solid, single source of truth
const oracleMode = resolveMarketOracleMode({
  requestedMode: resolvedOracleMode,
  network,
  hasMainnetCA: !!params.mainnetCA,
});

network comes straight from getNetwork(), so today the wiring is sound and I'm not asking for a hook test.

But it's worth naming why this gap is a bit sharper here than in the sibling PRs: the original bug was itself a call-site inference error — inferring "devnet" from token metadata. The helper now encodes the correct rule, but the class of bug it fixes lives at the boundary the tests don't reach. If requestedMode or network were ever fed from something derived rather than getNetwork(), all six tests would stay green while mainnet Hyperp silently degraded to Admin again — same symptom, same invisibility.

Cheapest guard, if you want one: assert resolveMarketOracleMode is called with getNetwork()'s value, rather than a full hook test.

Not blocking — the fix is real and verified. Flagging for consistency with the same note I left on #2442.

@Bayyan16

Copy link
Copy Markdown
Contributor Author

Thank you for the careful mutation check and for inspecting the production call-site inputs directly.

Understood: the six-case helper matrix faithfully catches #2433, while the remaining gap is future protection at the useCreateMarket boundary. The current wiring obtains network directly from getNetwork() and passes it unchanged to resolveMarketOracleMode, so the implementation is sound on the current head.

Since this is non-blocking and no hook-level test is being requested, I’ll keep the production patch and current regression suite unchanged and await the formal QA/Security review. If the boundary assertion becomes a merge requirement, I’ll add only the smallest focused call-site guard rather than a broad hook test.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants