Contract 0d0e7ecab580ddb27a036a1323aa9126bb0cf3fc45fa546b7152bd52eea49329

← Back to Index 📥 Download WASM

Meta

rssdkver 21.7.7#5da789c50b18a4c2be53394138212fed56f0dfc4
rsver 1.96.0

Instances

  • CBC2XPFPI2DI7CVGRHDLVIHENIPFJJOGSUPAHLWZ43UTHK5TFJVIHJTC

Interface

Two-tier pause (L0-15). mode 1 = halt-open: opens and non-reduce-only order placement are blocked (#4), but closes, liquidations, stops and cross deposit/withdraw still work — traders can always de-risk and exit. mode 2 = full-freeze: even those are blocked symmetrically (#90); only cancel_order works, and it auto-degrades to mode 1 after 72h. mode must be 1 or 2 (use unpause for 0).

fn pause(env: soroban_sdk::Env, mode: u32) -> Result<(), NoetherError>

Resume fully (mode 0).

fn unpause(env: soroban_sdk::Env) -> Result<(), NoetherError>

Swap the running WASM in place; all storage (positions, orders, cross balances) is preserved across the upgrade.

⚠️ When the new wasm's MarketConfig has MORE fields than the stored one, every config-reading entry point traps until migrate_config rewrites it (Soroban decodes structs by exact field set). Pause → upgrade → migrate_config → unpause. See migrate_config.

fn upgrade(
    env: soroban_sdk::Env,
    new_wasm_hash: soroban_sdk::BytesN<32>,
) -> Result<(), NoetherError>

Force-realize a winning position at the oracle mark while ADL is active for its asset (L0-1). Permissionless — the on-chain gates (flag active + net winner) are the consensus; the keeper's ranking walk is advisory ordering. Pause-exempt (same class as liquidate). The trader is paid in full through the normal close settlement and optionally compensated from the buffer (adl_compensation_bps).

fn adl_close(
    env: soroban_sdk::Env,
    caller: soroban_sdk::Address,
    position_id: u64,
) -> Result

Get a specific order by ID.

fn get_order(env: soroban_sdk::Env, order_id: u64) -> Option

Liquidate an underwater position. Callable by anyone (keeper). Keeper receives liquidation reward.

Arguments

  • keeper - Address executing the liquidation (receives reward)
  • position_id - ID of position to liquidate

Returns

Keeper reward amount

Flow

  1. Verify position is liquidatable
  2. Calculate remaining equity and keeper reward
  3. Pay keeper their reward
  4. Transfer remaining collateral to Vault
  5. Update Vault accounting
fn liquidate(
    env: soroban_sdk::Env,
    keeper: soroban_sdk::Address,
    position_id: u64,
) -> Result

Rotate the admin (R-11: the multisig migration path). Both current and new admin must sign — a one-sided rotation to a mistyped address would brick every admin power forever.

fn set_admin(
    env: soroban_sdk::Env,
    new_admin: soroban_sdk::Address,
) -> Result<(), NoetherError>
fn initialize(
    env: soroban_sdk::Env,
    admin: soroban_sdk::Address,
    oracle_adapter: soroban_sdk::Address,
    vault: soroban_sdk::Address,
    usdc_token: soroban_sdk::Address,
    config: MarketConfig,
) -> Result<(), NoetherError>

Cancel a pending order. For limit orders, refunds the locked collateral.

Arguments

  • trader - Address of the trader (must own order)
  • order_id - ID of the order to cancel
fn cancel_order(
    env: soroban_sdk::Env,
    trader: soroban_sdk::Address,
    order_id: u64,
) -> Result<(), NoetherError>

Get a position by ID.

fn get_position(env: soroban_sdk::Env, position_id: u64) -> Option

L1-18: wire the referral registry. Every fee-bearing open then try-invokes referral.record_trade for the discount/payout split; unset (or a broken registry) is a silent no-op — fail-open, trading never blocks on referrals.

fn set_referral(
    env: soroban_sdk::Env,
    referral: soroban_sdk::Address,
) -> Result<(), NoetherError>

Apply funding to all positions (can be called periodically). Funding balances long/short interest:

  • If more longs than shorts: longs pay shorts
  • If more shorts than longs: shorts pay longs Apply per-market funding (L0-13). Permissionless, same no-arg signature the keeper already calls. Loops every listed pair and accrues each asset's own SIP-279 velocity-based rate (clamped), driven by that asset's TIME-WEIGHTED skew — so a quiet pair cannot collect carry off another pair's imbalance, and a keeper outage under-accrues (the dt cap kills the M-7 retroactive window) rather than pricing hours at one instant's skew. Returns FundingIntervalNotElapsed (#55) only when NOTHING was due, preserving the keeper's applied|not-due tri-state.
fn apply_funding(env: soroban_sdk::Env) -> Result<(), NoetherError>

Execute a triggered order (called by keeper). Checks if price condition is met and executes the order. Keeper receives a fee for successful execution.

Arguments

  • keeper - Address of the keeper executing the order
  • order_id - ID of the order to execute

Returns

Keeper reward amount

fn execute_order(
    env: soroban_sdk::Env,
    keeper: soroban_sdk::Address,
    order_id: u64,
) -> Result

Whether ADL is active for an asset.

fn is_adl_active(env: soroban_sdk::Env, asset: soroban_sdk::Symbol) -> bool

Open a new leveraged position.

Arguments

  • trader - Address of the trader
  • asset - Asset symbol (e.g., "XLM")
  • collateral - USDC collateral amount (7 decimals)
  • leverage - Leverage multiplier (1-10)
  • direction - Long or Short

Returns

The created Position

Flow

  1. Validate parameters
  2. Check Vault liquidity for potential payout
  3. Fetch price from oracle
  4. Calculate position size and liquidation price
  5. Transfer collateral from trader
  6. Deduct trading fee
  7. Store position
fn open_position(
    env: soroban_sdk::Env,
    trader: soroban_sdk::Address,
    asset: soroban_sdk::Symbol,
    collateral: i128,
    leverage: u32,
    direction: Direction,
    acceptable_price: i128,
) -> Result

Route a share of trading fees to a treasury address (insurance / operations funding). bps is the protocol share in basis points, capped at 50%.

fn set_fee_split(
    env: soroban_sdk::Env,
    treasury: soroban_sdk::Address,
    bps: u32,
) -> Result<(), NoetherError>

Set a stop-loss order on an existing position. Automatically closes the position when price reaches trigger to limit losses.

Arguments

  • trader - Address of the trader (must own position)
  • position_id - ID of the position to protect
  • trigger_price - Price at which to close (7 decimals)
  • slippage_tolerance_bps - Max allowed slippage in basis points
fn set_stop_loss(
    env: soroban_sdk::Env,
    trader: soroban_sdk::Address,
    position_id: u64,
    trigger_price: i128,
    slippage_tolerance_bps: u32,
) -> Result

Add collateral to an isolated position (L0-6), moving its liquidation price further away. Risk-REDUCING — deliberately NOT pause-gated (exit-only-pause philosophy, L0-15). Cross uses deposit_cross_margin.

fn add_collateral(
    env: soroban_sdk::Env,
    trader: soroban_sdk::Address,
    position_id: u64,
    amount: i128,
) -> Result<(), NoetherError>

Close an existing position.

Arguments

  • trader - Address of the trader (must own position)
  • position_id - ID of position to close

Returns

Final PnL amount (positive = profit, negative = loss)

Flow

  1. Verify ownership
  2. Apply any pending funding
  3. Calculate PnL at current price
  4. Settle with vault (handles fund transfer for wins)
  5. Transfer loss to vault if trader lost
  6. Return collateral +/- PnL to trader
fn close_position(
    env: soroban_sdk::Env,
    trader: soroban_sdk::Address,
    position_id: u64,
    acceptable_price: i128,
) -> Result

Per-market risk params for an asset (L0-12) — the single source of truth the keeper (simulate) and gateway (contractReader) read.

fn get_asset_risk(
    env: soroban_sdk::Env,
    asset: soroban_sdk::Symbol,
) -> Option

Overwrite the stored MarketConfig WITHOUT reading the old value.

A Soroban struct is stored as an exact field map, so after an upgrade that adds config fields the old value no longer decodes: get_config traps with UnexpectedSize and the market is bricked until this runs. This entry point never reads the old config, so it is callable in that state — the one-way door out of a config-shape upgrade. Admin-only; validated like initialize.

fn migrate_config(
    env: soroban_sdk::Env,
    config: MarketConfig,
) -> Result<(), NoetherError>

Emergency admin tool (2026-08 funding-counterparty upgrade): reset the funding baseline of the given positions to the current per-asset index, voiding their pending funding. Refuses unless the market is in full-freeze (mode 2) so nothing settles mid-reseed. Unknown ids are skipped; returns the number of positions touched.

fn reseed_funding(
    env: soroban_sdk::Env,
    ids: soroban_sdk::Vec,
) -> Result

L1-24: halt/resume trading in ONE market (admin). While halted, risk- increasing ops on the asset revert #92; closing/liquidating/cancelling still works — one broken feed no longer forces a whole-venue pause.

fn set_asset_halt(
    env: soroban_sdk::Env,
    asset: soroban_sdk::Symbol,
    halted: bool,
) -> Result<(), NoetherError>

Set per-market risk params (L0-12). Admin-only. The asset must be a listed pair and params must satisfy the MM=IM/2 ladder invariant. The first successful call stamps RiskEpochTs, activating the ladder: from then on risk-increasing ops on UNconfigured assets fail #88, while positions opened before the stamp keep the legacy MM.

fn set_asset_risk(
    env: soroban_sdk::Env,
    asset: soroban_sdk::Symbol,
    params: AssetRiskParams,
) -> Result<(), NoetherError>

Permissionless NAV freshener: recompute one asset's unrealized trader PnL at the current oracle price and push it to the vault, so NOE pricing tracks the market between trades. Returns the pushed value.

fn sync_asset_pnl(
    env: soroban_sdk::Env,
    asset: soroban_sdk::Symbol,
) -> Result

Current pause state as (mode, since). mode reflects the 72h auto-degrade logically (read-only, no persist).

fn get_pause_state(env: soroban_sdk::Env) -> (u32, u64)

L1-24 view: whether one market is halted.

fn is_asset_halted(env: soroban_sdk::Env, asset: soroban_sdk::Symbol) -> bool

Migrate the single global funding index into per-asset indices (L0-13). Admin-only, run inside the upgrade pause window after migrate_config + set_asset_risk. Seeds every pair's index at the legacy global cumulative value so every open position's pending funding is preserved by delta-continuity — no per-position writes.

fn migrate_funding(env: soroban_sdk::Env) -> Result<(), NoetherError>

Set a take-profit order on an existing position. Automatically closes the position when price reaches trigger to lock in profits.

Arguments

  • trader - Address of the trader (must own position)
  • position_id - ID of the position
  • trigger_price - Price at which to close (7 decimals)
  • slippage_tolerance_bps - Max allowed slippage in basis points
fn set_take_profit(
    env: soroban_sdk::Env,
    trader: soroban_sdk::Address,
    position_id: u64,
    trigger_price: i128,
    slippage_tolerance_bps: u32,
    limit_price: i128,
) -> Result

One-shot bootstrap of the live counters for a market upgraded in place: the counter keys do not exist in pre-upgrade storage, so decrements would saturate at zero and the counts would under-report forever. Admin derives the true open totals (a ledger-entry walk of Position/Order ids — the same walk the keeper runs) and writes them here, exactly once. The latch is a DEDICATED marker, NOT the counter key: closes and liquidations run even while paused and their saturating decrement CREATES the counter key at zero, which must not lock the admin out of seeding. Seeding overwrites whatever phantom value such an early transaction left behind. initialize() sets the marker, so a freshly deployed market can never be re-seeded.

fn seed_open_counts(
    env: soroban_sdk::Env,
    positions: u32,
    orders: u32,
) -> Result<(), NoetherError>

Permissionless solvency check: flips the asset's ADL flag on when pool coverage (buffer + LP USDC) falls under adl_trigger_ratio_bps of the payable winner uPnL, and off again above the clear ratio (hysteresis). Returns the flag. Pause-exempt, lenient price read — it must work in exactly the stress states it exists for.

fn check_adl_trigger(
    env: soroban_sdk::Env,
    asset: soroban_sdk::Symbol,
) -> Result

L1-18: rolling 14-day traded volume for a trader — the referral registry's create_code gate cross-reads this (min_code_volume). Read-only local window rotation, nothing persisted.

fn get_trader_volume(env: soroban_sdk::Env, trader: soroban_sdk::Address) -> i128

Place a limit entry order. Collateral is locked immediately when the order is placed. Order executes when price reaches trigger_price (based on trigger_condition).

Arguments

  • trader - Address of the trader
  • asset - Asset symbol (e.g., "BTC", "ETH", "XLM")
  • direction - Long or Short
  • collateral - USDC collateral to lock (7 decimals)
  • leverage - Leverage multiplier (1-10)
  • trigger_price - Price at which to execute (7 decimals)
  • trigger_above - true = execute when price >= trigger, false = when price <= trigger
  • slippage_tolerance_bps - Max allowed slippage in basis points (e.g., 100 = 1%)

Returns

The created Order Place a limit order.

Time-in-Force (time_in_force parameter)

  • 0 = GTC (Good Till Cancel) — stays open until triggered or cancelled
  • 1 = IOC (Immediate Or Cancel) — executes immediately if conditions met, else cancelled
  • 2 = Post Only — rejected if it would execute immediately (guarantees maker fee)

Reduce Only (bit 8 of time_in_force)

If `time_in_

fn place_limit_order(
    env: soroban_sdk::Env,
    trader: soroban_sdk::Address,
    asset: soroban_sdk::Symbol,
    direction: Direction,
    collateral: i128,
    leverage: u32,
    trigger_price: i128,
    trigger_above: bool,
    slippage_tolerance_bps: u32,
    time_in_force: u32,
) -> Result

Remove collateral from an isolated position (L0-6). Risk-INCREASING — pause-gated, IM-floor gated, and gated on a STRICT (fresh) oracle read so a stale/deviant print can't authorize margin extraction.

fn remove_collateral(
    env: soroban_sdk::Env,
    trader: soroban_sdk::Address,
    position_id: u64,
    amount: i128,
) -> Result<(), NoetherError>

L1-30: proceeds a full isolated close/liquidation paid the trader, keyed by position_id (temporary storage; 0 if unknown/expired). A vault_factory owner reads this to reconcile a keeper-executed protective close.

fn get_closed_proceeds(env: soroban_sdk::Env, position_id: u64) -> i128

Mark-to-market equity of one position (L0-20), for vault_factory NAV: max(0, collateral + uPnL − pending funding) at the lenient close price. Fail-closed — a missing position (#20) or an oracle failure propagates so the factory reverts (ValuationUnavailable) rather than mispricing.

fn get_position_equity(
    env: soroban_sdk::Env,
    position_id: u64,
) -> Result

Trader's live fee-tier standing (C2 restore — the 128 KB WASM limit gives the view room again; the OrderPanel fee preview reads it directly instead of estimating from the gateway). Read-only: the rolling window rotates on a LOCAL copy, nothing is persisted.

fn get_trader_fee_info(
    env: soroban_sdk::Env,
    trader: soroban_sdk::Address,
) -> TraderFeeInfo

Open a cross-margin position. Single transaction: if pool balance is insufficient, automatically transfers the shortfall from trader's wallet. Works like Binance cross-margin: no separate deposit step needed.

fn open_position_cross(
    env: soroban_sdk::Env,
    trader: soroban_sdk::Address,
    asset: soroban_sdk::Symbol,
    collateral: i128,
    leverage: u32,
    direction: Direction,
    acceptable_price: i128,
) -> Result

Place a trailing stop order attached to a position. Tracks peak price, triggers when price drops trailing_percent from peak.

fn place_trailing_stop(
    env: soroban_sdk::Env,
    trader: soroban_sdk::Address,
    position_id: u64,
    trailing_percent_bps: u32,
    slippage_tolerance_bps: u32,
) -> Result

Close a cross-margin position. PnL returns to cross-margin pool, not trader wallet.

fn close_position_cross(
    env: soroban_sdk::Env,
    trader: soroban_sdk::Address,
    position_id: u64,
    acceptable_price: i128,
) -> Result

Deposit USDC into cross-margin pool. Funds are held by the Market contract and shared across all cross positions.

fn deposit_cross_margin(
    env: soroban_sdk::Env,
    trader: soroban_sdk::Address,
    amount: i128,
) -> Result<(), NoetherError>

Update trailing stop peak price for a specific order. Called by keeper for each trailing stop order per cycle.

fn update_trailing_peak(
    env: soroban_sdk::Env,
    order_id: u64,
) -> Result

Withdraw USDC from cross-margin pool. Only withdrawable if free margin remains sufficient after withdrawal.

fn withdraw_cross_margin(
    env: soroban_sdk::Env,
    trader: soroban_sdk::Address,
    amount: i128,
) -> Result<(), NoetherError>

Close part of an isolated position (L0-6). close_size is NOTIONAL USD (same unit as Position.size). Settles only the closed portion's pnl + funding, refunds its pro-rata collateral (minus the closed portion's loss), releases its OI reservation, and shrinks the position in place — the collateral/size ratio is preserved so the stored liquidation_price stays valid and entry_cumulative_funding is untouched (only the closed portion's funding settled). Returns pnl on the closed portion. A full-size close delegates to close_position.

fn close_position_partial(
    env: soroban_sdk::Env,
    trader: soroban_sdk::Address,
    position_id: u64,
    close_size: i128,
) -> Result

Place a stop-limit order. Phase 1: waits for price to hit trigger_price (stop). Phase 2: once triggered, acts as limit order at limit_price. Place a stop-limit order with optional time-in-force and reduce-only flags. See place_limit_order for time_in_force encoding details. Note: IOC and PostOnly apply to the limit phase (phase 1), not the stop phase.

fn place_stop_limit_order(
    env: soroban_sdk::Env,
    trader: soroban_sdk::Address,
    asset: soroban_sdk::Symbol,
    direction: Direction,
    collateral: i128,
    leverage: u32,
    trigger_price: i128,
    limit_price: i128,
    trigger_above: bool,
    slippage_tolerance_bps: u32,
    time_in_force: u32,
) -> Result

A trader's open position ids — the per-trader index the contract keeps anyway for cross-margin aggregation. This is the point-read replacement for the deleted global scan: a wallet lists ITS positions in one bounded call (MAX_OPEN_POSITIONS_PER_TRADER) instead of walking every id on the market, and it works against any market without a gateway in the middle.

fn get_trader_position_ids(
    env: soroban_sdk::Env,
    trader: soroban_sdk::Address,
) -> soroban_sdk::Vec

Liquidate a cross-margin account. Closes ALL cross positions. Called by keeper when account equity < maintenance margin.

fn liquidate_cross_account(
    env: soroban_sdk::Env,
    keeper: soroban_sdk::Address,
    trader: soroban_sdk::Address,
) -> Result

Get cross-margin balance for a trader (pool balance only).

fn get_cross_margin_balance(
    env: soroban_sdk::Env,
    trader: soroban_sdk::Address,
) -> i128

Get cross-margin position IDs for a trader.

fn get_cross_margin_positions(
    env: soroban_sdk::Env,
    trader: soroban_sdk::Address,
) -> soroban_sdk::Vec

Imports

WebAssembly Text (WAT) ▶