> For the complete documentation index, see [llms.txt](https://gotts.gitbook.io/docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://gotts.gitbook.io/docs/gotts-safe-mcp-server/mcp-server/05-tools-liquidity.md).

# Liquidity Tools

> **Package**: `packages/safe/` | **Prerequisites**: [02-architecture.md](/docs/gotts-safe-mcp-server/mcp-server/02-architecture.md)
>
> Liquidity management tools (Phase 3). For other tool categories, see the [README](/docs/gotts-safe-mcp-server/mcp-server.md).

***

## Liquidity Tools

#### `add_liquidity`

Add liquidity to a Uniswap pool (V2 simple, V3/V4 concentrated).

**Parameters**:

| Name            | Type     | Required    | Description                                                                                                                      |
| --------------- | -------- | ----------- | -------------------------------------------------------------------------------------------------------------------------------- |
| `token0`        | `string` | Yes         | First token symbol or address                                                                                                    |
| `token1`        | `string` | Yes         | Second token symbol or address                                                                                                   |
| `fee`           | `number` | Yes (V3/V4) | Fee tier in basis points. Not required for V2.                                                                                   |
| `amount0`       | `string` | Yes         | Amount of token0 to provide (human-readable)                                                                                     |
| `amount1`       | `string` | No          | Amount of token1. If omitted, calculated to match current pool ratio.                                                            |
| `chain`         | `string` | Yes         | Chain name or chain ID                                                                                                           |
| `version`       | `string` | No          | "v2", "v3" (default), or "v4"                                                                                                    |
| `priceLower`    | `number` | No (V3/V4)  | Lower bound of price range. Required for V3/V4 unless `rangeStrategy` is set.                                                    |
| `priceUpper`    | `number` | No (V3/V4)  | Upper bound of price range. Required for V3/V4 unless `rangeStrategy` is set.                                                    |
| `rangeStrategy` | `string` | No          | Shortcut: "full" (full range), "narrow" (current price +/- 5%), "medium" (current price +/- 15%), "wide" (current price +/- 50%) |
| `hookAddress`   | `string` | No (V4)     | V4 hook contract address. Required only for V4 pools with hooks.                                                                 |

**Returns**:

```json
{
  "status": "success",
  "txHash": "0xLPTX...",
  "positionId": "456790",
  "version": "v3",
  "pool": {
    "poolAddress": "0x88e6...",
    "token0": "USDC",
    "token1": "WETH",
    "fee": 500
  },
  "amount0Deposited": "5000.000000",
  "amount1Deposited": "1.540000000000000000",
  "amount0DepositedUsd": 5000.0,
  "amount1DepositedUsd": 5000.0,
  "priceLower": 2800.0,
  "priceUpper": 3600.0,
  "currentPrice": 3245.67,
  "rangeWidth": "28.57%",
  "estimatedFeeApy7d": 12.3,
  "explorerUrl": "https://etherscan.io/tx/0xLPTX...",
  "safetyChecks": {
    "tokenAllowlistPassed": true,
    "spendingLimitPassed": true,
    "simulationPassed": true
  }
}
```

**Implementation**: When `GOTTS_UNISWAP_API_KEY` is set, uses `POST /lp/approve` (checks both token approvals, returns `batchPermitData`) then `POST /lp/create` with position params (protocol, pool config, tick range, amounts, slippage, batch permit signature). V4 pools with hooks pass the hook address in `outputHooks`. When pool doesn't exist, the API creates it automatically. SDK fallback: `@uniswap/v3-sdk` NonfungiblePositionManager or `@uniswap/v4-sdk` PositionManager.

**Memory-Augmented LP Entry** (when `learning` profile active): Before execution, the memory middleware retrieves past IL patterns for this pair, historical fee/LVR ratios, and range performance data from similar episodes. Memory may surface advisory insights like "Narrow range on WETH/USDC has averaged 15% fee APY but 40% time out-of-range over last 30 episodes" or "Fee tier 0.05% has outperformed 0.30% for this pair in 7 of 10 episodes". After execution, a Reflexion episode is stored with entry params (range, amounts, fee tier), pool conditions at entry (tick, TVL, volatility), and predicted fee APY for retrospective comparison.

***

#### `remove_liquidity`

Remove liquidity from a position (partial or full).

**Parameters**:

| Name          | Type      | Required | Description                                                |
| ------------- | --------- | -------- | ---------------------------------------------------------- |
| `positionId`  | `string`  | Yes      | V3 NFT token ID or V4 position ID                          |
| `chain`       | `string`  | Yes      | Chain name or chain ID                                     |
| `percentage`  | `number`  | No       | Percentage to remove (1-100). Default: 100 (full removal). |
| `version`     | `string`  | No       | "v2", "v3" (default), or "v4"                              |
| `collectFees` | `boolean` | No       | Also collect accrued fees. Default: true.                  |

**Returns**:

```json
{
  "status": "success",
  "txHash": "0xRMTX...",
  "positionId": "456790",
  "percentageRemoved": 100,
  "amount0Received": "5050.230000",
  "amount1Received": "1.542300000000000000",
  "amount0ReceivedUsd": 5050.23,
  "amount1ReceivedUsd": 5008.07,
  "feesCollected": {
    "amount0": "45.230000",
    "amount1": "0.013500000000000000",
    "totalUsd": 89.05
  },
  "totalReceivedUsd": 10147.35,
  "explorerUrl": "https://etherscan.io/tx/0xRMTX..."
}
```

**Implementation**: When API key available, uses `POST /lp/decrease` with `liquidityPercentageToDecrease` (1-100). For V2, requires prior position token approval.

**Memory-Augmented LP Exit** (when `learning` profile active): After removal, stores a detailed Reflexion episode capturing position performance: total fees earned vs IL incurred, time in range vs out-of-range, fee APY vs predicted at entry, exit timing relative to volatility. This data feeds the ExpeL consolidation loop to build insights about optimal range widths, fee tiers, and exit timing for specific pairs and chains.

***

#### `collect_fees`

Collect accrued fees from a liquidity position without removing liquidity.

**Parameters**:

| Name         | Type     | Required | Description                       |
| ------------ | -------- | -------- | --------------------------------- |
| `positionId` | `string` | Yes      | V3 NFT token ID or V4 position ID |
| `chain`      | `string` | Yes      | Chain name or chain ID            |
| `version`    | `string` | No       | "v3" (default) or "v4"            |

**Returns**:

```json
{
  "status": "success",
  "txHash": "0xFEETX...",
  "positionId": "456790",
  "feesCollected": {
    "amount0": "45.230000",
    "amount0Symbol": "USDC",
    "amount1": "0.013500000000000000",
    "amount1Symbol": "WETH",
    "totalUsd": 89.05
  },
  "explorerUrl": "https://etherscan.io/tx/0xFEETX..."
}
```

**Implementation**: When API key available, uses `POST /lp/claim`. For V4 positions, the API internally executes a "zero liquidity decrease" pattern (DECREASE\_LIQUIDITY with 0 liquidity, then TAKE\_PAIR) to collect accrued fees.

***

#### `increase_liquidity`

Add more liquidity to an existing position without changing the price range.

**Parameters**:

| Name         | Type     | Required | Description                                                                        |
| ------------ | -------- | -------- | ---------------------------------------------------------------------------------- |
| `positionId` | `string` | Yes      | V3 NFT token ID or V4 position ID                                                  |
| `amount0`    | `string` | Yes      | Additional amount of token0 (human-readable)                                       |
| `amount1`    | `string` | No       | Additional amount of token1. If omitted, calculated to match the position's ratio. |
| `chain`      | `string` | Yes      | Chain name or chain ID                                                             |
| `version`    | `string` | No       | "v3" (default) or "v4"                                                             |

**Returns**:

```json
{
  "status": "success",
  "txHash": "0xINCTX...",
  "positionId": "456790",
  "amount0Added": "2000.000000",
  "amount1Added": "0.616000000000000000",
  "newTotalAmount0": "7050.230000",
  "newTotalAmount1": "2.158300000000000000",
  "newTotalValueUsd": 14100.46,
  "explorerUrl": "https://etherscan.io/tx/0xINCTX..."
}
```

**Implementation**: When API key available, uses `POST /lp/increase` with `tokenId`, `amount0`, `amount1`, and batch permit if needed.

***

## LP Optimization Tools

Analytical tools for LP strategy optimization. All computations run locally using data from existing subgraph/RPC tools — no external APIs, no API keys, no cost.

***

#### `optimize_lp_range`

Compute the optimal tick range for a concentrated liquidity position using Monte Carlo simulation. Accepts token pair, time horizon, and risk tolerance; outputs fee income, impermanent loss, net APR, and probability of going out-of-range for candidate ranges. This is the highest-value analytical tool for professional LPs.

**Parameters**:

| Name               | Type     | Required | Description                                                                                                  |
| ------------------ | -------- | -------- | ------------------------------------------------------------------------------------------------------------ |
| `token0`           | `string` | Yes      | First token symbol or address                                                                                |
| `token1`           | `string` | Yes      | Second token symbol or address                                                                               |
| `chain`            | `string` | Yes      | Chain name or chain ID                                                                                       |
| `fee`              | `number` | No       | Fee tier (100, 500, 3000, 10000). Uses highest-liquidity if omitted.                                         |
| `amount`           | `string` | Yes      | Capital to deploy in USD terms (e.g., "10000")                                                               |
| `horizonDays`      | `number` | No       | Time horizon in days. Default: 30.                                                                           |
| `riskTolerance`    | `string` | No       | `"conservative"` (95% in-range), `"moderate"` (80%), `"aggressive"` (60%). Default: `"moderate"`.            |
| `simulations`      | `number` | No       | Number of Monte Carlo paths. Default: 5000. Max: 50000.                                                      |
| `volatilitySource` | `string` | No       | `"historical"` (realized vol from swap history, default) or `"implied"` (if available from options markets). |

**Returns**:

```json
{
  "pair": "WETH/USDC",
  "chain": "base",
  "fee": 500,
  "currentPrice": 3245.67,
  "volatility": {
    "source": "historical",
    "annualized": 0.62,
    "lookback": "30d"
  },
  "optimalRange": {
    "priceLower": 2780.0,
    "priceUpper": 3850.0,
    "tickLower": 199800,
    "tickUpper": 203400,
    "rangeWidthPct": 38.5
  },
  "projections": {
    "expectedFeeApr": 18.4,
    "expectedIlPct": -2.1,
    "expectedNetApr": 16.3,
    "inRangeProbability": 0.82,
    "feeIncome30dUsd": 150.2,
    "ilCost30dUsd": -17.5,
    "netPnl30dUsd": 132.7
  },
  "candidateRanges": [
    {
      "label": "narrow (+/- 10%)",
      "priceLower": 2921.1,
      "priceUpper": 3570.24,
      "expectedFeeApr": 28.6,
      "expectedIlPct": -4.2,
      "expectedNetApr": 24.4,
      "inRangeProbability": 0.61,
      "outOfRangeRisk": "HIGH"
    },
    {
      "label": "optimal (Monte Carlo)",
      "priceLower": 2780.0,
      "priceUpper": 3850.0,
      "expectedFeeApr": 18.4,
      "expectedIlPct": -2.1,
      "expectedNetApr": 16.3,
      "inRangeProbability": 0.82,
      "outOfRangeRisk": "MODERATE"
    },
    {
      "label": "wide (+/- 30%)",
      "priceLower": 2271.97,
      "priceUpper": 4219.37,
      "expectedFeeApr": 8.2,
      "expectedIlPct": -0.8,
      "expectedNetApr": 7.4,
      "inRangeProbability": 0.95,
      "outOfRangeRisk": "LOW"
    },
    {
      "label": "full range",
      "priceLower": 0,
      "priceUpper": "Infinity",
      "expectedFeeApr": 3.1,
      "expectedIlPct": -0.3,
      "expectedNetApr": 2.8,
      "inRangeProbability": 1.0,
      "outOfRangeRisk": "NONE"
    }
  ],
  "methodology": "10,000 GBM price paths with EWMA volatility. Fee income estimated from 30d historical volume distribution across ticks.",
  "timestamp": "2026-02-19T12:00:00Z"
}
```

**Implementation**: Fetches current pool state via `get_pool_info`, historical price/volume via `get_token_price_history` and `get_pool_volume_history`, tick distribution via `get_tick_data`. Runs Geometric Brownian Motion simulation with EWMA-weighted realized volatility. For each candidate range, estimates fee income from volume distribution across ticks and IL from terminal price distribution. The AR(1)-GARCH(1,1) model from Gamma Strategies can be used for near-term volatility prediction with configurable alpha and tau parameters.

**Memory-Augmented Optimization** (when `learning` profile active): Monte Carlo inputs are augmented with historical memory data. If past episodes recorded actual realized volatility for this pair, the model blends EWMA estimates with episodic volatility observations (weighted by recency and episode count). Past range performance data (actual fee APY, actual time in range) is compared against Monte Carlo projections to calibrate the simulation. The response includes a `memoryCalibration` field indicating whether memory data was used and how it adjusted projections.

**Error Cases**:

* `NO_POOL`: No pool found for pair and fee tier
* `INSUFFICIENT_HISTORY`: Less than 7 days of price history for volatility estimation
* `SIMULATION_TIMEOUT`: Monte Carlo exceeded time limit

***

#### `recommend_fee_tier`

Analyze a token pair and recommend the optimal fee tier based on volatility, volume distribution across tiers, and historical backtest performance.

**Parameters**:

| Name     | Type     | Required | Description                    |
| -------- | -------- | -------- | ------------------------------ |
| `token0` | `string` | Yes      | First token symbol or address  |
| `token1` | `string` | Yes      | Second token symbol or address |
| `chain`  | `string` | Yes      | Chain name or chain ID         |

**Returns**:

```json
{
  "pair": "WETH/USDC",
  "chain": "base",
  "recommendation": {
    "feeTier": 500,
    "confidence": "HIGH",
    "reason": "Moderate volatility (62% annualized) with 78% of volume in the 0.05% tier. Higher tiers have less volume and lower capital efficiency."
  },
  "analysis": {
    "pairVolatility": {
      "annualized": 0.62,
      "classification": "MODERATE",
      "note": "Higher volatility -> higher fee tier captures more per-swap. But volume concentrates in lower tiers."
    },
    "volumeDistribution": [
      { "feeTier": 100, "volumePct": 5, "tvlUsd": 12000000, "feeApy7d": 4.2 },
      {
        "feeTier": 500,
        "volumePct": 78,
        "tvlUsd": 245000000,
        "feeApy7d": 12.3
      },
      { "feeTier": 3000, "volumePct": 15, "tvlUsd": 85000000, "feeApy7d": 8.7 },
      { "feeTier": 10000, "volumePct": 2, "tvlUsd": 5000000, "feeApy7d": 6.1 }
    ],
    "competitiveLandscape": {
      "otherDexVolumePct": 22,
      "note": "22% of this pair's volume goes to other DEXs. Lower fee tiers are more competitive."
    }
  },
  "timestamp": "2026-02-19T12:00:00Z"
}
```

**Data Source**: Existing subgraph pool data (`get_pools_by_token_pair`) for volume distribution, historical price data for volatility calculation.

**Error Cases**:

* `NO_POOLS`: No pools exist for this token pair on this chain
* `TOKEN_NOT_FOUND`: Token not recognized

***

#### `backtest_lp_strategy`

Backtest LP strategies against historical data. Compare different tick ranges, rebalancing frequencies, and fee tiers against a HODL benchmark over a configurable lookback period.

**Parameters**:

| Name         | Type     | Required | Description                                                            |
| ------------ | -------- | -------- | ---------------------------------------------------------------------- |
| `token0`     | `string` | Yes      | First token symbol or address                                          |
| `token1`     | `string` | Yes      | Second token symbol or address                                         |
| `chain`      | `string` | Yes      | Chain name or chain ID                                                 |
| `fee`        | `number` | No       | Fee tier. Default: highest-liquidity pool.                             |
| `amount`     | `string` | Yes      | Capital deployed in USD (e.g., "10000")                                |
| `startTime`  | `string` | Yes      | ISO 8601 backtest start date                                           |
| `endTime`    | `string` | No       | ISO 8601 end date. Default: now.                                       |
| `strategies` | `string` | No       | JSON array of strategies to compare. Default: narrow/medium/wide/full. |

**Default strategies** (when `strategies` is omitted):

```json
[
  { "name": "narrow", "rangePct": 10, "rebalanceThresholdPct": 80 },
  { "name": "medium", "rangePct": 25, "rebalanceThresholdPct": 90 },
  { "name": "wide", "rangePct": 50, "rebalanceThresholdPct": 95 },
  { "name": "full-range", "rangePct": null, "rebalanceThresholdPct": null }
]
```

The `rebalanceThresholdPct` is the percentage of out-of-range time before triggering a rebalance (e.g., 80 means rebalance when price has been outside range for 80% of the interval).

**Returns**:

```json
{
  "pair": "WETH/USDC",
  "chain": "base",
  "fee": 500,
  "period": { "start": "2025-11-01", "end": "2026-02-19", "days": 111 },
  "priceChange": {
    "startPrice": 2800.0,
    "endPrice": 3245.67,
    "changePct": 15.9
  },
  "hodlBenchmark": {
    "startValueUsd": 10000.0,
    "endValueUsd": 11590.0,
    "returnPct": 15.9
  },
  "strategies": [
    {
      "name": "narrow (+/- 10%)",
      "feesEarnedUsd": 1420.5,
      "ilCostUsd": -580.2,
      "rebalanceCount": 8,
      "rebalanceGasCostUsd": 2.4,
      "endValueUsd": 11838.9,
      "netReturnPct": 18.4,
      "vsHodlPct": 2.5,
      "inRangePct": 62,
      "sharpeRatio": 1.45
    },
    {
      "name": "medium (+/- 25%)",
      "feesEarnedUsd": 890.3,
      "ilCostUsd": -210.4,
      "rebalanceCount": 2,
      "rebalanceGasCostUsd": 0.6,
      "endValueUsd": 11669.9,
      "netReturnPct": 16.7,
      "vsHodlPct": 0.8,
      "inRangePct": 88,
      "sharpeRatio": 1.52
    }
  ],
  "winner": {
    "strategy": "narrow (+/- 10%)",
    "reason": "Highest net return (18.4%) despite lower in-range time, due to concentrated fee capture.",
    "caveat": "Requires 8 rebalances. On Ethereum mainnet, gas costs would be ~$98 instead of $2.40."
  },
  "timestamp": "2026-02-19T12:00:00Z"
}
```

**Implementation**: Replays historical price data from `get_token_price_history` and volume data from `get_pool_volume_history`. For each strategy, simulates position creation, fee accrual (proportional to tick-level volume), out-of-range detection, and rebalancing. Gas costs estimated per chain.

**Error Cases**:

* `INSUFFICIENT_HISTORY`: Less than 14 days of data for the requested period
* `NO_POOL`: No pool found for pair and fee tier
* `INVALID_STRATEGY`: Malformed strategy JSON

***

#### `compound_fees`

Collect accrued LP fees and reinvest them into the same position in a single atomic operation. For V4 pools, uses zero-delta modification to collect then increases liquidity.

**Parameters**:

| Name          | Type      | Required | Description                                                                                |
| ------------- | --------- | -------- | ------------------------------------------------------------------------------------------ |
| `positionId`  | `string`  | Yes      | V3 NFT token ID or V4 position ID                                                          |
| `chain`       | `string`  | Yes      | Chain name or chain ID                                                                     |
| `version`     | `string`  | No       | "v3" (default) or "v4"                                                                     |
| `swapToRatio` | `boolean` | No       | Swap collected fees to match the position's token ratio before reinvesting. Default: true. |

**Returns**:

```json
{
  "status": "success",
  "txHash": "0xCOMPOUND...",
  "positionId": "456790",
  "feesCollected": {
    "amount0": "45.230000",
    "amount1": "0.013500000000000000",
    "totalUsd": 89.05
  },
  "reinvested": {
    "amount0": "44.800000",
    "amount1": "0.013800000000000000",
    "swapExecuted": true,
    "swapDetails": "Swapped 0.43 USDC → 0.0003 WETH to match ratio"
  },
  "newLiquidity": "14523456789012345678",
  "explorerUrl": "https://basescan.org/tx/0xCOMPOUND..."
}
```

**Implementation**: When API key available, uses `POST /lp/claim` then `POST /quote` + `POST /swap` for the ratio swap, then `POST /lp/increase` to reinvest.

***

#### `batch_lp_operations`

Execute multiple LP operations atomically in a single multicall transaction. Supports combinations of add, remove, collect, rebalance, and compound across multiple positions.

**Parameters**:

| Name         | Type     | Required | Description                                               |
| ------------ | -------- | -------- | --------------------------------------------------------- |
| `operations` | `array`  | Yes      | Array of operation objects: `[{ type, positionId, ... }]` |
| `chain`      | `string` | Yes      | Chain name or chain ID                                    |

Each operation object:

```json
{
  "type": "collect" | "compound" | "remove" | "add" | "rebalance",
  "positionId": "456790",
  "params": { }
}
```

**Returns**:

```json
{
  "status": "success",
  "txHash": "0xBATCH...",
  "operationsExecuted": 3,
  "results": [
    { "type": "collect", "positionId": "456790", "feesUsd": 89.05 },
    { "type": "remove", "positionId": "456791", "receivedUsd": 5200.0 },
    { "type": "add", "positionId": "456792", "depositedUsd": 5200.0 }
  ],
  "totalGasCostUsd": 0.08,
  "gasSavedVsSeparate": "~60% gas savings vs 3 separate transactions",
  "explorerUrl": "https://basescan.org/tx/0xBATCH..."
}
```

***

#### `submit_twamm_order`

Submit a TWAMM (Time-Weighted Average Market Maker) order on a V4 pool with a TWAMM hook. Enables DCA-style gradual execution over a configurable time window, minimizing price impact for large trades.

**Parameters**:

| Name       | Type     | Required | Description                                                |
| ---------- | -------- | -------- | ---------------------------------------------------------- |
| `tokenIn`  | `string` | Yes      | Input token symbol or address                              |
| `tokenOut` | `string` | Yes      | Output token symbol or address                             |
| `amount`   | `string` | Yes      | Total amount to sell over the duration (human-readable)    |
| `duration` | `number` | Yes      | Duration in seconds (e.g., 86400 for 24 hours)             |
| `chain`    | `string` | Yes      | Chain name or chain ID                                     |
| `pool`     | `string` | No       | V4 pool address with TWAMM hook. Auto-detected if omitted. |

**Returns**:

```json
{
  "status": "success",
  "txHash": "0xTWAMM...",
  "orderId": "12345",
  "tokenIn": { "symbol": "USDC", "totalAmount": "50000" },
  "tokenOut": { "symbol": "WETH" },
  "duration": 86400,
  "sellRate": "0.5787 USDC/second",
  "estimatedOutput": "15.4 WETH",
  "expiresAt": "2026-02-20T12:00:00Z",
  "explorerUrl": "https://basescan.org/tx/0xTWAMM..."
}
```

**Error Cases**:

* `NO_TWAMM_HOOK`: Pool does not have a TWAMM hook
* `AMOUNT_TOO_SMALL`: Amount below minimum for TWAMM execution
* `DURATION_TOO_SHORT`: Duration below minimum (typically 1 hour)

***

#### `get_twamm_order_status`

Check the progress and status of a TWAMM order.

**Parameters**:

| Name      | Type     | Required | Description                     |
| --------- | -------- | -------- | ------------------------------- |
| `orderId` | `string` | Yes      | TWAMM order ID                  |
| `pool`    | `string` | Yes      | V4 pool address with TWAMM hook |
| `chain`   | `string` | Yes      | Chain name or chain ID          |

**Returns**:

```json
{
  "orderId": "12345",
  "status": "active",
  "progress": {
    "percentComplete": 45.2,
    "amountSold": "22600 USDC",
    "amountReceived": "6.95 WETH",
    "avgPrice": "3251.80",
    "remainingAmount": "27400 USDC"
  },
  "timing": {
    "startedAt": "2026-02-19T12:00:00Z",
    "expiresAt": "2026-02-20T12:00:00Z",
    "timeRemaining": "13h 10m"
  },
  "vsSpotExecution": {
    "spotPriceAtStart": 3245.67,
    "currentAvgPrice": 3251.8,
    "twammPremiumPct": 0.19,
    "note": "TWAMM execution is 0.19% worse than spot at order start, but likely better than a single large trade"
  }
}
```

**Error Cases**:

* `ORDER_NOT_FOUND`: Order ID not found on pool
* `POOL_NOT_FOUND`: Pool not found

***

#### `rebalance_position`

Close an existing position and open a new one at a new price range. Combines remove + add in one operation.

**Parameters**:

| Name            | Type     | Required | Description                                              |
| --------------- | -------- | -------- | -------------------------------------------------------- |
| `positionId`    | `string` | Yes      | Position to close                                        |
| `chain`         | `string` | Yes      | Chain name or chain ID                                   |
| `priceLower`    | `number` | No       | New lower price bound. If omitted, uses `rangeStrategy`. |
| `priceUpper`    | `number` | No       | New upper price bound. If omitted, uses `rangeStrategy`. |
| `rangeStrategy` | `string` | No       | "narrow", "medium" (default), "wide", "full"             |
| `version`       | `string` | No       | "v3" (default) or "v4"                                   |

**Returns**:

```json
{
  "status": "success",
  "closedPosition": {
    "positionId": "456790",
    "txHash": "0xCLOSE...",
    "amount0Received": "5050.230000",
    "amount1Received": "1.542300000000000000",
    "feesCollected": { "totalUsd": 89.05 }
  },
  "newPosition": {
    "positionId": "456791",
    "txHash": "0xOPEN...",
    "amount0Deposited": "5050.230000",
    "amount1Deposited": "1.542300000000000000",
    "priceLower": 2900.0,
    "priceUpper": 3500.0,
    "currentPrice": 3245.67
  },
  "totalGasCostUsd": 12.34
}
```

**Implementation**: When API key available, orchestrates `POST /lp/decrease` (100%) → `POST /lp/claim` → `POST /lp/create` with new range parameters in sequence.

***

#### `migrate_position`

Migrate a V3 liquidity position to V4. Only available when `GOTTS_UNISWAP_API_KEY` is configured (no SDK fallback for migration).

**Parameters**:

| Name                | Type     | Required | Description                                               |
| ------------------- | -------- | -------- | --------------------------------------------------------- |
| `positionId`        | `string` | Yes      | V3 NFT token ID to migrate                                |
| `chain`             | `string` | Yes      | Chain name or chain ID                                    |
| `outputHooks`       | `string` | No       | V4 hooks address for the new position. Default: no hooks. |
| `slippageTolerance` | `number` | No       | Slippage tolerance in basis points. Default: 50 (0.5%).   |

**Returns**:

```json
{
  "status": "success",
  "txHash": "0xMIGRATE...",
  "oldPositionId": "456790",
  "newPositionId": "v4-789012",
  "version": "v4",
  "pool": {
    "token0": "USDC",
    "token1": "WETH",
    "fee": 500
  },
  "amount0Migrated": "5000.000000",
  "amount1Migrated": "1.540000000000000000",
  "hooks": "0x0000000000000000000000000000000000000000",
  "explorerUrl": "https://basescan.org/tx/0xMIGRATE..."
}
```

**Implementation**: Uses `POST /lp/migrate` with `{ positionTokenId, chainId, outputHooks, slippageTolerance }`. The API handles the full migration atomically: removes V3 liquidity, creates equivalent V4 position.

**Error Cases**:

* `API_KEY_NOT_CONFIGURED`: This tool requires the Uniswap Trading API key
* `POSITION_NOT_FOUND`: V3 position not found
* `MIGRATION_NOT_SUPPORTED`: V4 pool not available for this pair on this chain

***

#### `claim_lp_rewards`

Claim LP incentive rewards (e.g., MERKL distribution) for liquidity positions.

**Parameters**:

| Name          | Type     | Required | Description                              |
| ------------- | -------- | -------- | ---------------------------------------- |
| `chain`       | `string` | Yes      | Chain name or chain ID                   |
| `tokens`      | `array`  | Yes      | Array of reward token addresses to claim |
| `distributor` | `string` | No       | Reward distributor. Default: "MERKL".    |

**Returns**:

```json
{
  "status": "success",
  "txHash": "0xCLAIM...",
  "claimed": [
    {
      "token": "0xARB...",
      "symbol": "ARB",
      "amount": "125.500000",
      "amountUsd": 187.5
    }
  ],
  "totalClaimedUsd": 187.5,
  "distributor": "MERKL",
  "explorerUrl": "https://basescan.org/tx/0xCLAIM..."
}
```

**Implementation**: Uses `POST /lp/claim_rewards` with `{ chainId, tokens, distributor }`. Requires `GOTTS_UNISWAP_API_KEY`.

**Error Cases**:

* `NO_REWARDS_AVAILABLE`: No claimable rewards for the specified tokens
* `API_KEY_NOT_CONFIGURED`: This tool requires the Uniswap Trading API key

***

#### `harvest_rewards`

Collect LP fees and claim distributor rewards across all positions on a chain in one coordinated operation. Optionally consolidates everything into a single target token via swaps. Composes `collect_fees` + `claim_lp_rewards` + `execute_swap` into a single workflow — the "drain all earnings" power tool.

**Parameters**:

| Name             | Type      | Required | Description                                                                                                    |
| ---------------- | --------- | -------- | -------------------------------------------------------------------------------------------------------------- |
| `wallet`         | `string`  | No       | Wallet address. Default: configured agent wallet.                                                              |
| `chain`          | `string`  | Yes      | Chain name or chain ID                                                                                         |
| `targetToken`    | `string`  | No       | Token to consolidate all earnings into (symbol or address). Omit to skip consolidation and receive raw tokens. |
| `positionIds`    | `array`   | No       | Specific position IDs to harvest from. Default: all positions owned by wallet.                                 |
| `includeRewards` | `boolean` | No       | Also claim MERKL distributor rewards. Default: true.                                                           |
| `slippageBps`    | `number`  | No       | Slippage tolerance for consolidation swaps in basis points. Default: 100 (1%).                                 |
| `minHarvestUsd`  | `number`  | No       | Skip positions with accrued fees below this USD threshold (dust filter). Default: 0.10.                        |
| `dryRun`         | `boolean` | No       | Preview planned operations without executing. Returns gas estimates and expected values. Default: false.       |

**Returns**:

```json
{
  "status": "success",
  "feesCollected": [
    {
      "positionId": "456790",
      "pool": "WETH/USDC 0.05%",
      "txHash": "0xFEE1...",
      "amount0": "45.23",
      "amount0Symbol": "USDC",
      "amount1": "0.0135",
      "amount1Symbol": "WETH",
      "totalUsd": 89.05
    },
    {
      "positionId": "456791",
      "pool": "WETH/DAI 0.30%",
      "txHash": "0xFEE2...",
      "amount0": "120.50",
      "amount0Symbol": "DAI",
      "amount1": "0.0042",
      "amount1Symbol": "WETH",
      "totalUsd": 134.13
    }
  ],
  "rewardsClaimed": [
    {
      "distributor": "MERKL",
      "txHash": "0xREWARD...",
      "tokens": [{ "symbol": "ARB", "amount": "125.50", "amountUsd": 187.5 }]
    }
  ],
  "consolidationSwaps": [
    {
      "txHash": "0xSWAP1...",
      "tokenIn": "WETH",
      "amountIn": "0.0177",
      "tokenOut": "USDC",
      "amountOut": "57.48",
      "priceImpactBps": 1
    },
    {
      "txHash": "0xSWAP2...",
      "tokenIn": "DAI",
      "amountIn": "120.50",
      "tokenOut": "USDC",
      "amountOut": "120.38",
      "priceImpactBps": 0
    },
    {
      "txHash": "0xSWAP3...",
      "tokenIn": "ARB",
      "amountIn": "125.50",
      "tokenOut": "USDC",
      "amountOut": "187.12",
      "priceImpactBps": 2
    }
  ],
  "summary": {
    "positionsHarvested": 2,
    "positionsSkipped": 1,
    "totalFeesUsd": 223.18,
    "totalRewardsUsd": 187.5,
    "consolidatedAmount": "530.21",
    "consolidatedToken": "USDC",
    "totalGasCostUsd": 0.24,
    "netValueUsd": 410.44
  }
}
```

**Implementation**: Discovers positions via `get_positions_by_owner`, filters by `minHarvestUsd` threshold, then executes in order: (1) `collect_fees` on each qualifying position, (2) `claim_lp_rewards` from MERKL distributor if `includeRewards` is true, (3) consolidation swaps to `targetToken` via `execute_swap` for each distinct token received. Each consolidation swap runs through the full 7-layer safety pipeline independently. Dry run returns the planned operations with gas estimates but does not broadcast.

**Risk Tier**: Standard (10 min Agent Proxy delay). Each consolidation swap is independently risk-assessed.

**Error Cases**:

* `NO_POSITIONS`: No positions found for wallet on this chain
* `HARVEST_PARTIAL_FAILURE`: Some fee collections or reward claims failed (partial results returned)
* `DUST_FILTER_ALL_SKIPPED`: All positions below `minHarvestUsd` threshold — nothing to harvest
* `CONSOLIDATION_SWAP_FAILED`: One or more consolidation swaps failed (fees still collected)

***
