> ## Documentation Index
> Fetch the complete documentation index at: https://shield.fi/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Manage liquidity with the TypeScript SDK

> Preview, mint, manage, collect, and close a Shield Swap liquidity position.

A Shield Swap position has public economic state and a `PositionNFT` record. The record authorizes position changes. Its token ID remains stable while replacement records are issued through the position lifecycle.

## Preview a position

Choose a pool and read balances for both tokens. `previewMint` aligns the range to tick spacing, calculates the supported liquidity, and reports the token amounts the position would consume.

```typescript theme={"languages":{"custom":["/languages/leo.tmLanguage.json"]}}
const { data: pools } = await client.api.getPools({ limit: 20 })
const market = pools.find((pool) => pool.token0_info && pool.token1_info)
if (!market) throw new Error("no pool with token metadata")

const balances = await client.getBalances({
  tokens: [market.token0, market.token1],
})

const preview = await client.previewMint({
  poolKey: market.key,
  amount0Desired: (balances[market.token0]?.private ?? 0n) / 10n,
  amount1Desired: (balances[market.token1]?.private ?? 0n) / 10n,
  rangePercent: 5,
})

if (preview.liquidity === 0n) {
  throw new Error("deposit is too small for this range")
}
```

You can pass `tickLower` and `tickUpper` together instead of `rangePercent`. `preview.inRange` tells you whether the position will earn fees at the current tick. State can move between preview and finalization, so set minimum amounts when the application needs tighter deposit protection.

## Mint

Resolve token programs, then use the aligned range and consumed amounts returned by the preview.

```typescript theme={"languages":{"custom":["/languages/leo.tmLanguage.json"]}}
const token0 = await client.tokenData(market.token0)
const token1 = await client.tokenData(market.token1)
if (!token0.ammTokenProgram || !token1.ammTokenProgram) {
  throw new Error("pool token program is missing")
}

const imports = await client.resolveDexImports({
  tokenPrograms: [token0.ammTokenProgram, token1.ammTokenProgram],
})

const minted = await client.mint({
  poolKey: market.key,
  tickLower: preview.tickLower,
  tickUpper: preview.tickUpper,
  amount0Desired: preview.amount0,
  amount1Desired: preview.amount1,
  recipient: account.address,
  withdrawal: account.address,
  imports,
})

if (!minted.positionTokenId) {
  throw new Error("position token ID is not available yet")
}

const positionTokenId = minted.positionTokenId
```

`recipient` receives the `PositionNFT` and controls the position. `withdrawal` receives every later `collect` payout. The withdrawal address is fixed at mint and cannot be changed. Confirm both addresses before submission. They may be different from each other and from the transaction signer.

Mint creates the first `PositionNFT`. Store its token ID and wait for the new record before offering another position action.

## Find owned positions

```typescript theme={"languages":{"custom":["/languages/leo.tmLanguage.json"]}}
const positions = await client.getOwnedPositions()

const position = await client.getOwnedPosition({
  positionTokenId,
})
```

Mapping state may appear before the replacement record reaches the scanner. Poll for the expected record after each accepted action.

## Increase liquidity

An increase spends the current position record and both token records. It keeps the same position token ID and issues an updated `PositionNFT`.

```typescript theme={"languages":{"custom":["/languages/leo.tmLanguage.json"]}}
await client.increaseLiquidity({
  poolKey: market.key,
  positionTokenId,
  amount0Desired: 100_000n,
  amount1Desired: 200_000n,
  imports,
})
```

The tick range and withdrawal address do not change. A local-key client can select records through its scanner. A wallet client must supply its position and token record requests.

## Decrease and collect

`decreaseLiquidity` moves principal and accrued fees into the position's owed balances. It does not transfer token records to the LP.

```typescript theme={"languages":{"custom":["/languages/leo.tmLanguage.json"]}}
await client.decreaseLiquidity({
  poolKey: market.key,
  positionTokenId,
  liquidityToRemove: 500_000n,
})

await client.collect({
  poolKey: market.key,
  positionTokenId,
  amount0Requested: 100_000n,
  amount1Requested: 200_000n,
  imports,
})
```

Collect sends token records to the immutable withdrawal address stored in the `PositionNFT`. It has no recipient parameter. Increase, decrease, and collect each consume the current position record and issue a replacement, so wait for that replacement before the next action.

## Close the position

Burn succeeds after liquidity and both owed balances reach zero.

```typescript theme={"languages":{"custom":["/languages/leo.tmLanguage.json"]}}
await client.burn({
  poolKey: market.key,
  positionTokenId,
})
```

The closing order is:

1. Decrease all remaining liquidity.
2. Collect all owed token amounts.
3. Confirm the latest replacement `PositionNFT` is available.
4. Burn the empty position.

Burn consumes the final record and removes the public position. It does not issue a replacement.

See [Liquidity provider workflow](../liquidity/lp-workflow), [minting a position](../liquidity/minting-a-position), and [collecting and burning](../liquidity/collecting-and-burning) for the contract-level behavior.
