Skip to main content

Deployment Parameters

Every Zenex deployment is configured through a set of constructor parameters that define the protocol's fee structure, risk limits, oracle settings, and vault behavior. These parameters are typically defined in zenex-utils/deploy.json, which serves as the single source of truth for deployment configuration. Before executing any deployment, operators must review the contents of this file and confirm that all values are correct. Fee rates, leverage limits, and market parameters are critical to protocol safety and should be verified every time.

Factory

The factory is the entry point for deploying new trading and vault pairs. Its constructor accepts a single FactoryInitMeta struct.

ParameterTypeDescription
trading_hashBytesN<32>Compiled WASM hash for trading contracts
vault_hashBytesN<32>Compiled WASM hash for vault contracts
treasuryAddressProtocol-wide treasury contract address

These values are immutable after construction. The WASM hashes determine which contract code every pool will run. The treasury address determines where protocol fees are routed. Changing any of these requires deploying a new factory entirely.

Trading Contract

The trading contract is deployed through the factory's deploy function, which passes the following parameters to the trading constructor.

ParameterTypeDescription
ownerAddressAdmin address with permission to configure markets and parameters
tokenAddressSEP-41 collateral token (e.g., USDC)
vaultAddressPaired strategy vault (precomputed by factory)
price_verifierAddressPyth Lazer price verification contract
treasuryAddressProtocol treasury (inherited from factory)
feed_idu32Pyth Lazer feed id for the market, immutable for the life of the contract
exponenti32Price exponent for the feed, immutable, sets price_scalar = 10^-exponent. Must be between -18 and 0 inclusive, rejected otherwise
configConfigGlobal trading configuration (see below)

feed_id and exponent anchor the contract to a single oracle feed and its precision for good. There is no function to change either after deployment. A new feed or a re-scaled exponent means a fresh trading contract.

Trading Configuration

The Config struct carries the fee, sizing, risk, and vault-order parameters that govern a single market. It is set at construction and can later be replaced wholesale by the owner, subject to the same validation bounds described below. A borrowing-parameter change additionally requires a borrowing accrual in the same ledger, and a funding-parameter change first settles funding under the outgoing parameters.

Sizing and Fees

FieldScaleBoundsDescription
keeper_rateSCALAR_180 to MAX_KEEPER_RATE (50%)Keeper's share of trade fees and vault-order fill fees
min_position_notionaltoken-decgreater than 0, less than max_position_notionalMinimum notional size per position
max_position_notionaltoken-decgreater than min_position_notional, at most max_open_interestMaximum notional size per position
max_open_interesttoken-decat least max_position_notionalPer-side open-interest ceiling across all positions on that side
min_order_notionaltoken-decgreater than 0, at most min_position_notionalMinimum absolute notional per order, a dust floor sized so a full position can still close in a single order
min_order_collateraltoken-decgreater than 0Minimum absolute collateral per order, a dust floor
exec_feetoken-decat least 0, no upper boundFlat keeper execution fee escrowed with every trade and vault order at creation, paid to the keeper on fill and refunded on cancel, including the auto-cancel of resting decrease orders when a position fully closes
fee_domSCALAR_180 to MAX_FEE_RATE (1%), at least fee_non_domTrade fee charged to the dominant side
fee_non_domSCALAR_180 to MAX_FEE_RATE (1%)Trade fee charged to the non-dominant side
impact_scalartoken-decgreater than 0Sets the size-quadratic price-impact fee: a fill pays its size squared divided by the scalar, at a rate capped at 10% of the fill

Utilization Caps

Utilization is measured per side: each side's reserved open interest against half the vault balance. Opens are blocked once either side's utilization would exceed max_util_open, and the same half-vault capacity is the denominator of that side's borrowing curve.

FieldScaleBoundsDescription
max_util_openSCALAR_18greater than 0, at most MAX_UTIL (1000%)Opens are blocked once either side's utilization would exceed this cap. Also sets the per-side borrow-reserve denominator used by the borrowing curve
max_util_withdrawSCALAR_18at least max_util_open, at most MAX_UTIL (1000%)Redeem fills are blocked once either side's utilization exceeds this higher cap, holding a buffer of vault liquidity above the open cap

Margin and Liquidation

FieldScaleBoundsDescription
init_marginSCALAR_18MIN_MARGIN (0.1%) to MAX_MARGIN (50%), greater than maintenance_marginInitial margin requirement. Maximum leverage is 1 divided by init_margin
maintenance_marginSCALAR_18greater than liq_fee, less than init_marginMaintenance margin floor. A position becomes liquidatable once its equity falls below this fraction of notional
liq_feeSCALAR_180 to MAX_LIQ_FEE (25%), less than maintenance_marginFee charged on hard liquidations, where equity has fallen below this fraction of notional. Liquidations above that line charge no fee

Position Lifecycle

FieldScaleBoundsDescription
notional_locksecondsMIN_NOTIONAL_LOCK (15s) to MAX_NOTIONAL_LOCK (1 day)Decrease lock on newly added notional. The floor matches the price verifier's maximum staleness, so an accepted stale price can never open and close the same size

Borrowing

Borrowing follows a kink model over reserve utilization: a flat slope below the kink, a steeper one above it.

FieldScaleBoundsDescription
target_utilSCALAR_180 to just under 100%Kink utilization on the normalized reserve curve where the borrowing-rate slope steepens
borrow_rateSCALAR_18, per second0 to increased_borrow_rateBorrowing-rate slope below the kink
increased_borrow_rateSCALAR_18, per secondborrow_rate to MAX_BORROW_RATE (1000% APR)Borrowing rate at full utilization

Funding

Funding follows a velocity model: the rate accelerates as the skew between long and short open interest widens, and decays back once the skew narrows. See funding rate for the mechanics.

FieldScaleBoundsDescription
funding_increaseSCALAR_18, per second squared0 to MAX_FUNDING_RATEVelocity acceleration as the skew widens
funding_decreaseSCALAR_18, per second squared0 to MAX_FUNDING_RATEVelocity decay once the skew sits inside the decay band
threshold_stable_fundingSCALAR_180 to 100%Skew band within which the funding rate holds steady
threshold_decrease_fundingSCALAR_180 to threshold_stable_fundingSkew band within which the funding rate decays back toward zero
funding_minSCALAR_18, per second0 to funding_maxFloor on the rate actually charged
funding_maxSCALAR_18, per secondfunding_min to MAX_FUNDING_RATE (1000% APR)Hard cap on the funding rate

Risk and Auto-Deleveraging

Every rung below is a side's pending PnL measured as a fraction of half the vault balance. See auto-deleveraging for the mechanics.

FieldScaleBoundsDescription
adl_max_pnlSCALAR_18MIN_ADL_TRIGGER (45%) to max_pnl_trader, under 100%Threshold that arms auto-deleveraging for the crowded side
adl_clear_targetSCALAR_18MIN_ADL_CLEAR (40%) to adl_max_pnlTarget that auto-deleveraging closes positions down to once armed
max_pnl_traderSCALAR_18at least adl_max_pnl, under 100%Realized-profit haircut threshold. While a side's pending PnL exceeds this fraction, close payouts on that side scale down proportionally. Also caps each side's pending profit in the net PnL used to price vault shares

Vault Orders

A deposit order fills as soon as a keeper submits a verified price published strictly after the order's creation, subject to the order's own min_out bound, the min_deposit floor at creation, and the max_vault_balance ceiling. Only the redeem side has a cooldown, and redeem fills are additionally gated on pending trader PnL, with each side's pending profit measured against half the post-redeem vault balance. See providing liquidity for the mechanics.

FieldScaleBoundsDescription
redeem_lockseconds0 to MAX_REDEEM_LOCK (30 days)Cooldown from a vault order's creation before a redeem can fill
deposit_feeSCALAR_180 to MAX_FEE_RATE (1%)Fee charged on deposit fills, taken from the assets moved
redeem_feeSCALAR_180 to MAX_FEE_RATE (1%)Fee charged on redeem fills, taken from the proceeds
min_deposittoken-decgreater than 0, at most max_vault_balance divided by 100Minimum assets per deposit order, enforced at creation. Redeems have no minimum amount
max_pnl_withdrawSCALAR_18greater than 0, at most max_pnl_traderRedeem fills are blocked while either side's pending PnL exceeds this fraction of half the post-redeem vault balance
max_vault_balancetoken-decgreater than 0, at least min_deposit times 100Vault balance ceiling enforced on deposit fills

A configuration that violates any bound, or that gets the relative ordering between two fields wrong (such as fee_dom below fee_non_dom, or min_position_notional above max_position_notional), is rejected at construction and on every later set_config call.

Strategy Vault

The vault is also deployed through the factory, receiving its parameters from the same deploy call.

ParameterTypeDescription
nameStringVault share token name (e.g., "Zenex USDC Vault")
symbolStringVault share token symbol (e.g., "zUSDC")
assetAddressUnderlying collateral token (same as trading's token)
decimals_offsetu32Extra share-token decimals on top of the asset's own decimals, hardening the share price against donation-based inflation attacks. Capped at 10 by the token library
strategyAddressAuthorized trading contract (precomputed by factory)

The strategy parameter is the only address authorized to call the vault's strategy_deposit, strategy_redeem, and strategy_withdraw entry points. It is set to the precomputed trading contract address, which the factory calculates before either contract exists. LP sizing rules, including the minimum deposit amount, the redeem cooldown, and the vault-order fee, are not vault constructor parameters. They live on the trading contract's Config and are documented above under Vault Orders.

Price Verifier

The price verifier is deployed independently (not through the factory) and can be shared across multiple trading contracts.

ParameterTypeDescription
ownerAddressAdmin address for configuration updates
lazerAddressDeployed Pyth Lazer verification contract that holds the trusted-signer set
max_confidence_bpsu32Maximum allowed confidence interval in basis points
max_stalenessu64Maximum age of a price update in seconds (hard cap: 15s)

Signature verification is delegated entirely to the lazer contract, which checks the LE-ECDSA envelope against its own trusted-signer set and returns the verified inner payload. The price verifier only parses that payload and enforces confidence and staleness on it. The max_confidence_bps parameter rejects prices whose confidence interval exceeds the threshold, preventing the protocol from accepting highly uncertain oracle data. The max_staleness parameter rejects prices that are too old, ensuring the protocol operates on recent market data. The constructor and update_max_staleness both reject any value above MAX_STALENESS_SECONDS = 15. This cap is the floor for the trading contract's notional_lock, so an accepted stale price can never open and close the same size.

Treasury

The treasury is also deployed independently and referenced by the factory.

ParameterTypeDescription
ownerAddressAdmin with permission to adjust rate and withdraw funds
ratei128 (SCALAR_18)Fraction of protocol fees directed to treasury, bounded to 0 to 50%

The treasury rate determines what percentage of protocol revenue (base fees, impact fees, borrowing fees, vault-order fill fees, and forfeited liquidation remainders) is retained by the protocol versus flowing to vault depositors. A rate of 100_000_000_000_000_000 (10% in SCALAR_18) means the treasury keeps 10% of all protocol fees. This rate can be adjusted after deployment by the treasury owner via set_rate.

Governance

The governance contract is an optional timelock proxy deployed independently and assigned as the owner of a trading contract.

ParameterTypeDescription
ownerAddressAdmin who can queue parameter changes
delayu64Minimum seconds between queuing and executing a change

The delay must be non-zero and at most 60 days (60 * 24 * 3600 seconds). Values outside this range cause the constructor to panic. The delay can be changed after deployment via set_delay, and the new value takes effect only after the current delay has elapsed, so the timelock cannot be shortened instantly.

The delay parameter enforces a timelock on all configuration updates. When the owner queues a parameter change (such as updating Config), it specifies the target contract address at call time. The change cannot be executed until delay seconds have passed. This gives traders and LPs time to react to upcoming parameter changes. set_status is exempt from the timelock, allowing immediate emergency pauses.

Circular Dependency Resolution

The factory resolves a fundamental circular dependency between the trading contract and the vault. The trading contract needs the vault address at construction (to call total_assets and strategy_withdraw), while the vault needs the trading address at construction (to authorize strategy_withdraw calls from the trading contract).

The factory solves this by precomputing both addresses using deterministic salt derivation before deploying either contract. The vault is deployed first with the precomputed trading address as its strategy. The trading contract is deployed second with the precomputed vault address. No post-deployment linking or initialization step is required. Both contracts are fully configured from the moment they are instantiated.