> ## Documentation Index
> Fetch the complete documentation index at: https://docs.mobula.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Execute Perp Action

> Submit a canonical perpetual action (order, close, cancel, deposit, withdraw, …) previously built by /2/perp/payloads/<action>. Handles both off-chain DEX APIs and signed EVM transaction broadcast.

Single unified execution endpoint for every perp action. You forward the exact envelope returned by the matching `/2/perp/payloads/<action>` call plus a signature that proves the caller approved this specific payload at this specific timestamp.

<Warning>
  The `/2/perp/execute` (v1) endpoint is deprecated. All new integrations must use `/2/perp/execute-v2`.
</Warning>

## Flow

1. Call `POST /2/perp/payloads/<action>` (see left-hand nav) → receive `{ action, dex, chainId, marketId?, transport, payloadStr }`.
2. For Gains `transport: "evm-tx"` only: parse `payloadStr`, extract `payload.data`, sign the EVM transaction locally → obtain `signedTx`.
3. Sign the execute-v2 authentication message (see Authentication below).
4. `POST /2/perp/execute-v2` with the envelope fields, signature, timestamp, and (when required) `signedTx`.

### Request Body

<ParamField body="action" type="string" required>
  Canonical action name. One of `withdraw`, `create-account`, `deposit`, `create-order`, `close-position`, `cancel-order`, `update-margin`, `edit-order`. Must match the `action` inside `payloadStr`.
</ParamField>

<ParamField body="dex" type="string" required>
  `gains` or `lighter`. Must match `payloadStr`.
</ParamField>

<ParamField body="chainId" type="string" required>
  Chain of the action. Must match `payloadStr`.
</ParamField>

<ParamField body="marketId" type="string">
  Mobula market identifier. If provided, must match `payloadStr.marketId`.
</ParamField>

<ParamField body="transport" type="string" required>
  `offchain-api` — server submits to the DEX off-chain API using the user's signature.<br />
  `evm-tx` — server broadcasts the user-signed EVM transaction supplied in `signedTx`. Must match `payloadStr`.
</ParamField>

<ParamField body="payloadStr" type="string" required>
  JSON-stringified canonical envelope returned by `/2/perp/payloads/<action>`.

  **Envelope metadata** (`action`, `dex`, `chainId`, `transport`, `marketId`) must be forwarded unchanged — it is cross-checked against the request fields.

  **`payload` sub-fields are action-specific and some flows require mutating them** before signing execute-v2:

  * `deposit` (Lighter route with EVM bridge steps) — sign every tx in `payload.steps[].items[].data` and write the hex results into `payload.signedTxs` as an array of strings.
  * `withdraw` on Lighter — sign `payload.MessageToSign`, set the hex result on `payload.L1Sig`, then delete `payload.MessageToSign`.

  In those cases you re-stringify the envelope and use the **new** string everywhere: as this field, and in the execute-v2 signed message below.
</ParamField>

<ParamField body="timestamp" type="number" required>
  Unix timestamp in milliseconds, within 30 seconds of server time.
</ParamField>

<ParamField body="signature" type="string" required>
  Hex signature of the message `api/2/perp/execute-v2-{timestamp}-{payloadStr}`. The recovered signer address must match the `payload.data.from` address for actions that carry a `from` (e.g., `create-order`, `close-position`). Single-use: a second call with the same signature is rejected.
</ParamField>

<ParamField body="signedTx" type="string">
  Hex-encoded **single** signed EVM transaction. Used for Gains single-tx actions (`create-order`, `close-position`, `cancel-order`, `edit-order`, `update-margin`) whose response has `transport: "evm-tx"`. The server broadcasts it via `eth_sendRawTransaction` on `chainId`.

  Do **not** use this field for multi-tx flows (Lighter `deposit` bridge route) — those inject signed txs **into** `payloadStr` under `payload.signedTxs` instead.
</ParamField>

### Response

<ResponseField name="data" type="object">
  <Expandable title="data">
    <ResponseField name="success" type="boolean">Always `true` on success.</ResponseField>

    <ResponseField name="executionDetails" type="array">
      Per-leg execution details.

      <Expandable title="executionDetails[]">
        <ResponseField name="txHash" type="string">Transaction hash (EVM) or DEX-side identifier.</ResponseField>
        <ResponseField name="type" type="string">Execution kind (e.g., `evm-tx`, `lighter-order`).</ResponseField>
        <ResponseField name="status" type="string">DEX-reported status (e.g., `submitted`, `filled`).</ResponseField>

        <ResponseField name="orderStatuses" type="array">
          DEX-reported order-level statuses, when applicable.

          <Expandable title="orderStatuses[]">
            <ResponseField name="orderId" type="string">Order id (numeric string).</ResponseField>
            <ResponseField name="status" type="string">Order status.</ResponseField>
            <ResponseField name="type" type="string">Order type (`order`, `tp`, `sl`, …).</ResponseField>
          </Expandable>
        </ResponseField>
      </Expandable>
    </ResponseField>

    <ResponseField name="processId" type="string">
      UUID of the async tracking process. Present for long-running actions (typically `deposit`). Poll `/2/perp/check-process?processId=<id>` until it reports terminal status.
    </ResponseField>
  </Expandable>
</ResponseField>

### Errors

| Status | `message`                                                          | Cause                                                                      |
| ------ | ------------------------------------------------------------------ | -------------------------------------------------------------------------- |
| 400    | `payloadStr is not valid JSON`                                     | `payloadStr` is not parseable                                              |
| 400    | `invalid payloadStr`                                               | envelope fails schema validation (see `errors`)                            |
| 400    | `payloadStr metadata does not match request metadata`              | `action`/`dex`/`transport`/`chainId` mismatch between envelope and request |
| 400    | `payloadStr marketId does not match request marketId`              | `marketId` mismatch                                                        |
| 400    | `signedTx execution requires an EVM chainId, received "<chainId>"` | `transport: "evm-tx"` with a non-EVM chain                                 |
| 403    | `timestamp expired`                                                | `timestamp` more than 30s from server clock                                |
| 403    | `signature already used`                                           | Replay of an earlier request                                               |
| 403    | `signature signer does not match payload.from`                     | Signer address ≠ `payload.data.from` for actions that carry a `from`       |
| 500    | `Failed to broadcast signed transaction on <chainId>`              | RPC rejected `signedTx` (see `errors` list for details)                    |
| 500    | *DEX-specific reason*                                              | Adapter-level failure returned by the DEX                                  |

### Authentication

The execute-v2 signature binds **the exact payloadStr** to **the exact timestamp**:

```javascript theme={null}
const timestamp = Date.now();
const message = `api/2/perp/execute-v2-${timestamp}-${payloadStr}`;
const signature = await wallet.signMessage(message);
```

Rules:

* `payloadStr` in the signed message is the **same string** you put in the request body. If the flow requires injecting fields (deposit `signedTxs`, Lighter withdraw `L1Sig`), do the injection + re-stringify **before** signing — sign the final string, not the original.
* For actions whose envelope carries `payload.data.from` (Gains EVM txs, Lighter orders), the signer must equal `from`. Deposit/withdraw envelopes that don't expose a `from` skip this check.
* Signatures are single-use for 30 seconds after the timestamp.

### Transport & signing matrix

The `transport` returned by `/2/perp/payloads/<action>` is authoritative — forward it as-is. What the client must sign depends on both the DEX and the action:

| DEX     | Action                                                                          | Transport      | Client signing                                                                                                                                                                                                                                                                                 |
| ------- | ------------------------------------------------------------------------------- | -------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Lighter | `create-order`, `close-position`, `cancel-order`, `edit-order`, `update-margin` | `offchain-api` | execute-v2 signature only — forward `payloadStr` byte-for-byte                                                                                                                                                                                                                                 |
| Lighter | `create-account`                                                                | `offchain-api` | If the response carries `payload.MessageToSign`: sign it, set `payload.L1Sig`, delete `payload.MessageToSign`, re-stringify. Otherwise no mutation.                                                                                                                                            |
| Lighter | `deposit`                                                                       | `evm-tx`       | Sign **each** tx in `payload.steps[].items[].data` (each item's `data` is a tx object: `to`, `data`, `value`, `chainId`, `gas?`, fee fields). Inject results into `payload.signedTxs: string[]`, re-stringify, then sign execute-v2 over the new string. Do **not** send top-level `signedTx`. |
| Lighter | `withdraw`                                                                      | `offchain-api` | Sign `payload.MessageToSign`, set `payload.L1Sig`, delete `payload.MessageToSign`, re-stringify, then sign execute-v2 over the new string.                                                                                                                                                     |
| Gains   | `create-order`, `close-position`, `cancel-order`, `edit-order`, `update-margin` | `evm-tx`       | Read the tx object at `payload.data` (fields: `to`, **`callData`** *(not `data`)*, `value`, `chainId`, `nonce?`, `gas?`, fee fields). Sign as a type-2 EVM tx → submit as top-level `signedTx`. `payloadStr` is forwarded unchanged.                                                           |


## OpenAPI

````yaml POST /2/perp/execute-v2
openapi: 3.0.0
info:
  version: 1.0.0
  title: Mobula API
  description: >-
    Documentation of the Mobula API


    **Demo API**: The default server (demo-api.mobula.io) is a demo API with
    rate limits.

    For production use, please use api.mobula.io with an API key from
    https://admin.mobula.io
servers:
  - url: https://demo-api.mobula.io/api/
    description: Demo API (rate limited, for testing only)
  - url: https://api.mobula.io/api/
    description: Production API (requires API key)
security: []
tags:
  - name: V2 - Token
    description: Token details, price, security, ATH, and holder data
  - name: V2 - Market Data
    description: Market details, OHLCV history, and lighthouse metrics
  - name: V2 - Trades
    description: Token trades, enriched trades, and trade filters
  - name: V2 - Wallet
    description: Wallet positions, activity, trades, analysis, and labels
  - name: V2 - Assets
    description: Cross-chain asset details and price history
  - name: V2 - Swap
    description: Swap quoting and execution
  - name: V2 - Perps
    description: Perpetual futures quoting, execution, and positions
  - name: V2 - Bridge
    description: Cross-chain bridge quoting and intent status (Alpha Preview)
  - name: V2 - DeFi
    description: Bonding pools and pulse data
  - name: V2 - Search
    description: Universal fast search
  - name: V2 - Blockchains
    description: System metadata and chain listings
  - name: V1 - Market Data
    description: Market prices, history, sparklines, pairs, and multi-data
  - name: V1 - Wallet
    description: Wallet portfolio, transactions, history, and NFTs
  - name: V1 - Token
    description: First buyers
  - name: V1 - Trades
    description: Market trades by pair
  - name: V1 - Metadata
    description: Token metadata, categories, trendings, and news
  - name: V1 - Assets
    description: List all assets
  - name: V1 - Search
    description: Search for assets, tokens, and pairs
  - name: V1 - DeFi
    description: Bonding pool pulse data
  - name: V1 - Blockchains
    description: Blockchain listings, pairs, and stats
  - name: V1 - Webhooks
    description: Webhook management
  - name: V1 - Feed
    description: Custom feed creation
paths:
  /2/perp/execute-v2:
    post:
      tags:
        - V2 - Perps
      summary: Execute perp action
      description: >-
        Submit a canonical perpetual action previously built by
        /2/perp/payloads/<action>. Unified endpoint covering both off-chain DEX
        APIs and signed EVM transaction broadcast.
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                action:
                  type: string
                  enum:
                    - withdraw
                    - create-account
                    - deposit
                    - create-order
                    - close-position
                    - cancel-order
                    - update-margin
                    - edit-order
                dex:
                  type: string
                  enum:
                    - gains
                    - lighter
                chainId:
                  type: string
                marketId:
                  type: string
                transport:
                  type: string
                  enum:
                    - offchain-api
                    - evm-tx
                payloadStr:
                  type: string
                  description: >-
                    JSON-stringified canonical envelope. Envelope metadata must
                    match the request fields.
                timestamp:
                  type: number
                signature:
                  type: string
                  description: >-
                    Hex signature of
                    `api/2/perp/execute-v2-{timestamp}-{payloadStr}`.
                signedTx:
                  type: string
                  description: >-
                    Hex-encoded single signed EVM transaction (Gains single-tx
                    actions with transport evm-tx).
              required:
                - action
                - dex
                - chainId
                - transport
                - payloadStr
                - timestamp
                - signature
      responses:
        '200':
          description: Perp execute response
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                  data:
                    type: object
                    properties:
                      success:
                        type: boolean
                      executionDetails:
                        type: array
                        items:
                          type: object
                          properties:
                            txHash:
                              type: string
                            type:
                              type: string
                            status:
                              type: string
                            orderStatuses:
                              type: array
                              items:
                                type: object
                                properties:
                                  orderId:
                                    type: string
                                  status:
                                    type: string
                                  type:
                                    type: string
                      processId:
                        type: string
                    required:
                      - success
                required:
                  - success
                  - data

````