# Uniswap Position Quoter - Usage Guide

A comprehensive guide for using the `@steerprotocol/uniswap-position-quoter` SDK to calculate optimal swap amounts for rebalancing multi-position liquidity strategies across multiple DEX protocols.

## Table of Contents

- [Installation](#installation)
- [Test Package](#test-package)
- [Quick Start](#quick-start)
- [Core Concepts](#core-concepts)
- [API Reference](#api-reference)
- [Supported Protocols](#supported-protocols)
- [Common Patterns](#common-patterns)
- [Advanced Usage](#advanced-usage)
- [Troubleshooting](#troubleshooting)

---

## Installation

```bash
# npm
npm install @steerprotocol/uniswap-position-quoter

# yarn
yarn add @steerprotocol/uniswap-position-quoter

# pnpm
pnpm add @steerprotocol/uniswap-position-quoter
```

---

## Test Package

For testing upcoming releases before they're officially published, a test package is available:

### Current Test Version

**`test-uniswap-position-quoter@1.0.4-test.1`**

This test package allows you to validate new features and changes without affecting your production dependencies.

### Installing the Test Package

```bash
# npm
npm install test-uniswap-position-quoter@1.0.4-test.1

# yarn
yarn add test-uniswap-position-quoter@1.0.4-test.1

# pnpm
pnpm add test-uniswap-position-quoter@1.0.4-test.1
```

### Usage with Test Package

The API is identical to the production package. Simply import from the test package:

```typescript
import {
  CustomRouterFactory,
  NoopLogger,
  type Position,
  type SwapResult,
} from 'test-uniswap-position-quoter';
// ... rest of your code remains the same
```

### When to Use the Test Package

- **Pre-release Testing**: Test new features before they're officially released
- **Integration Testing**: Validate compatibility with your codebase
- **Bug Verification**: Test fixes for specific issues
- **Performance Testing**: Evaluate performance improvements

### Important Notes

⚠️ **Warning**: The test package is for evaluation purposes only. Do not use in production.

- Test packages may have breaking changes
- Test packages are not guaranteed to be stable
- Always upgrade to the official release once available

### Finding Available Test Versions

Check the [releases documentation](../../releases.txt) for the latest test package versions.

---

## Quick Start

### Basic 3-Step Usage

```typescript
import {
  CustomRouterFactory,
  NoopLogger,
  type Position,
  type SwapResult,
} from '@steerprotocol/uniswap-position-quoter';
import { ethers } from 'ethers';

// Step 1: Create the factory
const factory = new CustomRouterFactory(NoopLogger);

// Step 2: Create a router (automatic protocol detection)
const router = factory.createCustomRouter({
  beaconName: 'UniswapV3', // Your vault's beacon name
  quoterV2: quoterContract, // Your QuoterV2 contract instance
  poolObj: poolContract, // Your pool contract instance
});

// Step 3: Get swap amount for rebalancing
const result: SwapResult = await router.getSwapAmount(
  poolContract,
  positions, // Array of desired positions
  token0Balance, // Current token0 balance (BigNumber)
  token1Balance, // Current token1 balance (BigNumber)
  token0Address, // Token0 contract address
  token1Address, // Token1 contract address
  poolFee, // Pool fee (e.g., 3000 for 0.3%)
  10, // Max iterations for calculation
);

// Use the result
console.log('Swap amount:', result.amountToSwap.toString());
console.log(
  'Direction:',
  result.zeroForOne ? 'Token0 → Token1' : 'Token1 → Token0',
);
```

### Complete Working Example

```typescript
import {
  CustomRouterFactory,
  NoopLogger,
  type Position,
  type SwapResult,
} from '@steerprotocol/uniswap-position-quoter';
import { ethers } from 'ethers';

// Setup provider and contracts
const provider = new ethers.JsonRpcProvider(RPC_URL);
const quoterContract = new ethers.Contract(
  QUOTER_V2_ADDRESS,
  QuoterV2ABI,
  provider,
);
const poolContract = new ethers.Contract(POOL_ADDRESS, PoolABI, provider);

// Create factory
const factory = new CustomRouterFactory(NoopLogger);

// Create router
const router = factory.createCustomRouter({
  beaconName: 'UniswapV3',
  quoterV2: quoterContract,
  poolObj: poolContract,
});

// Define your liquidity positions
const positions: Position[] = [
  {
    lowerTick: -887220, // Lower tick boundary
    upperTick: 887220, // Upper tick boundary
    weight: 1, // Position weight (for multi-position strategies)
  },
];

// Get optimal swap amount
const result: SwapResult = await router.getSwapAmount(
  poolContract,
  positions,
  ethers.utils.parseEther('100'), // 100 token0
  ethers.utils.parseEther('100'), // 100 token1
  TOKEN0_ADDRESS,
  TOKEN1_ADDRESS,
  3000, // 0.3% fee tier
  10, // max iterations
);

// Execute swap based on result
if (result.zeroForOne) {
  console.log(`Swap ${result.amountToSwap} of token0 for token1`);
  // Execute swap: token0 → token1
} else {
  console.log(`Swap ${result.amountToSwap} of token1 for token0`);
  // Execute swap: token1 → token0
}
```

---

## Core Concepts

### Position

A `Position` represents a liquidity range where you want to provide liquidity:

```typescript
type Position = {
  lowerTick: number; // Lower tick boundary
  upperTick: number; // Upper tick boundary
  weight: number; // Relative weight (for multi-position strategies)
};
```

### SwapResult

The result of calculating the optimal swap amount:

```typescript
type SwapResult = {
  amountToSwap: BigNumber; // Amount to swap (in token units)
  zeroForOne: boolean; // true: swap token0→token1, false: token1→token0
};
```

### Beacon Name

The `beaconName` is a string identifier that determines which protocol router to use. The factory automatically detects the protocol based on this name.

Examples:

- `'UniswapV3'` → Uniswap V3 router
- `'AlgebraIntegral_v2_0'` → Algebra Integral V2 router
- `'AlgebraIntegral_v2_0_Blackhole'` → Algebra Integral V2 with Blackhole protocol

---

## API Reference

### CustomRouterFactory

The main entry point for creating protocol-specific routers.

#### Constructor

```typescript
const factory = new CustomRouterFactory(logger: Logger);
```

**Parameters:**

- `logger`: A logger instance (use `NoopLogger` for no logging)

#### createCustomRouter()

Creates a router instance for the specified protocol.

```typescript
const router = factory.createCustomRouter({
  beaconName: string,
  quoterV2: ethers.Contract,
  poolObj: ethers.Contract,
  protocolParams: ProtocolSpecificParams,
});
```

**Parameters:**

- `beaconName` (required): Protocol identifier string
- `quoterV2` (required): QuoterV2 contract instance
- `poolObj` (required): Pool contract instance
- `protocolParams` (optional): Protocol-specific parameters (see [Advanced Usage](#advanced-usage))

**Returns:** `CustomRouter` - A router instance for your protocol

### Router.getSwapAmount()

Calculates the optimal swap amount for rebalancing positions.

```typescript
const result = await router.getSwapAmount(
  pool: ethers.Contract,
  desiredPositions: Position[],
  inputT0Bal: BigNumber,
  inputT1Bal: BigNumber,
  t0Address: string,
  t1Address: string,
  fee: number,
  maxIterations: number,
  options?: GetSwapAmountOptions
);
```

**Parameters:**

- `pool` (required): Pool contract instance
- `desiredPositions` (required): Array of target positions
- `inputT0Bal` (required): Current token0 balance
- `inputT1Bal` (required): Current token1 balance
- `t0Address` (required): Token0 contract address
- `t1Address` (required): Token1 contract address
- `fee` (required): Pool fee tier (e.g., 3000 for 0.3%)
- `maxIterations` (required): Maximum calculation iterations (10-20 recommended)
- `options` (optional): Additional options (see below)

**Options (GetSwapAmountOptions):**

- `ratioErrorTolerance?: Fraction` - Error tolerance for ratio calculations
- `maxPriceImpactBps?: number` - Maximum price impact in basis points (e.g., 1000 = 10%)
- `searchMode?: 'input' | 'output' | 'parallel'` - Search strategy (Uniswap only)
- `clampMode?: 'price' | 'tick'` - Ratio calculation variant ('tick' matches legacy behavior)
- `engineExtras?: Record<string, unknown>` - Engine-specific extras

**Returns:** `Promise<SwapResult>`

---

## Supported Protocols

The SDK automatically detects and supports the following protocols:

| Protocol                | Beacon Name Examples   | Special Config            |
| ----------------------- | ---------------------- | ------------------------- |
| **Uniswap V3**          | `UniswapV3`            | None                      |
| **Uniswap V4**          | `UniswapV4`            | None                      |
| **Algebra**             | `Algebra`              | None                      |
| **Algebra Integral**    | `AlgebraIntegral`      | None                      |
| **Algebra Integral V2** | `AlgebraIntegral_v2_0` | Optional deployer address |
| **ThickV2**             | `ThickV2`              | None                      |
| **Aerodrome**           | `Aerodrome`            | None                      |
| **Shadow**              | `Shadow`               | None                      |
| **PoolShark**           | `PoolShark`            | None                      |

---

## Common Patterns

### Pattern 1: Single Position Strategy

```typescript
const positions: Position[] = [
  {
    lowerTick: -887220,
    upperTick: 887220,
    weight: 1,
  },
];

const result = await router.getSwapAmount(
  poolContract,
  positions,
  token0Balance,
  token1Balance,
  token0Address,
  token1Address,
  3000,
  10,
);
```

### Pattern 2: Multi-Position Strategy

```typescript
// Multiple positions with different weights
const positions: Position[] = [
  { lowerTick: -887220, upperTick: -443610, weight: 2 }, // Wide position (20%)
  { lowerTick: -443610, upperTick: 443610, weight: 5 }, // Main position (50%)
  { lowerTick: 443610, upperTick: 887220, weight: 2 }, // Wide position (20%)
];

const result = await router.getSwapAmount(
  poolContract,
  positions,
  token0Balance,
  token1Balance,
  token0Address,
  token1Address,
  3000,
  15, // More iterations for complex strategies
);
```

### Pattern 3: Protocol-Specific Configuration

```typescript
// For protocols requiring special parameters (e.g., Blackhole)
const router = factory.createCustomRouter({
  beaconName: 'AlgebraIntegral_v2_0_Blackhole',
  quoterV2: quoterContract,
  poolObj: poolContract,
  protocolParams: {
    algebraIntegralV2Deployer: '0x1234567890123456789012345678901234567890',
  },
});
```

### Pattern 4: Environment-Based Configuration

```typescript
const BLACKHOLE_DEPLOYER = process.env.BLACKHOLE_DEPLOYER_ADDRESS;
const beaconName = process.env.BEACON_NAME || 'UniswapV3';

const router = factory.createCustomRouter({
  beaconName,
  quoterV2: quoterContract,
  poolObj: poolContract,
  protocolParams: BLACKHOLE_DEPLOYER
    ? {
        algebraIntegralV2Deployer: BLACKHOLE_DEPLOYER,
      }
    : undefined,
});
```

### Pattern 5: Manager Class Pattern

```typescript
class VaultRouterManager {
  private factory: CustomRouterFactory;
  private provider: ethers.providers.Provider;

  constructor(logger: Logger, provider: ethers.providers.Provider) {
    this.factory = new CustomRouterFactory(logger);
    this.provider = provider;
  }

  async createRouterForVault(
    beaconName: string,
    quoterV2Address: string,
    poolAddress: string,
    options?: { deployerAddress?: string },
  ) {
    const quoterV2 = new ethers.Contract(
      quoterV2Address,
      QuoterV2ABI,
      this.provider,
    );
    const poolObj = new ethers.Contract(poolAddress, PoolABI, this.provider);

    return this.factory.createCustomRouter({
      beaconName,
      quoterV2,
      poolObj,
      protocolParams: options?.deployerAddress
        ? {
            algebraIntegralV2Deployer: options.deployerAddress,
          }
        : undefined,
    });
  }
}

// Usage
const manager = new VaultRouterManager(NoopLogger, provider);
const router = await manager.createRouterForVault(
  'UniswapV3',
  QUOTER_ADDRESS,
  POOL_ADDRESS,
);
```

---

## Advanced Usage

### Custom Logger

Implement your own logger for production monitoring:

```typescript
import { type Logger } from '@steerprotocol/uniswap-position-quoter';

const myLogger: Logger = {
  log: (payload, msg, ...args) => console.log(msg, payload),
  info: (payload, msg, ...args) => console.info(msg, payload),
  error: (payload, msg, ...args) => console.error(msg, payload),
  warn: (payload, msg, ...args) => console.warn(msg, payload),
  debug: (payload, msg, ...args) => console.debug(msg, payload),
};

const factory = new CustomRouterFactory(myLogger);
```

### Advanced Options

```typescript
const result = await router.getSwapAmount(
  poolContract,
  positions,
  token0Balance,
  token1Balance,
  token0Address,
  token1Address,
  3000,
  10,
  {
    // Error tolerance for ratio calculations
    ratioErrorTolerance: new Fraction(1, 10000), // 0.01%

    // Maximum price impact (10%)
    maxPriceImpactBps: 1000,

    // Search mode (Uniswap only)
    searchMode: 'parallel', // 'input' | 'output' | 'parallel'

    // Ratio calculation variant
    clampMode: 'tick', // 'price' | 'tick'
  },
);
```

### TypeScript Types

Full type safety with exported types:

```typescript
import type {
  Position,
  SwapResult,
  GetSwapAmountOptions,
  CreateCustomRouterParams,
  ProtocolSpecificParams,
  Logger,
} from '@steerprotocol/uniswap-position-quoter';
```

---

## Troubleshooting

### Common Issues

#### 1. "Protocol not detected" Error

**Problem:** The beacon name doesn't match any known protocol.

**Solution:** Verify your beacon name matches one of the supported protocols. Check the [Supported Protocols](#supported-protocols) section.

```typescript
// ❌ Wrong
beaconName: 'UniswapV3Custom';

// ✅ Correct
beaconName: 'UniswapV3';
```

#### 2. Missing Deployer Address for Blackhole

**Problem:** Using Blackhole protocol without providing deployer address.

**Solution:** Include the deployer address in `protocolParams`:

```typescript
const router = factory.createCustomRouter({
  beaconName: 'AlgebraIntegral_v2_0_Blackhole',
  quoterV2: quoterContract,
  poolObj: poolContract,
  protocolParams: {
    algebraIntegralV2Deployer: '0x...', // Required for Blackhole
  },
});
```

#### 3. Calculation Not Converging

**Problem:** `getSwapAmount` doesn't converge within max iterations.

**Solution:**

- Increase `maxIterations` (try 15-20)
- Check that positions are valid (lowerTick < upperTick)
- Verify token balances are sufficient
- Adjust `ratioErrorTolerance` if needed

```typescript
const result = await router.getSwapAmount(
  poolContract,
  positions,
  token0Balance,
  token1Balance,
  token0Address,
  token1Address,
  3000,
  20, // Increased from 10
  {
    ratioErrorTolerance: new Fraction(1, 1000), // More lenient
  },
);
```

#### 4. Incorrect Swap Direction

**Problem:** The calculated swap direction doesn't match expectations.

**Solution:** Verify your token balances and positions are correct. The SDK calculates the optimal direction based on the current state and desired positions.

```typescript
// Check the result
if (result.zeroForOne) {
  // Need to swap token0 → token1
  console.log('Swap token0 for token1');
} else {
  // Need to swap token1 → token0
  console.log('Swap token1 for token0');
}
```

### Best Practices

1. **Always validate inputs:** Ensure token balances, addresses, and positions are valid before calling `getSwapAmount`.

2. **Use appropriate iterations:** Start with 10 iterations for simple strategies, increase to 15-20 for complex multi-position strategies.

3. **Handle errors gracefully:** Wrap calls in try-catch blocks and handle potential contract call failures.

4. **Monitor price impact:** Use `maxPriceImpactBps` to prevent excessive slippage.

5. **Cache router instances:** Reuse router instances when possible rather than creating new ones for each calculation.

```typescript
// ✅ Good: Reuse router
const router = factory.createCustomRouter({...});
const result1 = await router.getSwapAmount(...);
const result2 = await router.getSwapAmount(...);

// ❌ Bad: Create new router each time
const result1 = await factory.createCustomRouter({...}).getSwapAmount(...);
const result2 = await factory.createCustomRouter({...}).getSwapAmount(...);
```

---

## Additional Resources

- **Full API Documentation:** See `API_GUIDE.md`
- **Factory Usage:** See `ROUTER_FACTORY_USAGE.md`
- **Logger Guide:** See `LOGGER_USAGE.md`
- **Testing Guide:** See `TESTING_GUIDE.md` (includes test package information)
- **Test Package:** `test-uniswap-position-quoter@1.0.4-test.1` - See [Test Package](#test-package) section above
- **Examples:** See `examples/` directory

---

## Support

- **GitHub Issues:** [Report bugs or request features](https://github.com/steerprotocol/uniswap-position-quoter/issues)
- **Documentation:** See the `docs/` directory for detailed guides
- **Discord:** Join the Steer Protocol community

---

**Built with ❤️ by [Steer Protocol](https://steer.finance)**
