> For the complete documentation index, see [llms.txt](https://docs.dimes.fi/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.dimes.fi/for-developers/order-types/partial-close.md).

# Partial Close (coming soon)

{% hint style="warning" %}
**Coming soon (beta).** Partial close is gated and rolling out — it is **not yet enabled in production**. The on-chain `requestPartialClose` call is accepted and indexed, but the backend does not yet act on it. Check the changelog or your integration contact before building against it.
{% endhint %}

A normal [close](/for-developers/wallet-integration/on-chain-integration.md#closing-a-position) exits the **entire** position. A **partial close** sells only a slice of it: the position stays open at a reduced size and the proportional capital is repaid. It is the symmetric counterpart to a full close — same owner-initiated on-chain trigger, same backend-automatic execution — but it is **non-terminal**: the position never leaves the `open` lifecycle.

This is useful for taking partial profit, trimming exposure, or de-risking a position you still want to hold.

## Enablement

Partial close is **gated and rolling out** — it is not yet enabled in production. When it is off, the on-chain `requestPartialClose` call is accepted and indexed but the backend does not act on it. Check the changelog or your integration contact before building against it. Everything below describes the enabled behaviour.

## How it works

One user-initiated on-chain transaction; the backend handles the rest, exactly like a full close.

```
User action                               Backend (automatic)
───────────                               ───────────────────
Call requestPartialClose(key, units) ───► Detect event
                                          Withdraw the slice off-vault
                                          Sell the slice on the exchange
                                          Repay proportional capital, finalize on-chain
                                          ──► position stays open at reduced size
```

1. **Request.** The position owner calls `requestPartialClose(positionKey, closeTokenUnits)` on the vault, naming how many position tokens to sell.
2. **Initiate.** The backend withdraws that slice from the vault and posts it as a sell order on the exchange. The position is now mid-close — its live size reflects only the **survivor** (the part not being sold).
3. **Finalize.** When the slice sells, the backend repays the proportional borrowed capital, books the proceeds, and returns the position to `open` at its new smaller size and lower leverage. If the slice only partially fills, the unsold remainder is returned to the position and only the sold fraction is booked. If nothing fills, the request is aborted and the position is restored to its original size — no change.

Throughout, the position's public `status` stays `open` (see [Position status](#position-status)).

## Calling the contract

Only the position owner (the wallet that called `createPosition`) can request a partial close. The `positionKey` is the `on_chain_position_key` field from `GET /positions`; `closeTokenUnits` is the number of position tokens to sell, which must be less than the position's current `current.position_token_units` and leave a non-trivial remainder.

{% tabs %}
{% tab title="ethers.js" %}

```javascript
const VAULT_ABI = ["function requestPartialClose(bytes32 positionKey, uint256 closeTokenUnits) external"];
const vault = new ethers.Contract(vaultAddress, VAULT_ABI, signer);

const tx = await vault.requestPartialClose(position.on_chain_position_key, closeTokenUnits);
await tx.wait();
```

{% endtab %}

{% tab title="viem" %}

> The SDK does not yet ship a partial-close transaction builder (`@dimes-dot-fi/sdk/contract` covers open and full close via `buildCreatePositionTx` / `buildRequestCloseTx`). Until it does, call `requestPartialClose` directly with viem using the fragment below.

```typescript
const vaultAbi = [
  {
    type: "function",
    name: "requestPartialClose",
    stateMutability: "nonpayable",
    inputs: [
      { name: "positionKey", type: "bytes32" },
      { name: "closeTokenUnits", type: "uint256" },
    ],
    outputs: [],
  },
] as const;

await walletClient.writeContract({
  address: vaultAddress,
  abi: vaultAbi,
  functionName: "requestPartialClose",
  args: [positionKey, closeTokenUnits],
});
```

{% endtab %}
{% endtabs %}

For smart-contract wallets, wrap `requestPartialClose` in the wallet's batch the same way you would `requestClose` (see [Smart Wallet Integration](/for-developers/wallet-integration/smart-wallet-integration.md)).

`requestPartialClose` is the **only** call your integration makes. `initiatePartialClose`, `finalizePartialClose` (and its `WithUnwind` / `WithReturn` variants) and `abortPartialClose` are all executed by the backend — do not call them yourself. The full canonical vault ABI (every function, event, and custom error) is published in the [UI repo](https://github.com/dimes-fi/dimes-demo-ui/blob/main/src/contract/canonical-leveraged-prediction-vault-v1-abi.json).

## Position status

A partial close never changes the position's public `status` — it stays `open` from request through finalize, just like a [force-unwind](/for-developers/api-and-events/websocket.md#event-types). After it completes, the `current.*` fields reflect the new, smaller position:

* `current.position_token_units` — fewer tokens held.
* `current.notional_*` — reduced exposure.
* `current.book_leverage_bps` / `current.market_leverage_bps` — lower leverage.
* `entry.*` — unchanged (the original signed open).

Positions mid-partial-close are returned by `GET /positions?status=open` and counted under the `active` state group.

Two basis-point fields let you tell an initial partial fill apart from a partial close:

* `entry.initial_fill_bps` — how much of the requested size was filled **at open**. Frozen at open; it does **not** move when the position is later partially closed. Use it for an "opened at X% of requested" badge.
* `current.remaining_bps` — how much of the originally opened size is **still held**. This drops after each partial close (e.g. `7000` = 70% left after a 30% close). That drop is by design — surface it as "remaining", not as a fill regression.

## History

Fetch the executed partial-close history for a position — tokens sold, average realized price, proceeds, capital repaid, owner payout, and the size/leverage left afterwards — from the partial-closes sub-resource:

```
GET /prediction-markets/positions/{position_id}/partial-closes
```

```typescript
const { data } = await client.getPositionPartialCloses(positionId);
```

Only partial closes that finalized on-chain are returned, in chronological order. See [Position partial closes](/for-developers/api-and-events/api-reference.md#position-partial-closes-sub-resource) for the full field reference.

## WebSocket events

Four events track the slice over its lifecycle. The payload of each is the full position object (open shape with `current` / `risk` / `fees` / `timing`), identical to the REST API — see [WebSocket Events](/for-developers/api-and-events/websocket.md). The `status` on every one is `open`.

| Event                              | When                                                                    |
| ---------------------------------- | ----------------------------------------------------------------------- |
| `position.partial_close_requested` | Owner's `requestPartialClose` was indexed; the slice is queued.         |
| `position.partial_close_initiated` | The slice was withdrawn and the sell order is in flight.                |
| `position.partial_closed`          | The partial close finalized; `current.*` now reflect the reduced size.  |
| `position.partial_close_aborted`   | The slice did not fill; the position was restored to its original size. |

```javascript
socket.on("position.partial_close_requested", (event) => {
  // Slice queued — optionally show a "reducing…" indicator
  updateOpenPosition(event.data);
});

socket.on("position.partial_closed", (event) => {
  // Position still open, now smaller — re-render with new current.* values
  updateOpenPosition(event.data);
});

socket.on("position.partial_close_aborted", (event) => {
  // Slice never filled — position unchanged
  updateOpenPosition(event.data);
});
```

`position.partial_closed` fires for every successful finalize, whether the slice fully sold, partially filled (unsold remainder returned), or was deleveraged by a concurrent risk unwind mid-slice — your integration treats them identically: re-render the now-smaller open position from `event.data`.
