d008ee1a / a2486b25. It describes
deployment behavior, not the current state of a network. Record the release,
WASM hashes, transaction results, and verified contract links for each deployment.
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.
Deployment configuration
Prerequisites
- The
stellarCLI andjq. The committed build uses Soroban SDK 28 and pins CLI 28.0.0 inmake install-stellar-cli. - A compatible Rust toolchain and the contract’s WASM build target.
- A funded signing identity (default
deployer), orSIGNER=ledgerfor a ledger-backed signer.
Build artifacts
make deploy-artifacts stages governance.wasm, controller.wasm,
pool.wasm, position_nft.wasm, and price_aggregator.wasm. Deployment
uploads the required WASM and passes the returned hashes to the deployment
calls; governance does not start with those hashes.
To use an attested release instead of building the local checkout, pass
RELEASE_TAG=<release-tag> to a deployment or upgrade target. Select a release
whose ABI matches the integration. Local changes are not part of that release.
Deployment sequence
Governance deploys first because it controls subsequent deployment calls. The controller starts paused. The pool deploys once for the whole protocol, and every asset registers on that one pool.make <network> deploy runs the first five phases below: governance, controller,
price aggregator, pool, and position NFT. make <network> setup also wires
addresses, creates hubs and spokes, and configures markets and memberships.
1
Upload WASM and deploy governance
Uploads the pool 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 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 constructor sets governance as owner, both position limits to
POSITION_LIMIT_MAX = 5, and the minimum-borrow floor to $5 WAD. It starts
the controller paused. Controller administration uses owner authorization;
governance holds the role registry.3
Deploy the price aggregator
The owner calls
deploy_price_aggregator(wasm_hash) on governance. Because
the controller was deployed first, this also wires it atomically.
Without that wiring, priced calls fail with #27 AggregatorNotSet.
Replacing the address uses the timelocked SetPriceAggregator operation
through execute_self.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. Code upgrades use
UpgradePool(hash).5
Deploy the position NFT
Schedule and execute
DeployPositionNft(...) through the timelock.The account_id is the NFT token_id, and the
controller asks the NFT contract who owns an account on every authority
check. Missing NFT wiring can raise #53 PositionNftNotSet during account
creation; an ownership lookup can instead raise #24 AccountNotFound.
Metadata-only views do not establish ownership or prove NFT deployment.6
Wire the swap aggregator and accumulator
make <network> configure-controller sets the swap aggregator and the
revenue accumulator.The target requires both addresses. 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. Create each
hub (CreateHub) and target spoke (AddSpoke) before its listings. Governance
also exposes immediate GUARDIAN calls to create these empty containers.
Market registration and asset listing still require their scheduled operations.
Per asset:AdminOperation::CreateLiquidityPool— registers the hub asset on the existing central pool.AdminOperation::ConfigureAssetOracle— price reads work only after this.AdminOperation::AddAssetToSpoke(orEditAssetInSpoke) per spoke.
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 liquidation curves and Blend pools
Propose and execute
SetSpokeLiquidationCurve for each spoke. Propose and
execute ApproveBlendPool(pool) for each permitted Blend migration pool.9
Unpause
Complete the readiness checks, then schedule and execute
To increase the delay, use
AdminOperation::Unpause. Only pause is immediate.Mainnet setup leaves the protocol paused. Before unpausing, compare
governance’s get_min_delay() with timelock_min_delay_ledgers in
configs/networks.json. The mainnet unpause script rejects a delay below
that configured floor:make mainnet updateDelay <ledgers> and wait for
the timelocked change to execute before retrying unpause.The committed registry uses 12 ledgers for both networks. The script checks
that configured floor; it does not establish a production governance policy.
Verify the intended delay and deployed governance code before enabling users.Setup and resume targets
make <network> setup chains: deploy → configure-controller → market, spoke,
and Blend setup → rent prepay → status. On testnet it also unpauses; on
mainnet it leaves the protocol paused. make <network> resume re-runs everything
from configure-controller onward. Run individual targets only for partial
deploys or recovery.
Readiness checks
Complete these checks before enabling a network for users:- Review workspace tests, integration harness results, fuzz runs, and Certora profiles for the release.
- Verify contract links, ownership, and governance roles. Confirm pool and position-NFT deployment.
- Confirm every launched
HubAssetKeyhas a pool market, working oracle, and the required spoke listings and caps. - Confirm swap-aggregator and accumulator wiring, position limits, and the minimum-borrow floor.
- Check keeper coverage for launched markets and required storage entries.
- Complete a pause/unpause drill on the target network.
Upgrades
upgradeAll does not upgrade the position NFT or price aggregator. Run their
targets separately when the release requires them.
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
make <network> getAllMarkets and
make <network> getAllIndexes. make <network> info prints local registry
addresses; it does not verify deployed wiring.
Next
Governance
Timelock roles and the propose/execute lifecycle.
Configuration
Validation rules for risk, oracle, and spoke parameters.
Addresses
Network registry and contract-link verification.

