Skip to main content
Bring the protocol up in order: governance, controller, price aggregator, the single central pool, the position NFT, then each market through timelocked proposals, then unpause. Operators use the stellar CLI, usually wrapped by make targets. The same sequence applies to testnet and mainnet. Substitute testnet or mainnet for <network> below.

What you need before deploy

Prerequisites

  • The stellar CLI and jq.
  • A compatible Rust toolchain for Soroban contracts.
  • A funded signing identity (default deployer), or SIGNER=ledger for a ledger-backed signer.

Build artifacts

make deploy-artifacts produces governance.wasm, controller.wasm, and pool.wasm for upload. The position NFT and price aggregator are deployed by governance from hashes it already holds, so they need no separate staging step.

Deploy and bring-up order

The order is load-bearing. Governance deploys first, because it owns everything after it. The controller starts paused. The pool deploys once for the whole protocol, and every asset registers on that one pool. make <network> deploy runs phases 1–8 below; make <network> setup runs the whole thing including markets and spokes.
1

Upload WASM and deploy governance

Uploads pool.wasm and controller.wasm, then deploys governance with __constructor(admin, min_delay).That constructor sets admin as owner, sets it as the access-control admin, grants it all five roles (PROPOSER, EXECUTOR, CANCELLER, GUARDIAN, ORACLE), and stores min_delay. A zero delay is rejected with #39 InvalidTimelockDelay.Record the governance id and the delay — later steps need both.
2

Deploy the controller

Governance calls deploy_controller(controller_wasm_hash) immediately. It is one-time and owner-gated; a second call reverts #5 PoolAlreadyDeployed.The controller’s __constructor(admin) does four things:
  • sets governance as owner;
  • sets both position limits to POSITION_LIMIT_MAX = 5;
  • sets the minimum-borrow floor to $5 WAD;
  • pauses the contract.
The controller grants no roles at all. It has no access-control role set — every admin entry point is owner-gated, and every user flow is caller-authenticated. Only governance has roles.
3

Deploy the price aggregator

Governance calls deploy_price_aggregator(wasm_hash). This also wires the controller atomically in the same call, so there is no separate “set price aggregator” step at bring-up.Skip this and every priced call fails with #27 AggregatorNotSet.To re-point a live aggregator later, use the timelocked SetPriceAggregator operation.
4

Deploy the central pool

Schedule and execute DeployPool(wasm_hash) through the timelock. The hash is an argument — there is no template-registration step.Run it once; a repeat reverts #5 PoolAlreadyDeployed. Day-2 code changes use the same shape, UpgradePool(hash).
5

Deploy the position NFT

Schedule and execute DeployPositionNft(...) through the timelock.This is not optional. The account_id is the NFT token_id, and the controller asks the NFT contract who owns an account on every authority check. Without it, every account-touching call fails with #53 PositionNftNotSet.
6

Wire the swap aggregator and accumulator

make <network> configure-controller sets the swap aggregator and the revenue accumulator.Both are checked, not skipped silently. Without an accumulator, claim_revenue fails with #211 NoAccumulator. Set ALLOW_MISSING_AGGREGATOR=1 or ALLOW_MISSING_ACCUMULATOR=1 only if you deliberately want to defer one.
7

Register each market on the shared pool

Governance schedules a typed AdminOperation per step through propose(proposer, op, salt), then execute after the delay. Per asset:
  1. AdminOperation::CreateLiquidityPool — registers the hub asset on the existing central pool.
  2. AdminOperation::ConfigureAssetOracle — price reads work only after this.
  3. AdminOperation::AddAssetToSpoke (or EditAssetInSpoke) per spoke.
There is no token-approval step.
create_liquidity_pool reverts with #30 PoolNotInitialized if deploy_pool has not run, #2 AssetAlreadySupported if the market already exists, #43 HubNotActive if the hub is missing or deactivated, and #8 WrongToken if params.asset_id does not match the asset.
8

Configure spokes and Blend pools

Add spokes and memberships through governance:
Approve Blend migration pools through governance:
9

Unpause

Turn the protocol live only after the pool and position NFT are deployed, every market has pool rows and a working oracle, and the spokes list them.Unpause is timelocked — it is AdminOperation::Unpause, scheduled and executed like any other governance change. Only pause is immediate. On mainnet the setup target deliberately leaves the protocol paused so the timelock can be raised to its production floor first:
make <network> setup chains: deploy → configure-controller → market, spoke, and Blend setup → rent prepay → status. On testnet it also unpauses; on mainnet it deliberately does not. make <network> resume re-runs everything from configure-controller onward. Run individual targets only for partial deploys or recovery.

Pre-flight checks

Before enabling a network for users, confirm:
  • Workspace tests, integration harness, and fuzz targets pass for the release
  • Certora profiles for the release tag
  • Deployed addresses, governance ownership, timelock roles, markets, spokes, oracles, and caps
  • Keeper config covers launched HubAssetKey markets
  • Pause / unpause drill on that network
Cap values are set per network at listing time. Prefer on-chain views and the app for live caps.

Upgrades

Governance self-upgrade uses AdminOperation::UpgradeGov(hash) scheduled with propose and applied with execute_self (make <network> upgradeGovernance). Governance-self operations cannot go through execute, because Soroban does not allow the generic self-call pattern used for controller calls.

Smoke tests

Confirm market state with make <network> getAllMarkets, make <network> getAllIndexes, and make <network> info.

Next

Governance

Timelock roles and the propose/execute lifecycle.

Configuration

Validation rules for risk, oracle, and spoke parameters.

Addresses

Deployed contract addresses per network.