Skip to content

feat(market-factory): support bStock markets and wire the liquidation suite - #231

Open
razww wants to merge 1 commit into
masterfrom
feat/market-factory-stock-and-liquidation-vault
Open

feat(market-factory): support bStock markets and wire the liquidation suite#231
razww wants to merge 1 commit into
masterfrom
feat/market-factory-stock-and-liquidation-vault

Conversation

@razww

@razww razww commented Aug 19, 2026

Copy link
Copy Markdown
Collaborator

Summary

Two additions to MarketFactory, plus a consistency fix the second one surfaced.

1. bStock market support. New stockCollateral flag on createMarket / stockCollaterals array on batchCreateMarkets. When set, the factory registers the collateral in StockOracleSwitch via setStock, so a tokenized-stock market is gated by market hours from the moment it exists rather than depending on a follow-up ops step.

The factory deliberately does not touch globalEnabled — the daily market-hours switch stays MANAGER-owned. An already-registered stock is skipped, because setStock reverts AlreadySet on a no-op and creating a second market on the same collateral must not reopen a stock ops deliberately closed.

2. LiquidationVault token whitelist, wired everywhere the Liquidator whitelist was already set.

3. Uniform whitelisting across all three liquidation contracts. Auditing (2) showed the factory only ever configured a subset per path. Now collapsed into one _whitelistToken(token, includeVault) helper applied to every path:

before after
common market Liquidator Liquidator + BrokerLiquidator + Vault
fixed-term market BrokerLiquidator Liquidator + BrokerLiquidator + Vault
smart-collateral legs Liquidator Liquidator + BrokerLiquidator + Vault
smart provider registration Liquidator + PublicLiquidator + BrokerLiquidator
smart LP reflow blacklist Liquidator Liquidator + BrokerLiquidator

One deliberate exception, carried by the includeVault flag: a smart-collateral LP is never vault-whitelisted. The vault cannot sell it, so it is reflow-blacklisted on both liquidators instead; whitelisting it would also have opened the BOT collect* path for it. MANAGER rescue via collectERC20 still works, since that bypasses tokenWhitelist.

Why this shape

liquidationVault and stockOracleSwitch are storage variables with admin setters, following the existing rateCalculator / brokerLiquidator pattern rather than new constructor args. The constructor immutables are unchanged, so deploy_marketFactory_impl.sol works as-is and ETH can leave both unset (vault whitelisting silently skips; the stock flag reverts loudly).

Verified against live BSC state

The chosen posture is not invented — it is what ops already maintains by hand, which the factory previously only half-reproduced:

  • All 7 StableSwapLPCollateral tokens: reflow-blacklisted on both liquidators, vault-whitelisted on none.
  • USDT / lisUSD / WBNB / slisBNB / BTCB / USD1: whitelisted on all three uniformly.
  • BNB_ADDRESS whitelisted on all three, and the slisBNB/BNB provider's token(1) is BNB_ADDRESS — so native legs need no special case; the generic leg path covers collectETH / sellBNB.
  • vault.liquidators: Liquidator ✅, BrokerLiquidator ✅, PublicLiquidator ❌ — correct, PublicLiquidator has no fundSource and never reflows.

Out of scope (flagged, not changed)

  • publicLiquidator.setMarketWhitelist is still never called by the factory. It is onlyRole(BOT) and the factory does not hold BOT on PublicLiquidator on mainnet. Enabling public liquidation is also a per-market policy call, not something to imply from liquidatorMarketWhitelist — wiring it needs a role grant plus a new flag.
  • pairWhitelist on all four contracts: DEX pools/routers are not derivable from MarketParams.

Deployment prerequisites

MarketFactory already holds MANAGER on Liquidator and BrokerLiquidator, so the new calls work. Still required before the upgraded factory can create anything:

  1. Grant MANAGER to MarketFactory on LiquidationVault (0xEe3aa1AF4Ee231f2e1277A48fc4A2f29A3D7C028) and StockOracleSwitch (0xb4678C3E8B49d2b95Da48458f98805da193A8498) — both currently false on BSC.
  2. Call setLiquidationVault / setStockOracleSwitch.

Note switch.globalEnabled is currently true on BSC, so a newly registered bStock goes live immediately rather than waiting for a market-hours open.

⚠️ Breaking ABI change: createMarket and batchCreateMarkets each take one additional argument. Any off-chain caller or script must be updated.

Test

forge test --mc MarketFactoryTest — 12 passed, including 5 new tests:

  • testCreateStockMarket — registers + enables the stock, leaves globalEnabled alone, stock only tradable after MANAGER opens the market
  • testCreateStockMarketAlreadyRegistered — idempotent, and does not reopen a closed stock
  • testCreateStockMarketRevertsWhenSwitchNotSet
  • testCreateMarketWhitelistsLiquidationVaultTokens / testCreateMarketVaultWhitelistIdempotent / testCreateMarketSkipsVaultWhenUnset
  • testSettersOnlyAdmin

testCreateSmartProviderMarket now asserts the full LP posture (not vault-whitelisted, reflow-blacklisted on both liquidators, provider registered on all three, legs whitelisted everywhere).

Full non-fork suite: 966 passed, 0 failed. forge build and npm run check clean.

🤖 Generated with Claude Code

… suite

Add a stockCollateral flag to createMarket / batchCreateMarkets that registers
the collateral in StockOracleSwitch via setStock. The factory never touches the
switch's global market-hours flag, so a new bStock is registered but stays gated
by ops. An already-registered stock is skipped, since setStock reverts on a
no-op and creating a market must not reopen a deliberately closed stock.

Whitelist market tokens uniformly across all three liquidation contracts
(Liquidator, BrokerLiquidator, LiquidationVault) for common, fixed-term and
smart-collateral markets, and register smart providers on BrokerLiquidator too.
A smart-collateral LP is the one exception: it is excluded from the vault
whitelist and reflow-blacklisted on both liquidators instead, because the vault
cannot sell it. This matches the live BSC posture, which was previously only
half-reproduced by the factory.

liquidationVault and stockOracleSwitch are storage variables with admin setters,
following the rateCalculator / brokerLiquidator pattern, so the constructor
immutables and the existing deploy scripts are unchanged and ETH can leave both
unset.

Deploying this requires granting MarketFactory the MANAGER role on
LiquidationVault and StockOracleSwitch, then calling the two setters.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@hashdit-bot

hashdit-bot Bot commented Aug 19, 2026

Copy link
Copy Markdown

Pull Request Review

This Solidity PR extends the upgradeable MarketFactory to register tokenized-stock collateral with StockOracleSwitch and adds admin-configurable StockOracleSwitch and LiquidationVault integrations. It also centralizes token and smart-provider configuration across Liquidator, BrokerLiquidator, and LiquidationVault, updates relevant interfaces, and adds tests for stock registration, uniform whitelisting, LP exclusions, idempotency, and setter access control.

Sensitive Content

No sensitive content detected.

Security Issues

No serious security issues detected.


Generated by Hashdit Bot. This tool can absolutely NOT replace manual audits.

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.

1 participant