# Core Concepts

## The Diamond

Every contract interaction goes through a single address — the LeverUp Diamond
(`0xea1b8E4aB7f14F7dCA68c5B214303B13078FC5ec`). Opening trades, limit orders, TP/SL orders,
one-click trading, pair configuration and all read functions are facets behind that one proxy.

Practically, this means: point every `readContract` / `writeContract` at the Diamond address, and
supply only the ABI fragment for the function you are calling.

## Pairs

A market is identified by its **`pairBase`** — an address that acts as the pair's identifier. It is
not a token you can hold or transfer; it is the key you pass to every trading and read function.

Pair addresses change as markets are listed and delisted. Fetch them from
[`GET /v1/pairs`](/api/pairs#list-pairs) rather than hardcoding.

Pairs carry a `status`:

| Status | Meaning |
| :--- | :--- |
| `AVAILABLE` | Open and close both allowed |
| `REDUCE_ONLY` | Only closes and reductions allowed |
| `CLOSE` | Market disabled |

Non-crypto markets (equities, indices, forex, commodities) also have trading hours. `GET /v1/pairs`
returns `nextOpen` / `nextClose`; attempting to trade a closed market reverts with `MarketClosed`.

## Collateral and lvToken

Every position references two tokens:

- **`tokenIn`** — what you deposit.
- **`lvToken`** — the protocol's settlement token for that position. Margin, fees and PnL are
  denominated in it.

The pairing is fixed:

| `tokenIn` | Required `lvToken` |
| :--- | :--- |
| USDC | LVUSD |
| LVUSD | LVUSD |
| Native MON (`0x0` address) | LVMON |
| WMON | LVMON |
| LVMON | LVMON |

In short: **USD-denominated collateral settles in LVUSD, MON-denominated collateral settles in
LVMON.** Passing a mismatched pair reverts.

You do not need to hold LV tokens to trade. Depositing USDC mints the LVUSD side internally. If you
already hold LVUSD or LVMON you can pass it directly as `tokenIn`, which skips a conversion step.

::: warning
[One-click trading](/gasless/overview) only accepts ERC-20 collateral. To trade MON via 1CT, wrap it
to WMON first.
:::

## Positions are merged slots

A position is keyed by `(user, pairBase, isLong, lvToken)`, not by trade:

```
positionHash = keccak256(abi.encode(user, pairBase, isLong, lvToken, "position.v1"))
```

Opening the same market and direction twice increases the existing position and blends the entry
price — you never get two BTC-long positions with the same collateral. Consequences:

- To reduce exposure without exiting, use [partial close](/onchain/closing#partial-close).
- A position can carry [multiple TP legs and one SL](/onchain/tpsl-orders).
- `positionHash` is stable and predictable, so you can compute it before the position exists.

::: info Legacy positions
Positions created before the merged-position upgrade use a random hash instead of the deterministic
key. They are full-close only and do not support TP/SL orders — they keep using the embedded
`updateTradeTp` / `updateTradeSl` fields. Read functions return both kinds; a position whose hash
does not match `getPositionHash(...)` for its own parameters is a legacy position.
:::

## Oracle updates

LeverUp settles against Pyth. Price-sensitive contract calls take an `OracleUpdateData` struct and a
native-token fee alongside it:

```solidity
struct OracleUpdateData {
    bytes[] pythPriceUpdateData;
    bytes[] pythProPriceUpdateData;
}
```

You do **not** fetch this from Pyth directly. Request it from the LeverUp backend, which selects the
right feeds for the pair and collateral and returns the fee to attach:

- Endpoint: [`POST /v1/oracle/price/updates/by-position`](/api/oracle#price-updates-by-position)
- Attach `updateFee + verifition_fee` as the transaction `value`.

Payloads are short-lived. Fetch immediately before sending the transaction, not at the start of a
long-running flow.

Calls that need oracle data: opening market and limit orders, removing margin, and closes. Calls
that only change stored values — updating a take-profit, cancelling an order — do not.

::: tip 1CT skips this entirely
When trading through [1CT](/gasless/overview), the relayer fetches oracle data for you. Intents never
carry oracle payloads.
:::

## Fees

| Fee | When | Paid in |
| :--- | :--- | :--- |
| Open fee | On open | `lvToken` |
| Close fee | On close | `lvToken` |
| Holding fee | Accrues while open | `lvToken` |
| Funding fee | Accrues while open, signed | `lvToken` |
| Oracle update fee | Per price-sensitive transaction | Native `MON` |
| Execution fee (1CT only) | Per intent | Configurable ERC-20, see [Execution Fee](/gasless/fees) |
| `extraFee` (optional) | Per open, if you set it | `tokenIn` — see [Brokers & Referrals](/introduction/brokers#extrafee) |

Fee rates are per pair and readable onchain via `getPairFeeConfig`. Some markets have a minimum
holding period (`minHoldingSeconds` in `GET /v1/pairs`) before a position can be closed; a position
also exposes `earliestCloseTime`.

Part of the open and close fee can be credited to a referral channel via the `broker` field that
appears on every fee-charging call. This does not change what the trader pays — see
[Brokers & Referrals](/introduction/brokers).

## Slippage

Fills are not at the raw oracle price. Each pair has a slippage model that adjusts the fill based on
open interest and trade size. This is why every open takes a `price` field — it is the **worst
acceptable price**, not a limit price:

- Long: the maximum price you will accept.
- Short: the minimum price you will accept.

If the adjusted fill falls outside it, the trade is refunded rather than filled. See
[Reading Data](/onchain/reading-data#pair-slippage-configuration) for reading the model's parameters.

For limit orders, `price` is the actual limit price.

## Keepers

Two things happen asynchronously, executed by protocol keepers:

1. **Fills.** A pending open or close is settled once the keeper delivers the oracle price.
2. **Triggers.** Limit orders, TP/SL orders and liquidations fire when the price condition is met.

Your transaction succeeding means the *request* was accepted, not that the position exists. Confirm
via [events](/onchain/events) or by polling [`getPositionsV4`](/onchain/reading-data#getpositionsv4).

Next: [Precision & Units](/introduction/precision), then pick an integration path —
[Onchain](/onchain/overview) or [Gasless](/gasless/overview).
