# Oracle & Prices

Base URL: `https://service.leverup.xyz`

## Price updates by position

The oracle payload required by price-sensitive contract calls. Do not fetch this from Pyth directly —
this endpoint selects the correct feeds for the pair and collateral and returns the fee to attach.

```http
POST /v1/oracle/price/updates/by-position
Content-Type: application/json
```

### Request

```json
{
  "pairBase": "0xcf5a6076cfa32686c0df13abada2b40dec133f1d",
  "collateral": "0x754704Bc059F8C67012fEd69BC8A327a5aafb603",
  "blockChain": "MONAD",
  "options": {
    "includeEncodingData": true,
    "includeFee": true,
    "includePrice": true,
    "includePublishTime": false,
    "includeEncodingDataByOracleId": false,
    "includePriceSafety": false,
    "allowPartial": false
  }
}
```

| Field | Required | Notes |
| :--- | :--- | :--- |
| `pairBase` | yes | Market identifier |
| `collateral` | yes | The collateral token. For native MON, pass the **WMON** address. |
| `blockChain` | no | Defaults to `MONAD` |
| `options.includeEncodingData` | | Return the update payloads — required for transactions |
| `options.includeFee` | | Return `updateFee` and `verifition_fee` — required for transactions |
| `options.includePrice` | | Also return current prices |
| `options.includePublishTime` | | Also return publish timestamps |

### Response

```json
{
  "pythPriceUpdateData": ["0x..."],
  "pythProPriceUpdateData": ["0x..."],
  "updateFee": "123456789",
  "verifition_fee": "0",
  "pythCorePrice": { "0xe62d...": "100123450000000000000000" },
  "pythCorePublishTime": { "0xe62d...": 1785312000 },
  "failures": []
}
```

| Field | Notes |
| :--- | :--- |
| `pythPriceUpdateData` | Pass as `updateData.pythPriceUpdateData` |
| `pythProPriceUpdateData` | Pass as `updateData.pythProPriceUpdateData` |
| `updateFee` | Wei of native MON |
| `verifition_fee` | Additional verification fee, wei. **The spelling is intentional** — the field is named that way in the API. |
| `pythCorePrice` | Keyed by oracle id, 1e18 |
| `failures` | Non-empty when a feed could not be resolved |

Attach `updateFee + verifition_fee` as the transaction `value`:

```ts
const value = BigInt(data.updateFee ?? '0') + BigInt(data.verifition_fee ?? '0')
```

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

::: tip Not needed for 1CT
[Gasless trading](/gasless/overview) fetches oracle data server-side. Intents never carry payloads.
:::

## Latest pair prices

Current prices for several markets at once.

```http
POST /v1/oracle/price/pairs/latest
Content-Type: application/json
```

```json
{
  "pairBases": [
    "0xcf5a6076cfa32686c0df13abada2b40dec133f1d",
    "0xb5a30b0fdc5ea94a52fdc42e3e9760cb8449fb37"
  ],
  "blockChain": "MONAD"
}
```

### Response

```json
{
  "prices": {
    "0xcf5a6076cfa32686c0df13abada2b40dec133f1d": "100123450000000000000000",
    "0xb5a30b0fdc5ea94a52fdc42e3e9760cb8449fb37": "3456780000000000000000"
  },
  "failures": []
}
```

Prices are 1e18 strings, keyed by lowercase `pairBase`. Pairs that could not be resolved appear in
`failures` with a reason rather than failing the whole request:

```json
{ "failures": [{ "pairBase": "0xdead…", "reason": "latest oracle price not found" }] }
```

`pairBases` must not be empty. Duplicates are collapsed.

```ts
const res = await fetch(`${API}/v1/oracle/price/pairs/latest`, {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ pairBases: [btcBase, ethBase] }),
})

const { prices } = await res.json()
const btcUsd = Number(BigInt(prices[btcBase.toLowerCase()])) / 1e18
```

## Historical price

Price at a point in the past — used for 24h change calculations.

```http
GET /v1/oracle/price/pair-24h-ago?pair_base=0x…
GET /v1/oracle/price/pair-hours-ago?pair_base=0x…&hours_ago=6
```

| Parameter | Required | Notes |
| :--- | :--- | :--- |
| `pair_base` | yes | |
| `hours_ago` | no | 1–24, defaults to 24. Values outside the range return `400`. |
| `block_chain` | no | Defaults to `MONAD` |

### Response

```json
{
  "pairBase": "0xcf5a6076cfa32686c0df13abada2b40dec133f1d",
  "hoursAgo": 24,
  "blockChain": "MONAD",
  "oracleId": { "kind": "PYTH_CORE", "id": "0xe62d…", "oracle": null },
  "pricePoint": {
    "price": "98765430000000000000000",
    "publishTime": 1785225600,
    "capturedAt": 1785225605
  }
}
```

`price` is 1e18. `publishTime` is the oracle's timestamp; `capturedAt` is when LeverUp recorded it.

Returns `404` if no snapshot exists for that window.

```ts
async function priceChange24h(pairBase: string) {
  const [nowRes, thenRes] = await Promise.all([
    fetch(`${API}/v1/oracle/price/pairs/latest`, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ pairBases: [pairBase] }),
    }),
    fetch(`${API}/v1/oracle/price/pair-24h-ago?pair_base=${pairBase}`),
  ])

  const now = Number(BigInt((await nowRes.json()).prices[pairBase.toLowerCase()])) / 1e18
  const then = Number(BigInt((await thenRes.json()).pricePoint.price)) / 1e18

  return ((now - then) / then) * 100
}
```

## Oracle kinds

Feeds are identified by an `OracleIdInput`:

```json
{ "kind": "PYTH_CORE", "id": "0x…", "oracle": null }
```

`kind` is one of `NONE`, `PYTH_CORE`, `PYTH_PRO`, `DEX_ORACLE`. You rarely need to construct these —
the position-based endpoint resolves them for you.
