Skip to content

Market Orders

Open a position at the current oracle price.

openMarketTradeV2

solidity
function openMarketTradeV2(
    OpenDataInput memory data,
    OracleUpdateData calldata updateData,
    uint96 extraFee
) external payable returns (bytes32 tradeHash);

Returns the pending trade hash, not the position hash. The position hash is deterministic and separate — see Positions are merged slots.

OpenDataInput

solidity
struct OpenDataInput {
    address pairBase;   // market identifier
    bool isLong;
    address tokenIn;    // collateral you deposit
    address lvToken;    // settlement token, see below
    uint96 amountIn;    // tokenIn decimals
    uint128 qty;        // base asset amount, 1e10
    uint128 price;      // worst acceptable price, 1e18
    uint128 stopLoss;   // 1e18, 0 to disable
    uint128 takeProfit; // 1e18, 0 to disable
    uint24 broker;      // referral id, 0 for the default broker
}
FieldNotes
pairBaseFrom GET /v1/pairs. Do not hardcode.
tokenIn / lvTokenMust be a valid pair — see the table.
amountInMargin plus the open fee. Under-funding reverts.
qtyBase asset, 1e10. $100 of BTC at $100k10000000.
priceSlippage bound, not a limit price. Long: maximum acceptable. Short: minimum acceptable.
stopLoss / takeProfitOptional, set at open. Can be changed later, or replaced by TP/SL orders.
brokerReferral channel for this trade's fee. 0 credits the default broker, not "no broker" — see Brokers & Referrals.

updateData and extraFee

updateData is the oracle payload from POST /v1/oracle/price/updates/by-position. Attach updateFee + verifition_fee as the transaction value.

extraFee is an optional surcharge you add on top of the trade, denominated in tokenIn and paid to your broker's receiver when the position fills. Pass 0 unless you operate a broker integration.

When non-zero:

  • It is transferred from the trader in addition to amountIn, so the allowance must cover amountIn + extraFee.
  • It goes to the receiver of whatever broker id you passed — pairing extraFee > 0 with broker: 0 pays the default broker, not you.
  • It is returned along with the collateral if the open is refunded.

See Brokers & Referrals.

Example

ts
import { parseUnits } from 'viem'
import { publicClient, walletClient, account, DIAMOND, USDC, LVUSD } from './config'
import { fetchOracleUpdate } from './oracle'

const OPEN_MARKET_ABI = [{
  type: 'function',
  name: 'openMarketTradeV2',
  stateMutability: 'payable',
  inputs: [
    {
      name: 'data', type: 'tuple', components: [
        { name: 'pairBase', type: 'address' },
        { name: 'isLong', type: 'bool' },
        { name: 'tokenIn', type: 'address' },
        { name: 'lvToken', type: 'address' },
        { name: 'amountIn', type: 'uint96' },
        { name: 'qty', type: 'uint128' },
        { name: 'price', type: 'uint128' },
        { name: 'stopLoss', type: 'uint128' },
        { name: 'takeProfit', type: 'uint128' },
        { name: 'broker', type: 'uint24' },
      ],
    },
    {
      name: 'updateData', type: 'tuple', components: [
        { name: 'pythPriceUpdateData', type: 'bytes[]' },
        { name: 'pythProPriceUpdateData', type: 'bytes[]' },
      ],
    },
    { name: 'extraFee', type: 'uint96' },
  ],
  outputs: [{ name: 'tradeHash', type: 'bytes32' }],
}] as const

export async function openLong(
  pairBase: `0x${string}`,
  notionalUsd: number,
  marginUsdc: string,
  markPrice: number,
  slippagePct = 1,
) {
  const oracle = await fetchOracleUpdate(pairBase, USDC)

  const qty = parseUnits((notionalUsd / markPrice).toFixed(10), 10)
  const worstPrice = parseUnits(
    (markPrice * (1 + slippagePct / 100)).toFixed(18),
    18,
  )

  const hash = await walletClient.writeContract({
    address: DIAMOND,
    abi: OPEN_MARKET_ABI,
    functionName: 'openMarketTradeV2',
    args: [
      {
        pairBase,
        isLong: true,
        tokenIn: USDC,
        lvToken: LVUSD,
        amountIn: parseUnits(marginUsdc, 6),
        qty,
        price: worstPrice,
        stopLoss: 0n,
        takeProfit: 0n,
        broker: 0,
      },
      oracle.updateData,
      0n,
    ],
    value: oracle.value,
  })

  return publicClient.waitForTransactionReceipt({ hash })
}

For a short, set isLong: false and invert the slippage bound:

ts
const worstPrice = parseUnits((markPrice * (1 - slippagePct / 100)).toFixed(18), 18)

Using native MON as collateral

ts
const oracle = await fetchOracleUpdate(pairBase, WMON)   // quote against WMON
const amountIn = parseUnits('1', 18)

await walletClient.writeContract({
  address: DIAMOND,
  abi: OPEN_MARKET_ABI,
  functionName: 'openMarketTradeV2',
  args: [
    { /* ... */ tokenIn: '0x0000000000000000000000000000000000000000', lvToken: LVMON, amountIn, /* ... */ },
    oracle.updateData,
    0n,
  ],
  value: amountIn + oracle.value,   // collateral travels as msg.value
})

No approval is needed for native MON.

After sending

The transaction emits MarketPendingTrade and locks your collateral. A keeper then fills it:

  • FilledOpenPosition (new slot) or PositionIncreased (existing slot).
  • RefundedPendingTradeRefund with a reason code.

Poll getPositionsV4 or subscribe to events.

Common reverts

ErrorCause
MarketClosed / PairClosedOutside trading hours, or the pair is disabled
UnsupportedMarginTokentokenIn / lvToken mismatch
InvalidAmountamountIn below the minimum, or below the open fee
InsufficientLiquidityPoolPool cannot back the position size
BelowDegenModeMinLeverageResulting leverage below the pair's minimum

Full list: Error Reference.

Trading perpetuals involves risk. Nothing here is financial advice.