# Splitting a range down the middle was always wrong

> Splitting a Uniswap range used to happen at the geometric midpoint and nowhere else. Some ranges could not be split at all, and the failure said nothing.

- Source: https://defiloops.com/blog/splitting-a-range-down-the-middle-was-always-wrong
- Published: 2026-09-16
- Category: Architecture
- Tags: uniswap, liquidity, ticks, engineering
- Author: DeFiLoops

---
Splitting a liquidity position means turning one range into two that meet somewhere in the
middle. It is how you put more liquidity near the price and less out at the edges without
closing anything.

Our first version of it always cut at the **geometric midpoint**. You named the position, and
the split happened exactly halfway. That looked like a sensible default and it was two bugs
wearing a convenience.

## Bug one: the default was a strategy decision

Cutting halfway produces two equal children. That is a fine thing to want and a strange thing
to be given automatically, because the reason to split a range is usually that you **do not**
want the two halves to be equal.

You are concentrating liquidity where the price is and thinning it where it is not.
An equal cut is the one case where the split has changed nothing about the shape of your
exposure — it has only doubled your position count.

<Callout type="note" title="Unequal children are the point">
  Naming the tick is what makes the two sides different sizes, and different sizes are the
  reason to do it at all.
</Callout>

## Bug two: some ranges could not be split, and said nothing

Uniswap ranges are not continuous. They are built from ticks, and legal ticks are multiples of
the pool's spacing — which differs per fee tier, from 1 at the lowest through 10, 60 and 200 at
the highest.

So the midpoint of a range is only a legal place to cut when the range spans an **even** number
of spacings. Span an odd number and the exact middle lands *between* two ticks, where no
position can begin or end.

<Spec rows={[
  ['Even number of spacings', 'The midpoint is a real tick. The split works'],
  ['Odd number of spacings', 'The midpoint falls between two ticks. The position manager refuses it from inside the pool — with EMPTY returndata'],
]} />

That second row is the part that made it a bad failure rather than a limitation. Empty
returndata means no error code, no message, no reason: the call comes back having
refused, with nothing to read. From the outside it is indistinguishable from a dozen other
problems, and nothing in the message tells you the fix is *pick a different tick*.

## What changed

The tick is now a required parameter with no default at all. You name where the two children
meet, and the constraint is stated rather than assumed: it must be a multiple of the pool's
spacing, and it must lie strictly inside the range you are splitting.

Two consequences worth knowing:

- **Odd-span ranges are splittable now.** There was never anything wrong with them. The
  midpoint was the wrong question to ask.
- **A one-spacing range can never be split.** Not a bug, arithmetic: no tick lies strictly
  inside it. This matters because repositioning a range around the current price leaves it
  exactly one spacing wide, so a position that has just been recentred is not a candidate.

<Callout type="warn" title="Do not compute the tick yourself">
  Ask for your positions and the valid cut points are printed beside each one. The midpoint is
  only one of the choices, and it is not always a legal one — which is the whole story above,
  in one sentence.
</Callout>

## The levered version, and the same lesson

There are two split operations: one for a position you hold outright, and one for a position
that has been borrowed against. The levered one does the same work inside a flash loan — each
child re-borrows after it is minted, so the loan can be repaid in the same transaction — and it
carried the identical midpoint assumption, so it needed the identical fix.

Both also refuse a zero on the unwind minimums, because a burn settles at whatever the pool's
price is at that instant and those minimums are the only bound on it. That one is not about
convenience either.

## Why write this up

Because the interesting failure is not that a default was wrong. It is that a default made a
decision nobody was asked about, and then, in the cases where it could not be honoured,
declined to say so.

<Callout type="note" title="A different kind of splitting">
  This is about splitting a liquidity range. Splitting a large *trade* across pools is a
  separate question with a separate answer, and mostly the answer is that it does not help:
  [splitting a big trade usually does nothing](/blog/splitting-a-big-trade-usually-does-nothing).
</Callout>