# Events

Every event is emitted by the Diamond (`0xea1b8E4aB7f14F7dCA68c5B214303B13078FC5ec`), so a single
address filter captures the whole protocol.

Because opens and closes are [two-phase](/onchain/overview#two-phase-execution), the events fall into
two groups: *request* events from the trader's own transaction, and *settlement* events from the
keeper's transaction. An integration that needs to know the real outcome must watch the settlement
events.

## Opening

| Event | Phase | Meaning |
| :--- | :--- | :--- |
| `MarketPendingTrade` | request | Market open accepted, collateral locked |
| `OpenPosition` | settlement | A new position slot was created |
| `PositionIncreased` | settlement | An existing slot grew, entry price blended |
| `PendingTradeRefund` | settlement | Open failed; collateral returned |
| `SetOpenTradeEarliestCloseTime` | settlement | Minimum holding period recorded |
| `ExtraFeeCharged` | settlement | [`extraFee`](/introduction/brokers#extrafee) paid to the broker's receiver |

```solidity
event MarketPendingTrade(address indexed user, bytes32 indexed tradeHash, OpenDataInput trade);

event OpenPosition(
    address indexed user,
    bytes32 indexed positionHash,
    bytes32 indexed sourceHash,
    PositionOpenUpdate update
);

event PositionIncreased(
    address indexed user,
    bytes32 indexed positionHash,
    bytes32 indexed sourceHash,
    PositionOpenUpdate update
);

event PendingTradeRefund(address indexed user, bytes32 indexed tradeHash, Refund refund);

event ExtraFeeCharged(
    uint24 indexed brokerId,
    address token,      // lvToken — extraFee is converted before payout
    uint96 amount,      // lvToken decimals
    address user,
    bytes32 tradeHash
);
```

`sourceHash` links back to the request: the pending trade hash for a market order, or the order hash
for a limit order.

```solidity
enum PositionOpenSource { MARKET, LIMIT }

struct PositionOpenUpdate {
    PositionOpenSource source;
    bytes32 sourceHash;
    uint128 addedQty;         // 1e10
    uint128 addedEntryPrice;  // 1e18, the fill price of this leg
    OpenTradeEventV2 position; // the position after the update
}
```

::: warning
There is no `OpenMarketTrade` event. If you are migrating from an older integration, replace it with
`OpenPosition` / `PositionIncreased`.
:::

## Closing

| Event | Phase | Meaning |
| :--- | :--- | :--- |
| `CloseTradeRequested` | request | Close accepted |
| `ClosePosition` | settlement | Position fully closed, slot removed |
| `PositionDecreased` | settlement | Partial close settled, remainder open |
| `ExecuteDecreaseOrderSuccessful` | settlement | A TP/SL order fired |
| `ExecuteCloseRejected` | settlement | Close could not be settled |
| `CloseTradeReceived` | settlement | Payout transferred to the trader |

```solidity
event CloseTradeRequested(
    address indexed user,
    bytes32 indexed positionHash,
    bytes32 indexed closeHash,
    bytes32 requestId,
    uint128 closeQty
);

event ClosePosition(
    address indexed user,
    bytes32 indexed positionHash,
    bytes32 indexed closeHash,
    uint128 closeQty,
    CloseInfo closeInfo,
    OpenTradeEventV2 ot
);

event PositionDecreased(
    address indexed user,
    bytes32 indexed positionHash,
    bytes32 indexed closeHash,
    uint128 closeQty,
    CloseInfo closeInfo,
    OpenTradeEventV2 ot
);

event ExecuteCloseRejected(
    address indexed user,
    bytes32 indexed tradeHash,
    ExecutionType executionType,
    uint128 execPrice,
    uint128 marketPrice
);
```

```solidity
enum ExecutionType { TP, SL, LIQ, ADL }

struct CloseInfo {
    uint128 closePrice;  // 1e18
    int96 fundingFee;    // lvToken decimals, signed
    uint96 closeFee;     // lvToken decimals
    int96 pnl;           // lvToken decimals, signed
    uint96 holdingFee;   // lvToken decimals
}
```

`ClosePosition` and `PositionDecreased` cover every kind of exit — manual close, take-profit,
stop-loss, liquidation and ADL. To distinguish them, look at whether the exit was accompanied by
`ExecuteDecreaseOrderSuccessful` (TP/SL order) or read `ExecutionType` from the paired execution
event.

::: info Legacy close events
`CloseTradeSuccessful`, `CloseTradeSuccessfulV2`, `ExecuteCloseSuccessful` and
`ExecuteCloseSuccessfulV2` are retained for positions created before the merged-position upgrade.
New integrations should index `ClosePosition` and `PositionDecreased`.
:::

## Limit orders

```solidity
event OpenLimitOrder(address indexed user, bytes32 indexed orderHash, OpenDataInput data);
event ExecuteLimitOrderSuccessful(address indexed user, bytes32 indexed orderHash);
event ExecuteLimitOrderRejected(address indexed user, bytes32 indexed orderHash, Refund refund);
event LimitOrderRefund(address indexed user, bytes32 indexed orderHash, Refund refund);
event CancelLimitOrder(address indexed user, bytes32 indexed orderHash);
event UpdateOrderTp(address indexed user, bytes32 indexed orderHash, uint256 oldTp, uint256 tp);
event UpdateOrderSl(address indexed user, bytes32 indexed orderHash, uint256 oldSl, uint256 sl);
```

A successful execution emits `ExecuteLimitOrderSuccessful` **and** an `OpenPosition` /
`PositionIncreased` whose `sourceHash` is the order hash.

## Position management

```solidity
event UpdateTradeTp(address indexed user, bytes32 indexed tradeHash, uint256 oldTp, uint256 tp);
event UpdateTradeSl(address indexed user, bytes32 indexed tradeHash, uint256 oldSl, uint256 sl);
event UpdateMargin(address indexed user, bytes32 indexed tradeHash, uint256 beforeMargin, uint256 margin);
```

## TP/SL orders

```solidity
event DecreaseOrderCreated(
    address indexed user,
    bytes32 indexed positionHash,
    bytes32 indexed orderHash,
    DecreaseOrderKind kind,   // 0 = TP, 1 = SL
    uint128 triggerPrice,
    uint128 closeQty,
    uint24 broker
);

event DecreaseOrderUpdated(bytes32 indexed orderHash, uint128 triggerPrice, uint128 closeQty, uint24 broker);

event DecreaseOrderCancelled(address indexed user, bytes32 indexed positionHash, bytes32 indexed orderHash);

event ExecuteDecreaseOrderSuccessful(
    address indexed user,
    bytes32 indexed orderHash,
    bytes32 indexed positionHash,
    DecreaseOrderKind kind,
    uint128 closeQty,
    CloseInfo closeInfo,
    OpenTradeEventV2 ot
);
```

`DecreaseOrderCancelled` fires on explicit cancellation, when a full close or liquidation sweeps the
position's remaining orders, and when a stale order is cleaned up at execution time.

## `OpenTradeEventV2`

The position snapshot carried by settlement events.

```solidity
struct OpenTradeEventV2 {
    address user;
    uint32 userOpenTradeIndex;
    uint40 holdingFeeRate;              // 1e12
    uint128 entryPrice;                 // 1e18
    uint128 qty;                        // 1e10
    address pairBase;
    address tokenPay;
    address lvToken;
    uint96 lvMargin;                    // lvToken decimals
    uint128 stopLoss;                   // 1e18
    uint128 takeProfit;                 // 1e18
    uint24 broker;
    bool isLong;
    uint32 timestamp;
    uint96 lvOpenFee;                   // lvToken decimals
    uint96 lvExecutionFee;              // lvToken decimals
    int256 longAccFundingFeePerShare;   // 1e18
    uint256 openBlock;
    int128 accruedFundingFee;
    uint128 accruedHoldingFee;
}
```

## Watching events

```ts
import { parseAbiItem } from 'viem'
import { publicClient, DIAMOND } from './config'

const unwatch = publicClient.watchEvent({
  address: DIAMOND,
  event: parseAbiItem(
    'event ClosePosition(address indexed user, bytes32 indexed positionHash, bytes32 indexed closeHash, uint128 closeQty, (uint128,int96,uint96,int96,uint96) closeInfo, (address,uint32,uint40,uint128,uint128,address,address,address,uint96,uint128,uint128,uint24,bool,uint32,uint96,uint96,int256,uint256,int128,uint128) ot)',
  ),
  args: { user: traderAddress },
  onLogs: (logs) => {
    for (const log of logs) {
      const { closeQty, closeInfo } = log.args
      console.log('closed', closeQty, 'pnl', closeInfo.pnl)
    }
  },
})
```

Filtering by `user` is cheap — it is an indexed topic on nearly every event. `positionHash` is
indexed too, so you can follow one position's whole life with a two-topic filter.

For historical data, the [REST API](/api/history) is generally easier than replaying logs: it
already joins events into position and trade records.

## Refund reasons

`PendingTradeRefund`, `ExecuteLimitOrderRejected` and `LimitOrderRefund` carry a `Refund` enum. See
[Error Reference → Refund reasons](/reference/errors#refund-reasons).
