ReferenceCurveFactory_v1 creates a market’s contracts and initializes them in one transaction (createReferenceCurveWithRecovery). The launch route (LaunchMarket.s.sol and LaunchMarketProduction.s.sol) takes one market configuration file to a sealed market. It creates the market, grants its roles, hands the admin role to the market’s admin execution core and seals it. A check runs before each broadcast and after each mined step. An off-chain readback (bin/lib/handoff-assert.ts) checks the final state.

Before any market

The protocol contracts exist before the first market. The deployment script creates them through DeterministicFactory_v1, a CREATE2 factory that only its allowedDeployer can use. ReferenceCurveFactory_v1 creates a market only for an allowed deployer, or for any caller while open deployment is on. A production plan refuses a factory with open deployment. Authority covers who changes the factory’s deployers and Safe pair.

Configuration inputs

A market configuration file sets these inputs. The launch plan (LaunchMarketPlan_v1) reads and validates them before any broadcast. The reserve manager accepts two configuration forms. The first holds the unbacked-draw flag and ceiling. The second also turns on the endowed option and names the expected endowment controller and endowment admin. The launch route at this commit encodes the first form. The plan validation reverts on any of these:
  • A bps value above 10,000.
  • A segment with a zero price, zero supply per step or zero steps.
  • A ceiling above 0 while unbacked draws are not allowed.
  • A zero value for rounds per transaction, fee multiplier, decay duration or the early access global cap.
  • A zero elevation threshold, an empty, zero or duplicate elevation keeper, or an elevation keeper equal to the deploy signer.
  • A rebalance keeper, operator or handoff holder that is unset, equal to another of the three, or equal to the deploy signer.
  • On a production plan: a chain ID not in productionChainIds, a fixture file, an empty metadata URI, no admin execution core, a handoff holder other than the core, or a missing proposer, executor or canceller.

Steps

The route runs in separate invocations. Each later invocation reads the mined state of the earlier ones.
  1. Preflight. Every check in the preflight table below runs before any broadcast. Any failure stops the route with nothing sent.
  2. Create (runCreate). The deploy signer calls createReferenceCurveWithRecovery. Inside that one transaction the factory:
    1. checks each module’s metadata title, and requires the guardian’s recovery Safe to be the factory’s emergency Safe;
    2. creates the curve proxy and records it under the next market ID;
    3. deploys the issuance token through CurveBoundTokenDeployer_v1, bound to the curve, with the approval Safe as owner and as rebind authority;
    4. writes the token address into the curve’s configuration, and reverts if that word was not zero;
    5. creates the authorizer, fee treasury, reserve manager, staking manager, credit facility and elevation treasury proxies through ModuleFactory_v1;
    6. deploys the market’s MarketFreezeGuardian_v1 with the factory’s Safe pair, the recovery Safe and the recovery delay;
    7. initializes the curve, then the authorizer, fee treasury, reserve manager and the three optional modules, each with the same curve, authorizer, fee treasury, reserve manager and guardian;
    8. runs the creation checks in the table below.
  3. Bind (runConfigure). The route reads the market ID, curve and block from the mined ReferenceCurveCreated log. It requires the ID to resolve to that curve on the factory, and the deploy signer to be the only DEFAULT_ADMIN_ROLE holder.
  4. Strategy. A production plan has no staking strategy. A testnet plan can create one.
  5. Early access. The deploy signer calls attachPresaleWithTreasury. The factory creates a second splitter for early access fees and a Presale_v1 that uses it. The caller must hold DEFAULT_ADMIN_ROLE on the market. A second attach for the same market reverts.
  6. Guardian check. The route requires the guardian to serve this curve, hold the governance Safe pair and the reviewed recovery settings, and be the guardian of every module.
  7. Roles. The route creates REBALANCER, OPERATOR and ECONOMIC_ADMIN and grants their selectors. It grants PUBLIC_ROLE these functions: sell, credit repay, and staking stake, harvestYield and withdrawFunds.
  8. Module permissions. The route creates PRESALE_MODULE, CREDIT_FACILITY_MODULE and STAKING_MODULE, each held by its module contract alone. It grants each module the calls it makes to the curve, the credit facility and the reserve manager. It pairs the presale with the credit facility on the authorizer (setPresaleCreditFacilityPair).
  9. Elevation. The route creates ELEVATION_MODULE, held by the elevation treasury, and ELEVATION_KEEPER, held by the elevation keepers. It points both splitters at the elevation treasury, as a share or as the elevation fee address, by the configured mode.
  10. Module factory allowance. The deploy signer drops any ModuleFactory_v1 allowance it holds.
  11. Spot tolerance. The deploy signer, still DEFAULT_ADMIN_ROLE, calls setSpotToleranceBps with the configured value.
  12. Admission (runAdmission). The core admits acceptDefaultAdmin and sealDefaultAdmin on the market’s authorizer, each by a scheduled self-call.
  13. Offer (runProposal). The deploy signer calls proposeDefaultAdmin(core, expiresAt).
  14. Acceptance (runAcceptance). The core schedules and then executes acceptDefaultAdmin(nonce).
  15. Retirement (runBootstrapRetirement). The deploy signer renounces DEFAULT_ADMIN_ROLE. The handover locks.
  16. Factory allowance (runFactoryAllowanceRemoval). The deploy signer renounces its ReferenceCurveFactory_v1 deployer allowance.
  17. Seal (runSeal). The core schedules and then executes sealDefaultAdmin.
  18. Final readback (readSeal, runPublish). The route requires isProductionActive and reruns the handoff checks. It then writes the market artifact.
Steps 12, 14 and 17 each wait at least the core’s minimum delay between scheduling and execution. Authority describes the handover rules. On a non-production plan, the route uses the generic role authorizer. Steps 12 to 17 are replaced by one step, _handOverAdmin, which grants the role to the handoff holder and renounces the deploy signer, then by the factory allowance renounce.

Checks

State at the end of the launch route

After the launch route

The launch scripts at this commit end at the seal. Opening the market is a set of later calls. Each call by the core needs an admitted selector and waits the core’s minimum delay. Curve buy checks the buy switch for every caller. Early access purchases go through the curve’s buy, so they also need buying open.

Edge cases

  • Restarted phases. Each production phase rebuilds the market from mined logs and reruns the complete configuration check. A phase that finds its operation waiting or done sends nothing.
  • Expired offer. If the offer cannot cover the core’s delay and window, the deploy signer cancels it and proposes a new one.
  • Interleaved creation. The creation phase checks that the latest market ID resolves to its curve in simulation only. The configuration phase binds to the mined log, and the on-chain readbacks detect another creation between the two.
  • Seal order. sealDefaultAdmin reverts unless the core is the only holder, the handover is locked and the core’s code hash is unchanged.
  • Funding before the seal. enableBuy, buy, buyFor and setPresaleState are refused for every caller until the seal.

Code

Source: house-markets/contracts at 0ebbc89a (branch integration/v12-post-qa).