Skip to main content
Place orders using the create -> sign -> submit pattern described in Action Pattern.
The examples on this page use Hyperliquid (provider: 'hyperliquid'). The trading API is DEX-agnostic — replace the provider value with any supported DEX from getProviders().

Order Flow

createAction → actions[] → sign each typedData → executeAction → results[]

USER_AGENT mode (automatic signing)

In USER_AGENT mode, the PerpsClient handles the entire flow:
const result = await perps.placeOrder({
  address: userAddress,
  provider: 'hyperliquid',
  asset: { assetId: 'BTC', market: 'hyperliquid' },
  side: 'BUY',
  type: 'MARKET',
  size: '0.1',
  price: '95500.00',
  leverage: 10,
});

console.log(result.results);
// [{ action: 'placeOrder', success: true, orderId: '12345678' }]

USER mode (manual signing)

In USER mode, use the service functions to control each step:
import { createAction, executeAction } from '@lifi/perps-sdk';

// Step 1: Create order → get actions to sign
const { actions } = await createAction(client, {
  provider: 'hyperliquid',
  address: userAddress,
  action: 'placeOrder',
  params: {
    asset: { assetId: 'BTC', market: 'hyperliquid' },
    side: 'BUY',
    type: 'LIMIT',
    size: '0.1',
    price: '94000.00',
    timeInForce: 'GTC',
  },
});

// Step 2: Sign each action with wallet
const signedActions = await Promise.all(
  actions.map(async (a) => ({
    action: a.action,
    typedData: a.typedData,
    // Using viem walletClient:
    signature: await walletClient.signTypedData({ ...a.typedData }),
    // Or using a raw EIP-1193 provider:
    // signature: await provider.request({
    //   method: 'eth_signTypedData_v4',
    //   params: [address, JSON.stringify(a.typedData)],
    // }),
  }))
);

// Step 3: Submit signed payloads
const result = await executeAction(client, {
  provider: 'hyperliquid',
  address: userAddress,
  action: 'placeOrder',
  actions: signedActions,
});
Or use PerpsClient.submitSignedOrder():
const result = await perps.submitSignedOrder({
  provider: 'hyperliquid',
  address: userAddress,
  actions: signedActions,
});

Order Types

Market Order

Executes immediately at the best available price. The price field acts as a slippage limit.
await perps.placeOrder({
  address: userAddress,
  provider: 'hyperliquid',
  asset: { assetId: 'BTC', market: 'hyperliquid' },
  side: 'BUY',
  type: 'MARKET',
  size: '0.1',
  price: '95500.00',   // slippage limit
});

Limit Order

Rests on the orderbook until filled or cancelled.
await perps.placeOrder({
  address: userAddress,
  provider: 'hyperliquid',
  asset: { assetId: 'ETH', market: 'hyperliquid' },
  side: 'BUY',
  type: 'LIMIT',
  size: '1.0',
  price: '3150.00',
  timeInForce: 'GTC',  // Good til cancelled
});

Time in Force options

ValueDescription
GTCGood til cancelled (default)
IOCImmediate or cancel — unfilled portion is cancelled
POST_ONLYCancelled if it would immediately match (maker only)
GTTGood til time — requires expiresAt
// GTT example
await perps.placeOrder({
  address: userAddress,
  provider: 'hyperliquid',
  asset: { assetId: 'BTC', market: 'hyperliquid' },
  side: 'BUY',
  type: 'LIMIT',
  size: '0.1',
  price: '90000.00',
  timeInForce: 'GTT',
  expiresAt: '1779123456789', // Unix ms — must be in the future
});
API Reference: POST /actions