# Actions Reference

Every 1CT intent names one **action**. The action determines three things: the EIP-712 type name you
sign under, the ABI layout of `actionData`, and the permission bit required.

## Action list

| # | Action | Description | `actionData` ABI types |
| :--- | :--- | :--- | :--- |
| 0 | `MARKET_OPEN` | Open at market | `address,bool,address,address,uint96,uint128,uint128,uint128,uint128,uint24,uint96` |
| 1 | `MARKET_CLOSE` | Close fully | `bytes32,uint24` |
| 2 | `LIMIT_OPEN` | Place a limit order | same as `MARKET_OPEN` |
| 3 | `LIMIT_CANCEL` | Cancel a limit order | `bytes32` |
| 4 | `LIMIT_UPDATE_TP_SL` | Change a limit order's TP/SL | `bytes32,uint128,uint128` |
| 5 | `ADD_MARGIN` | Add margin | `bytes32,address,uint96` |
| 6 | `REMOVE_MARGIN` | Remove margin | `bytes32,uint96` |
| 7 | `UPDATE_TP_SL` | Change a position's TP/SL | `bytes32,uint128,uint128` |
| 8 | `BATCH_MARKET_CLOSE` | Close several positions | `bytes32[],uint24` |
| 9 | `PARTIAL_CLOSE` | Close part of a position | `bytes32,uint128,uint24` |
| 10 | `CANCEL_DECREASE_ORDER` | Cancel a TP/SL order | `bytes32` |
| 11 | `BATCH_CREATE_DECREASE_ORDERS` | Create TP/SL orders | `bytes32,(uint8,uint128,uint128,uint24)[]` |
| 12 | `BATCH_UPDATE_DECREASE_ORDERS` | Update TP/SL orders | `(bytes32,uint128,uint128,uint24)[]` |
| 13 | `CANCEL_ALL_DECREASE_ORDERS` | Cancel every TP/SL order on a position | `bytes32` |

The listed ABI types are the *parameters* — the encoding also prepends `trader`, then strips it. See
[Building actionData](/gasless/signing#building-actiondata); getting this wrong is the single most
common 1CT integration bug.

## Parameters

### Opening — actions 0 and 2

| Position | Field | Type | Notes |
| :--- | :--- | :--- | :--- |
| 0 | `pairBase` | `address` | Market identifier |
| 1 | `isLong` | `bool` | |
| 2 | `tokenIn` | `address` | ERC-20 only, no native MON |
| 3 | `lvToken` | `address` | Must match `tokenIn` — [see the table](/introduction/concepts#collateral-and-lvtoken) |
| 4 | `amountIn` | `uint96` | `tokenIn` decimals, margin plus open fee |
| 5 | `qty` | `uint128` | Base asset, **1e10** |
| 6 | `price` | `uint128` | **1e18** — worst acceptable price (market) or limit price (limit) |
| 7 | `stopLoss` | `uint128` | 1e18, 0 to disable |
| 8 | `takeProfit` | `uint128` | 1e18, 0 to disable |
| 9 | `broker` | `uint24` | Referral id. `0` credits the default broker — see [Brokers & Referrals](/introduction/brokers) |
| 10 | `extraFee` | `uint96` | Optional surcharge in `tokenIn`, 0 if unused |

::: warning
`extraFee` is part of `actionData` for 1CT opens, where the onchain path takes it as a separate
argument. There are **11** values, not 10.
:::

Both work exactly as they do onchain: the fee share costs the trader nothing, `extraFee` is charged
on top and paid to your broker's receiver. On the gasless path the trader's allowance to the Diamond
must cover `amountIn + extraFee + antiDdosFee` when the [execution fee](/gasless/fees) is charged in
the same token.

### Closing — actions 1, 8, 9

| Action | Values |
| :--- | :--- |
| `MARKET_CLOSE` | `[positionHash, broker]` |
| `BATCH_MARKET_CLOSE` | `[[hashA, hashB, …], broker]` |
| `PARTIAL_CLOSE` | `[positionHash, closeQty, broker]` — `closeQty` at 1e10 |

### Margin — actions 5, 6

| Action | Values |
| :--- | :--- |
| `ADD_MARGIN` | `[positionHash, tokenIn, amount]` — `amount` in `tokenIn` decimals |
| `REMOVE_MARGIN` | `[positionHash, lvAmount]` — `lvAmount` in **`lvToken`** decimals |

### TP/SL — actions 4, 7, 10, 11, 12, 13

| Action | Values |
| :--- | :--- |
| `LIMIT_UPDATE_TP_SL` | `[orderHash, takeProfit, stopLoss]` |
| `UPDATE_TP_SL` | `[positionHash, takeProfit, stopLoss]` |
| `CANCEL_DECREASE_ORDER` | `[orderHash]` — one order |
| `CANCEL_ALL_DECREASE_ORDERS` | `[positionHash]` — every order on the position |
| `BATCH_CREATE_DECREASE_ORDERS` | `[positionHash, DecreaseOrderInput[]]` |
| `BATCH_UPDATE_DECREASE_ORDERS` | `[DecreaseOrderUpdateInput[]]` |

```
DecreaseOrderInput        = (kind, triggerPrice, closeQty, broker)   // kind: 0 = TP, 1 = SL
DecreaseOrderUpdateInput  = (orderHash, triggerPrice, closeQty, broker)
```

::: warning Actions 10 and 13 take different hashes
Both encode as a single `bytes32`, so a swapped argument produces a perfectly valid signature and
a well-formed `actionData` — it fails onchain, not at submission. `CANCEL_DECREASE_ORDER` (10)
takes an **`orderHash`**; `CANCEL_ALL_DECREASE_ORDERS` (13) takes a **`positionHash`**.

The two failures do not even look alike: action 13 given an `orderHash` reverts `NonexistentTrade`,
while action 10 given a `positionHash` reverts `Error("TradingPortalFacet: not order owner")` — a
string revert, so a handler that only decodes custom errors will report it as `Unknown error`.
:::

`CANCEL_ALL_DECREASE_ORDERS` clears every decrease order attached to the position — take-profit legs
and the stop-loss alike — and emits one `DecreaseOrderCancelled` per order removed. The position
itself is untouched. On a position that exists but has no orders it succeeds and does nothing; on a
position that no longer exists it fails with [`NonexistentTrade`](/reference/errors), so cancelling
after a full close is an error, not a no-op.

Behaviour matches the onchain path — both kinds may be partial, and there may be several of each up
to the per-position cap. See [TP/SL Orders](/onchain/tpsl-orders).

## Referrals

Every fee-charging action carries a `broker` id, and the two opens additionally accept `extraFee`:

| Action | `broker` | `extraFee` |
| :--- | :---: | :---: |
| `MARKET_OPEN` (0), `LIMIT_OPEN` (2) | yes | yes |
| `MARKET_CLOSE` (1), `BATCH_MARKET_CLOSE` (8), `PARTIAL_CLOSE` (9) | yes | — |
| `BATCH_CREATE_DECREASE_ORDERS` (11), `BATCH_UPDATE_DECREASE_ORDERS` (12) | per order | — |
| Actions 3–7, 10, 13 | — | — |

Actions 3–7, 10 and 13 charge no fee, so there is nothing to share. Note that the close broker is
independent of the open broker — a broker integration has to pass its id on opens, closes, and every
TP/SL order. See [Brokers & Referrals](/introduction/brokers).

## EIP-712 type names

The `primaryType` is what distinguishes one action from another in a signature. The field list is
identical for all of them.

| Action | `primaryType` |
| :--- | :--- |
| 0 | `OneClickMarketOpen` |
| 1 | `OneClickMarketClose` |
| 2 | `OneClickLimitOpen` |
| 3 | `OneClickLimitCancel` |
| 4 | `OneClickLimitUpdateTpSl` |
| 5 | `OneClickAddMargin` |
| 6 | `OneClickRemoveMargin` |
| 7 | `OneClickUpdateTpSl` |
| 8 | `OneClickBatchMarketClose` |
| 9 | `OneClickPartialClose` |
| 10 | `OneClickCancelDecreaseOrder` |
| 11 | `OneClickBatchCreateDecreaseOrders` |
| 12 | `OneClickBatchUpdateDecreaseOrders` |
| 13 | `OneClickCancelAllDecreaseOrders` |

## Permission bits

Bit number equals action value.

| bit | Action | bit | Action |
| :--- | :--- | :--- | :--- |
| 0 | `MARKET_OPEN` | 7 | `UPDATE_TP_SL` |
| 1 | `MARKET_CLOSE` | 8 | `BATCH_MARKET_CLOSE` |
| 2 | `LIMIT_OPEN` | 9 | `PARTIAL_CLOSE` |
| 3 | `LIMIT_CANCEL` | 10 | `CANCEL_DECREASE_ORDER` |
| 4 | `LIMIT_UPDATE_TP_SL` | 11 | `BATCH_CREATE_DECREASE_ORDERS` |
| 5 | `ADD_MARGIN` | 12 | `BATCH_UPDATE_DECREASE_ORDERS` |
| 6 | `REMOVE_MARGIN` | 13 | `CANCEL_ALL_DECREASE_ORDERS` |

```ts
const canTrade = (1n << 0n) | (1n << 1n) | (1n << 9n)  // open, close, partial close
```

::: warning Action numbers are appended over time
An agent authorized with an explicit bitmask holds exactly the bits it was granted. When a new
action is added, existing agents do **not** gain it — they keep returning `skipReason = INVALID`
for that action until the trader calls
[`updateAgentPermissions`](/gasless/authorization#update-and-revoke). Agents authorized with the
`type(uint256).max` wildcard are unaffected and pick up new actions automatically.
:::

## Actions that require an indexed position

Some actions need the position to exist in the relayer's database so it can resolve which oracle
feeds to fetch. These are:

`MARKET_CLOSE` · `BATCH_MARKET_CLOSE` · `PARTIAL_CLOSE` · `ADD_MARGIN` · `REMOVE_MARGIN` ·
`BATCH_CREATE_DECREASE_ORDERS`

Submitting one of these for a position that was opened moments ago can return
`404 Position not found` — the indexer has not caught up yet. Retry with backoff, or wait for the
position to appear in [`getPositionsV4`](/onchain/reading-data#getpositionsv4) first. This is the
only submission failure that is not a `400`.

**Being addressed by `positionHash` is not the test** — needing the relayer to *look the position
up* is. The opens (0 and 2) consume oracle data too, but they carry `pairBase` and `lvToken` in
`actionData`, so their feeds resolve without any lookup and they can never return this error.

The following actions only read or remove stored order state, so they need no feeds at all and are
never subject to this, even though some of them take a `positionHash`:

`LIMIT_CANCEL` · `LIMIT_UPDATE_TP_SL` · `UPDATE_TP_SL` · `CANCEL_DECREASE_ORDER` ·
`BATCH_UPDATE_DECREASE_ORDERS` · `CANCEL_ALL_DECREASE_ORDERS`

In particular `CANCEL_ALL_DECREASE_ORDERS` (13) is `positionHash`-addressed but exempt: you can
clear a freshly-opened position's orders without waiting for the indexer.

Next: [Signing Intents](/gasless/signing).
