Skip to content

feat(scenarios/phoenix): prepare state with Phoenix's own instructions - #31

Open
92Infinitus92 wants to merge 7 commits into
feat/scenarios/override/phoenix-supportfrom
feat/phoenix-support
Open

92Infinitus92 wants to merge 7 commits into
feat/scenarios/override/phoenix-supportfrom
feat/phoenix-support

Conversation

@92Infinitus92

@92Infinitus92 92Infinitus92 commented Oct 8, 2026 •

Copy link
Copy Markdown
Collaborator

Rebuilds the Phoenix Eternal templates so every change goes through Phoenix's own instructions instead of byte patches, which keeps the accounts that make up a market consistent with each other.

  • Each template encodes official Phoenix instructions from the bundled IDL and runs them on a copy of the local VM, signed by the key that signs them on mainnet (the oracle keys, the makers, the trader, or the GlobalConfig role), then writes back the accounts they changed. Phoenix validates every change itself, so a refused change is a skipped override with Phoenix's reason in the warning.
  • Templates: market move (oracle readings, maker splines and the book together), liquidation-ready positions, liquidation cascade, open position, cancel orders, withdraw, deposit, and twelve configuration levers (risk factors, cancel factor, max liquidation size, open interest cap, funding, fees, market and exchange status, withdraw limits and parameters, trader fees and capabilities). A configuration field left out keeps its current value. The collateral-stress, direct-mark and maintenance-stress templates and the create_phoenix_collateral_scenario MCP tool are removed.
  • Liquidation-ready takes one or more symbols of markets the trader holds. Phoenix judges liquidation by the whole account, so the template cancels the trader's orders, moves every listed market by one common share of its mark against the position (longs down, shorts up), tries the liquidations one after another on a copy, and prepares the middle of the moves where the whole run goes through. Surfpool's log names the order and each market's new price. Spline market makers are refused, and so is a hot trader the local GlobalTraderIndex does not list.
  • The cascade has no trader count: it looks at the first 24 holders of the chosen side in Phoenix's active trader index, in address order, reads their positions through Hawkeye, and prepares every holder that one price can make liquidatable. Surfpool's log names the price and the traders.
  • A market move repeats the uncross crank until Hawkeye's best bid and ask show the book uncrossed, or a crank changes neither those prices nor the book's resting-order count, since one crank matches at most 64 orders.
  • The first Phoenix template or tool that needs the GlobalTraderIndex on a surfnet loads it with the ActiveTraderBuffer and every market's book in one request. Books and the buffer name traders by their node in the index, and nodes are freed and reassigned within minutes, so copies from different moments made Phoenix refuse the book. On fresh surfnets this skipped liquidation-ready and cascade Plays in 2 of 2 runs before the change and in none after.
  • list_phoenix_trader_positions (MCP) lists a trader's positions as Hawkeye reports them, with each position's maintenance margin.
  • keep_markets_usable moves funding timestamps that are ahead of the local clock back to it, and oracle reading slots that are behind it up to it. A running surfnet's clock trails mainnet, and Phoenix refused funding updates and mark prices otherwise, even with the raised stale thresholds.
  • The README explains fetchBeforeUse for Phoenix, how the accounts are loaded, and the case the grouped load does not cover (Phoenix accounts your own code read first), with its symptoms and recovery.
  • Core: the Phoenix hook receives the template id instead of an unused freshness flag, and json_to_txtx_value_for_idl_type is pub(crate).

Tested with the full core suite with ignore_tests_ci (815 passed), the Phoenix tests against mainnet state (67), and the MCP tests (23). In Studio on a fresh surfnet, with 14 minutes between loading a trader's positions and Play: its SOL, AVAX, LTC and BNB positions were prepared and liquidated by four real keeper transactions in the logged order while its other positions stayed; earlier runs covered nine positions of one trader. A SOL long cascade was liquidated by real keeper transactions, and an ETH move of -10% landed on its target price with the book uncrossed.

Pairs with LimeChain/surfpool-web-ui#26.

RetriggerConfidence Score: 3/5

Fix the early crank exit and stale cascade holder filter before merging.

Fix All in Claude CodeFindings

  1. P1 Cranking stops before orders uncross ▶
  2. P1 Cascades miss current holders ▶
Fix with agent prompt
### Issue 1
crates/core/src/scenarios/protocols/phoenix-eternal/v1/market.rs:408-411
`uncross_done` treats unchanged bid and ask prices as success even when the book is still crossed. If more than 64 crossed orders share the best price, one crank can fill 64 orders without changing either price. The move then stops and writes back a crossed book, so later trades or liquidations can still fill against old orders.

Keep cranking while `after.crossed()` is true. Use changes in orders or filled amounts, rather than prices alone, to detect a stalled crank.

### Issue 2
crates/core/src/scenarios/protocols/phoenix-eternal/v1/trader.rs:505-510
`prepare_cascade` chooses holders from `Trader::positions()` before asking Hawkeye what they hold. A hot trader's account copy can miss a position that Phoenix keeps in `ActiveTraderBuffer`, so that trader is skipped before `holdings_in` runs. This can leave valid holders out or make the cascade report that no holder qualifies.

Read the chosen market's position through Hawkeye first. Apply the side filter and 24-holder limit to that result.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Summary

Replaces Phoenix byte patches with official instructions run on a copied VM.

  • Phoenix scenarios update markets and trader state through Phoenix instructions.
  • Liquidation scenarios prepare positions for keeper liquidations in a chosen order.
  • The MCP server lists Phoenix positions with their maintenance margins.
  • The older collateral-stress scenarios and collateral-scenario MCP tool are removed.

Diagram

%%{init: {'theme': 'neutral'}}%%
flowchart TD
  A[Scheduled Phoenix override] --> B[Load missing accounts]
  B --> C[Keep local markets usable]
  C --> D[Copy local VM]
  D --> E[Run Phoenix instructions]
  E --> F{Instructions succeed?}
  F -->|No| G[Skip override and warn]
  F -->|Yes| H[Return changed accounts]
  H --> I[Write prepared accounts to surfnet]
  I --> J[User sends trades or liquidations]
Loading

Reviews (1) · Last reviewed commit: "feat(scenarios/phoenix): liquidate sever..." · Reviewed by Greptile

@failfmi

failfmi commented Oct 9, 2026

Copy link
Copy Markdown
Collaborator

@greptileai

Comment on lines +408 to +411
fn uncross_done(before: &BboView, after: &BboView) -> bool {
!after.crossed()
|| (after.best_bid_ticks, after.best_ask_ticks)
== (before.best_bid_ticks, before.best_ask_ticks)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Cranking stops before orders uncross

uncross_done treats unchanged bid and ask prices as success even when the book is still crossed. If more than 64 crossed orders share the best price, one crank can fill 64 orders without changing either price. The move then stops and writes back a crossed book, so later trades or liquidations can still fill against old orders.

Keep cranking while after.crossed() is true. Use changes in orders or filled amounts, rather than prices alone, to detect a stalled crank.

Prompt To Fix With AI
This is a comment left during a code review.
Path: crates/core/src/scenarios/protocols/phoenix-eternal/v1/market.rs
Line: 408-411

Comment:
**Cranking stops before orders uncross**

`uncross_done` treats unchanged bid and ask prices as success even when the book is still crossed. If more than 64 crossed orders share the best price, one crank can fill 64 orders without changing either price. The move then stops and writes back a crossed book, so later trades or liquidations can still fill against old orders.

Keep cranking while `after.crossed()` is true. Use changes in orders or filled amounts, rather than prices alone, to detect a stalled crank.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Fix in Claude Code Fix in Codex Fix in Cursor

Comment on lines +505 to +510
let lots = view
.positions()
.find(|(asset, _)| *asset == context.market.asset_id)
.map(|(_, position)| position.base_lot_position().as_inner())
.unwrap_or(0);
if (long && lots > 0) || (!long && lots < 0) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Cascades miss current holders

prepare_cascade chooses holders from Trader::positions() before asking Hawkeye what they hold. A hot trader's account copy can miss a position that Phoenix keeps in ActiveTraderBuffer, so that trader is skipped before holdings_in runs. This can leave valid holders out or make the cascade report that no holder qualifies.

Read the chosen market's position through Hawkeye first. Apply the side filter and 24-holder limit to that result.

Prompt To Fix With AI
This is a comment left during a code review.
Path: crates/core/src/scenarios/protocols/phoenix-eternal/v1/trader.rs
Line: 505-510

Comment:
**Cascades miss current holders**

`prepare_cascade` chooses holders from `Trader::positions()` before asking Hawkeye what they hold. A hot trader's account copy can miss a position that Phoenix keeps in `ActiveTraderBuffer`, so that trader is skipped before `holdings_in` runs. This can leave valid holders out or make the cascade report that no holder qualifies.

Read the chosen market's position through Hawkeye first. Apply the side filter and 24-holder limit to that result.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Fix in Claude Code Fix in Codex Fix in Cursor

@92Infinitus92
92Infinitus92 marked this pull request as ready for review October 9, 2026 12:09

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Overview

This PR replaces the Phoenix Eternal byte-patch templates with templates that run Phoenix's own IDL instructions in a sandbox copy of the local VM. Each instruction is signed by its mainnet signer (oracle keys, makers, the trader, or a GlobalConfig role), and only the accounts the instructions changed are written back. It adds:

  • market-move, liquidation-ready (several markets, one trader), cascade, open, cancel, withdraw and deposit templates;
  • 12 configuration templates;
  • a list_phoenix_trader_positions MCP tool;
  • keep_markets_usable, which rewinds funding timestamps and restamps oracle readings.

The two issues Greptile raised are fixed in 044171a:

  • The uncross loop now counts resting orders as well as the best bid and ask.
  • Cascade holders are now found through Hawkeye instead of the Trader account's copy.

Assessment

Overall the design is sound. Because Phoenix checks every change itself, refused changes come back with Phoenix's reason, and the sandbox trial() pattern keeps the searches free of side effects. The main remaining concern is how the cascade handles errors: some failures for one holder are skipped, but others abort the whole override. Smaller points are below.

Notes that don't belong to a single line:

  • Every Phoenix override rewrites the map. keep_markets_usable runs before every Phoenix override, including the ones that fall through to the generic IDL path (phoenix-stop-loss-trigger, phoenix-permission-limits). The account passed to the generic path was read before the map was patched. No current generic template targets the PerpAssetMap, so this is only a trap for later. A future generic override on the map would write back the stale copy and undo the raised thresholds.
  • Partial writes on failure. svm.rs stops applying a Phoenix override's writes at the first set_scenario_override_account error but keeps the writes already applied. Since the point of this PR is accounts that agree with each other, a partial write leaves the book, trader and map out of step. Writing all of them or none would be safer.
  • Cost. Each Sandbox::new and trial() clones the whole SVM (clone_for_profiling). The cascade does this per holder, up to 24, plus two bisections per holder, so Play can get slow on busy markets. A short note in the README, or a log line with the elapsed time, would help users.

mark
} else {
let Some(price) =
boundary(&mut search, &mut context, trader, far, precision, |view| {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Error handling is inconsistent within one holder's band search. Cancel and initial-margin failures continue to the next holder, but this boundary(...)? (and search.run(&context.oracle_reports(mark)?)? at 555, and the underwater call at 575) propagate errors. If Hawkeye's margin view fails at a deep search price for any single holder, the whole cascade override is skipped. The restamp_readings comment mentions exactly this: an EMA can leave no positive price. Consider treating an Err here like the other per-holder failures and skipping that holder.

.collect();

// A trader the book cannot absorb in turn is dropped, and the rest are prepared again.
for _ in 0..2 {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

The retry loop has two weaknesses.

  1. prepared.run(cancels)? at 624 aborts the cascade if the combined cancels fail, even though each holder's cancels passed on their own in the band phase.
  2. After two passes the function returns an error even when the second pass still had liquidations that went through, because the survivors are never re-prepared.

Looping until liquidations.len() == chosen.len() (each pass strictly shrinks chosen) would be both simpler and more robust than a fixed 0..2.

/// so 64 stay well inside a transaction's 1.4 million.
const UNCROSS_MATCH_LIMIT: u64 = 64;
/// A book side holds at most `ORDERBOOK_CAPACITY` resting orders, so no move needs more cranks.
const MAX_UNCROSS_CRANKS: usize = ORDERBOOK_CAPACITY / UNCROSS_MATCH_LIMIT as usize;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

The bound assumes one book side. Each match uses up at least one order on either side, so a fully crossed book can take up to (num_bids + num_asks) / 64 cranks, which is about 2× this. Spline liquidity may add more on top. It's unlikely to be hit, but the error at line 399 would then blame a book that was still making progress. Consider 2 * ORDERBOOK_CAPACITY / UNCROSS_MATCH_LIMIT, since the no-progress check in uncross_done already guards against spinning.

Comment thread crates/mcp/src/surfpool/mod.rs Outdated
rpc_url: &str,
trader: &str,
) -> Result<serde_json::Value, String> {
let trader = Pubkey::from_str(trader)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

The removed collateral tool trimmed the input (params.trader.trim()), but this one doesn't. An address pasted with whitespace or a newline is now rejected as invalid. Use Pubkey::from_str(trader.trim()).

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Overview

This PR replaces the Phoenix Eternal byte patches with real Phoenix instructions. Each template runs those instructions on a sandboxed copy of the VM, signed by the key that signs them on mainnet (with signature checks off), and writes back only the accounts that changed. That design is much more robust than hand-patching bytes, since Phoenix validates every change itself. It also adds cross-margin and cascade liquidation preparation, twelve config levers, and an MCP positions tool.

Greptile raised two issues. Both look fixed in 044171a:

  • uncross_done now also compares the number of resting orders.
  • The cascade now finds holders through Hawkeye instead of the Trader account's copy.

Main concerns

  • Cascade robustness: inside the per-trader band loop, a failure for one trader stops the whole cascade (?), even though candidate discovery carefully skips such traders. Details inline.
  • Cascade cost and side effects: prepare_cascade loads every trader in the GlobalTraderIndex into the live surfnet before it applies the 24-holder cap. Those writes are permanent, unlike the sandbox runs. The 24 holders are also the first ones in pubkey order, not the largest or the riskiest, so the cascade covers an arbitrary subset.
  • keep_markets_usable runs for every Phoenix-owned account override, including ones on the generic IDL path (prepare_phoenix_override gates on starts_with("phoenix-") || owner == PHOENIX). As a result, an unrelated override silently rewrites the PerpAssetMap: thresholds, reading slots and funding timestamps. That may be intended, but it is worth confirming and documenting.
  • markets_in_map cost: it finds each market's offset by searching the ~1.6 MB map for the metadata bytes. It runs three times per override (raise, rewind, restamp). Computing the offsets once and passing them to all three would avoid the repeated scans.

Minor

  • Input names are inconsistent: phoenix-open-position takes Bid/Ask, while the cascade takes long/short.
  • liquidation() uses Pubkey::new_unique() for the liquidator wallet. It is a test-oriented helper and makes the logged instructions non-deterministic. A fixed placeholder key would be clearer.
  • trader_positions builds a full SurfnetSvm for every MCP call. That is acceptable, but it costs more than a read-only tool suggests.

Overall the design is solid. The points above are mostly about robustness and efficiency in the cascade path.

continue;
}
search.next_slot();
search.run(&context.oracle_reports(mark)?)?;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

The trader-discovery loop above skips a trader on any failure (let Ok(..) else { continue }). Here, though, search.run(...)? (and the boundary(...)? calls on L567 and L575) fail the whole cascade if one holder's oracle report or margin view fails, for example because of a stale or inconsistent account. Treat these per-trader failures as continue too, so one bad holder doesn't block the rest.

.collect();
listed.sort_unstable();
listed.dedup();
hydrate(svm, remote_ctx, &listed).await?;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This loads every trader listed in the GlobalTraderIndex into the live surfnet. That can be thousands of accounts. Unlike the sandbox runs, these writes are permanent. The loop below then stops after MAX_CASCADE_CANDIDATES (24). Consider loading in chunks inside the loop and stopping once 24 holders are found. Separately, taking the first 24 in pubkey order is arbitrary: large holders can be left out of the cascade. It may be worth mentioning this in the docs or log.

/// so 64 stay well inside a transaction's 1.4 million.
const UNCROSS_MATCH_LIMIT: u64 = 64;
/// A book side holds at most `ORDERBOOK_CAPACITY` resting orders, so no move needs more cranks.
const MAX_UNCROSS_CRANKS: usize = ORDERBOOK_CAPACITY / UNCROSS_MATCH_LIMIT as usize;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Each match removes an order from either side, so emptying the book can take up to 2 * ORDERBOOK_CAPACITY matches, not ORDERBOOK_CAPACITY. Integer division also truncates. The uncross_done progress check makes this mostly theoretical, but ORDERBOOK_CAPACITY * 2 / UNCROSS_MATCH_LIMIT + 1 (or div_ceil) would match the comment's claim.

if !template_id.starts_with("phoenix-") && account.owner != PHOENIX_PROGRAM_ID {
return Ok(None);
}
keep_markets_usable(svm, remote_ctx, target_slot).await;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This guard also covers any Phoenix-owned account on the generic IDL path (the _ => Ok(None) arm). So keep_markets_usable rewrites the PerpAssetMap override (thresholds, restamped slots, rewound funding) even for an unrelated Phoenix account override. If that's intended, a comment would help. If not, move the call after the template_id dispatch so only instruction-based templates trigger it.

for entry in map_entries(account_pubkey, data)? {
let metadata = entry.metadata;
let metadata_bytes = metadata.as_bytes();
let offset = data[cursor..]

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Each market's offset comes from a linear byte search over the ~1.6 MB map. keep_markets_usable calls markets_in_map three times (raise, rewind, restamp) on every Phoenix override. Compute the offsets once and pass them to all three patchers. Ideally, derive them from the layout instead of searching for matching bytes, which could in theory match the wrong place.

("phoenixProgram", PHOENIX_PROGRAM_ID),
("phoenixLogAuthority", log_authority()),
("globalConfiguration", PHOENIX_GLOBAL_CONFIG),
("liquidatorWallet", Pubkey::new_unique()),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pubkey::new_unique() is a test-oriented helper built on a global counter, so the logged and returned liquidation instructions differ on every run. A fixed placeholder liquidator key would keep them deterministic and make clear this wallet is a stand-in.

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Overview

This PR replaces the Phoenix Eternal byte-patch templates with templates that run Phoenix's own instructions in a sandbox copy of the local VM. It adds market move, liquidation-ready (cross-margin), cascade, trading/collateral templates and 12 config templates, plus a list_phoenix_trader_positions MCP tool. Running everything through Phoenix keeps the book, oracles and trader index consistent, which is a clear improvement over the old approach. The searches and sandboxing are structured carefully.

Both Greptile findings appear fixed in the head commit:

  • uncross_done now compares resting order counts too.
  • prepare_cascade filters holders through Hawkeye's view_margin_for_asset, not Trader::positions().

Issues worth addressing

  • Cascade robustness: in prepare_cascade, most per-holder failures are skipped, but the oracle-report run, margin and the boundary(...)? calls use ?. One misbehaving holder can abort the whole cascade (see inline).
  • "Every holder" vs. the 24 cap: the PR body and README say the cascade prepares every holder of the chosen side. The scan actually stops after the first 24 matching traders in pubkey order, so the set is arbitrary on busy markets. Either document the cap or rank candidates (for example by distance to liquidation).
  • Search path-dependence: CrossMarginSearch::band and boundary reuse one sandbox for every bisection probe. Each probe stacks another oracle report on the previous ones. If the mark or its EMA depends on report history (the restamp_readings comment suggests it does), a probe's result depends on earlier probes. The final run_at re-checks the chosen point, so this affects how good the band is, not whether the result is correct.
  • Cost: prepare_cascade hydrates every trader in the GlobalTraderIndex, which can be thousands of RPC fetches. It then runs Hawkeye views until it has 24 candidates. trader_positions (MCP) builds a full SurfnetSvm for each call. Both are acceptable but could be slow on first play.
  • Unrelated file: .github/workflows/claude-review.yml has nothing to do with Phoenix and probably belongs in its own PR. It also puts the PR title and body straight into the prompt, and the post_feedback job has write permissions. That is a prompt-injection path for posting arbitrary review comments, though only from same-repo PRs.

Minor

  • uncross_done returns success when a crank makes no progress but the book is still crossed. That silently writes back a crossed book. A warning, or an error naming the market, would make this visible.

Overall the PR is in good shape. The cascade error handling is the main fix I'd make before merging.

continue;
}
search.next_slot();
search.run(&context.oracle_reports(mark)?)?;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Most per-holder failures in this loop are skipped with continue (cancels, margin view, header). This line, and the boundary(...)? calls at 570 and 578, use ? instead, so one holder whose oracle-report run or Hawkeye margin view fails mid-search aborts the whole cascade. Consider treating these errors as continue for this holder too.

};
candidates.push((trader, authority, held));
}
if candidates.len() == MAX_CASCADE_CANDIDATES {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

The scan stops at the first 24 matching holders in pubkey order. On a busy market that set is arbitrary, and it may leave out the holders closest to liquidation. That contradicts the PR body and README ("prepares every holder of the chosen side"). Either document the cap, or collect all holders and rank them before truncating, for example by margin ratio from the MarginView you already read.

} else {
9 * MOVE_SCALE
};
let mut search = self.base.trial();

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Every probe in both first_move searches runs on this one search sandbox, so each probe's oracle reports stack on the previous probes' reports. If Phoenix's mark or EMA depends on report history (the restamp_readings comment suggests it does), whether a move counts as liquidatable depends on the order of the probes. run_at re-checks the final point, but the band can come out skewed. Using self.base.trial() for each probe, or at least for each first_move, would make the search deterministic. The same applies to boundary in prepare_cascade.

fn uncross_done(before: &(BboView, usize), after: &(BboView, usize)) -> bool {
let ((before, before_orders), (after, after_orders)) = (before, after);
!after.crossed()
|| (after.best_bid_ticks, after.best_ask_ticks, after_orders)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

When a crank makes no progress (same bid, ask and order count) while after.crossed() is still true, this returns true, and move_in returns Ok with a book that is still crossed. Later trades or liquidations can then fill against stale orders without any signal. Consider returning an error, or at least logging a warning naming the market, when the loop exits through the no-progress branch while the book is still crossed.

@@ -0,0 +1,141 @@
name: Perform a code review when a pull request is created.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I am not sure if this file should enter

Use fetchBeforeUse: true on the first Phoenix scenario on a surfnet, false after that.
address:
type: pubkey
value: 2nHGAaEw3D5dd4hVueaUNoygkQFmoeKqRQWnSPqSMFUC

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I think that should be not hardcoded ?

protocols::{
phoenix_eternal::v1::state_builder::{
PHOENIX_PERP_ASSET_MAP, build_phoenix_collateral_scenario, phoenix_markets,
phoenix_eternal::v1::{

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

In my view that is to be generic file for the mcp, but it includes phoenix-specific logic. Do we really need that logic to be here and for what reason?

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.

3 participants