Execute perp action
Execution
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.
POST
Execute perp action
Single unified execution endpoint for every perp action. You forward the exact envelope returned by the matchingDocumentation Index
Fetch the complete documentation index at: https://docs.mobula.io/llms.txt
Use this file to discover all available pages before exploring further.
/2/perp/payloads/<action> call plus a signature that proves the caller approved this specific payload at this specific timestamp.
Flow
- Call
POST /2/perp/payloads/<action>(see left-hand nav) → receive{ action, dex, chainId, marketId?, transport, payloadStr }. - For Gains
transport: "evm-tx"only: parsepayloadStr, extractpayload.data, sign the EVM transaction locally → obtainsignedTx. - Sign the execute-v2 authentication message (see Authentication below).
POST /2/perp/execute-v2with the envelope fields, signature, timestamp, and (when required)signedTx.
Request Body
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.gains or lighter. Must match payloadStr.Chain of the action. Must match
payloadStr.Mobula market identifier. If provided, must match
payloadStr.marketId.offchain-api — server submits to the DEX off-chain API using the user’s signature.evm-tx — server broadcasts the user-signed EVM transaction supplied in signedTx. Must match payloadStr.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 inpayload.steps[].items[].dataand write the hex results intopayload.signedTxsas an array of strings.withdrawon Lighter — signpayload.MessageToSign, set the hex result onpayload.L1Sig, then deletepayload.MessageToSign.
Unix timestamp in milliseconds, within 30 seconds of server time.
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.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.Response
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:payloadStrin the signed message is the same string you put in the request body. If the flow requires injecting fields (depositsignedTxs, Lighter withdrawL1Sig), 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 equalfrom. Deposit/withdraw envelopes that don’t expose afromskip this check. - Signatures are single-use for 30 seconds after the timestamp.
Transport & signing matrix
Thetransport 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. |
Body
application/json
Available options:
withdraw, create-account, deposit, create-order, close-position, cancel-order, update-margin, edit-order Available options:
gains, lighter Available options:
offchain-api, evm-tx JSON-stringified canonical envelope. Envelope metadata must match the request fields.
Hex signature of api/2/perp/execute-v2-{timestamp}-{payloadStr}.
Hex-encoded single signed EVM transaction (Gains single-tx actions with transport evm-tx).