diff --git a/README.md b/README.md index 533b282..61ca7e6 100644 --- a/README.md +++ b/README.md @@ -7,17 +7,9 @@ > Modern TypeScript SDK for integrating Uniswap V4 into your dapp. > **Early version:** API may change rapidly. -A developer-friendly library for interacting with Uniswap V4 contracts. This library provides a simple and flexible interface for common operations like adding liquidity, swapping tokens, and managing positions. +An abstraction layer built on top of the official Uniswap V4 SDK that simplifies integration by providing high-level methods for common operations. This library maintains compatibility with the official SDK's interfaces while reducing boilerplate and bundling related contract interactions. -## Features - -- 🚀 Simple and intuitive API -- 🔄 Support for all major Uniswap V4 operations -- 💰 Native token support -- 🔒 Permit2 integration for gasless approvals -- 📊 Flexible liquidity management -- 🔍 Built-in quote simulation -- 🛠 TypeScript support +While the official SDK provides primitives for pool creation, position management, and swap execution, this SDK abstracts away the complexity of composing these primitives. Common workflows like adding liquidity or executing swaps are reduced to single method calls. ## Installation @@ -45,102 +37,91 @@ const uniDevKit = new UniDevKitV4({ }); const pool = await uniDevKit.getPool({ - tokens: ["0xTokenA", "0xTokenB"], + currencyA: "0xTokenA", + currencyB: "0xTokenB", fee: 3000 }); const quote = await uniDevKit.getQuote({ - pool, - amountIn: "1000000000000000000" + poolKey: pool.poolKey, + amountIn: "1000000000000000000", + zeroForOne: true }); ``` ## Documentation Full API documentation with TypeDoc: [https://bootnodedev.github.io/uni-dev-kit](https://bootnodedev.github.io/uni-dev-kit) -## API Reference - -### Index -- [uniswap-dev-kit](#uniswap-dev-kit) - - [Features](#features) - - [Installation](#installation) - - [Quick Start](#quick-start) - - [Documentation](#documentation) - - [API Reference](#api-reference) - - [Index](#index) - - [`getPool`](#getpool) - - [`getQuote`](#getquote) - - [`getTokens`](#gettokens) - - [`getPosition`](#getposition) - - [`getPoolKeyFromPoolId`](#getpoolkeyfrompoolid) - - [`buildSwapCallData`](#buildswapcalldata) - - [`buildAddLiquidityCallData`](#buildaddliquiditycalldata) - - [`preparePermit2BatchCallData`](#preparepermit2batchcalldata) - - [`buildRemoveLiquidityCallData`](#buildremoveliquiditycalldata) - - [Basis Points Reference](#basis-points-reference) - - [Useful Links](#useful-links) - - [Development](#development) - - [Scripts](#scripts) - - [Contributing](#contributing) - - [Release](#release) - - [License](#license) - -### `getPool` -Retrieve a pool object from two tokens and a fee tier. +## Core Operations + +### Pool Management + +#### `getPool` +Fetches live pool state from the blockchain and instantiates a fully configured Pool object. Uses multicall to batch `V4StateView.getSlot0()` and `V4StateView.getLiquidity()` calls, and handles sorting token pairs to match the official SDK's conventions. + +**Without this SDK:** Manually call V4StateView.getSlot0() and V4StateView.getLiquidity(), construct PoolKey, sort tokens, then instantiate Pool with the fetched data. This method encapsulates all of that logic. + ```ts const pool = await uniDevKit.getPool({ - tokens: [tokenA, tokenB], + currencyA: "0xTokenA", + currencyB: "0xTokenB", fee: 3000 }); ``` -### `getQuote` -Simulate a swap to get `amountOut` and `sqrtPriceLimitX96`. -```ts -const quote = await uniDevKit.getQuote({ - pool, - amountIn: "1000000000000000000" -}); -``` +#### `getTokens` +Fetches ERC20 metadata for multiple tokens and returns Currency instances. Uses multicall to batch `symbol()`, `name()`, and `decimals()` calls across all tokens, and automatically handles native currency by instantiating Ether based on the chain ID. + +**Without this SDK:** Call erc20Abi.symbol(), name(), and decimals() for each token, handle native currency separately, construct Token or Ether instances manually. This method handles all of that logic. -### `getTokens` -Retrieve token metadata. ```ts const tokens = await uniDevKit.getTokens({ - addresses: ["0x...", "0x..."] + addresses: ["0xTokenA", "0xTokenB"] }); ``` -### `getPosition` -Get details about a Uniswap V4 LP position. +#### `getQuote` +Simulates a swap through V4Quoter to get the expected amountOut and gas estimate. Returns structured data ready to use in your application. + +**Without this SDK:** Manually construct quote parameters with poolKey, encode swap details, call V4Quoter contract, decode and structure the results yourself. + ```ts -const position = await uniDevKit.getPosition({ - tokenId: 123 +const quote = await uniDevKit.getQuote({ + poolKey: pool.poolKey, + amountIn: "1000000000000000000", + zeroForOne: true }); +// Returns { amountOut, estimatedGasUsed, timestamp } ``` -### `getPoolKeyFromPoolId` -Retrieve the `PoolKey` object for a given pool ID. +#### `getPositionDetails` +Fetches position state from the PositionManager and decodes the tick range, liquidity, and pool key. Uses multicall to batch `V4PositionManager.getPoolAndPositionInfo()` and `V4PositionManager.getPositionLiquidity()` calls, and handles data decoding. + +**Without this SDK:** Call getPoolAndPositionInfo() and getPositionLiquidity() separately, decode packed position data, extract tick bounds and pool key manually. + ```ts -const poolKey = await uniDevKit.getPoolKeyFromPoolId({ - poolId: "0x..." -}); +const position = await uniDevKit.getPositionDetails("123"); +// Returns { tokenId, tickLower, tickUpper, liquidity, poolKey } ``` -### `buildSwapCallData` -Construct calldata for a Universal Router swap. +### Swap Operations + +#### `buildSwapCallData` +Generates Universal Router calldata for executing swaps. Encapsulates V4Planner usage to build swap actions, settle operations, and take operations. Supports Permit2 integration for gasless approvals. + +**Without this SDK:** Instantiate V4Planner, call addAction(), addSettle(), and addTake() in sequence, manually encode Permit2 data if needed, construct command array and inputs array, encode Universal Router execute() call. This method wraps all of that complexity. ```ts // Basic swap -const { calldata, value } = await uniDevKit.buildSwapCallData({ - tokenIn: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", // USDC - amountIn: parseUnits("100", 6), // 100 USDC - pool: pool, - slippageTolerance: 50, // 0.5% - recipient: "0x..." +const { calldata, value } = uniDevKit.buildSwapCallData({ + pool, + amountIn: "1000000000000000000", + zeroForOne: true, + recipient: "0x...", + amountOutMinimum: "900000000000000000" }); -// Swap with permit2 +// With Permit2 const permitData = await uniDevKit.preparePermit2Data({ token: tokenIn, spender: uniDevKit.getContractAddress('universalRouter'), @@ -155,95 +136,100 @@ const signature = await signer._signTypedData( const permitWithSignature = permitData.buildPermit2DataWithSignature(signature); -const { calldata, value } = await uniDevKit.buildSwapCallData({ - tokenIn, - amountIn, +const { calldata, value } = uniDevKit.buildSwapCallData({ pool, - slippageTolerance: 50, + amountIn, + zeroForOne: true, recipient, + amountOutMinimum, permit2Signature: permitWithSignature }); ``` -### `buildAddLiquidityCallData` -Build calldata to add liquidity to a pool. +### Liquidity Operations + +#### `buildAddLiquidityCallData` +Generates V4PositionManager.mint() calldata with intelligent handling of pool creation vs. existing pools. Automatically constructs Position instances, calculates sqrtPriceX96 for new pools, handles native currency wrapping/unwrapping, and integrates with Permit2 for batch approvals. + +**Without this SDK:** Check pool liquidity to determine if creating new pool, calculate sqrtPriceX96 manually for new pools, choose between Position.fromAmounts/fromAmount0/fromAmount1, handle native currency, construct V4PositionManager.addCallParameters with all edge cases, optionally prepare Permit2 batch data. This method handles all that complexity. + ```ts -// Without permit +// Adding to existing pool const { calldata, value } = await uniDevKit.buildAddLiquidityCallData({ pool, amount0: "100000000", - amount1: "50000000000000000", recipient: "0x...", slippageTolerance: 50 }); -// With Permit2 batch approval -const permitData = await uniDevKit.preparePermit2BatchData({ - tokens: [pool.token0.address, pool.token1.address], - spender: uniDevKit.getContractAddress('positionManager'), - owner: userAddress -}); - -const signature = await signer.signTypedData( - permitData.toSign.domain, - permitData.toSign.types, - permitData.toSign.values -); - -const permitWithSignature = permitData.buildPermit2BatchDataWithSignature(signature); - +// Creating new pool (both amounts required) const { calldata, value } = await uniDevKit.buildAddLiquidityCallData({ pool, - amount0: parseUnits("100", 6), + amount0: "100000000", + amount1: "50000000000000000", recipient: "0x...", - permit2BatchSignature: permitWithSignature -}); - -const tx = await sendTransaction({ - to: uniDevKit.getContractAddress('positionManager'), - data: calldata, - value + slippageTolerance: 50 }); ``` -### `preparePermit2BatchCallData` -Construct a Permit2 batch approval for gasless interactions. +#### `buildRemoveLiquidityCallData` +Generates V4PositionManager.burn() calldata for removing liquidity from positions. Automatically fetches current position state, handles liquidity percentage calculations, and applies slippage tolerance. + +**Without this SDK:** Fetch position details from PositionManager, decode position data, calculate liquidity percentage, construct Position instance, call V4PositionManager.removeCallParameters with all parameters. This method encapsulates that workflow. + ```ts -const permitData = await uniDevKit.preparePermit2BatchCallData({ - tokens: [tokenA.address, tokenB.address], - spender: uniDevKit.getContractAddress('positionManager'), - owner: userAddress +const { calldata, value } = await uniDevKit.buildRemoveLiquidityCallData({ + liquidityPercentage: 10000, // 100% + tokenId: '123', + slippageTolerance: 50 }); ``` -### `buildRemoveLiquidityCallData` -Build calldata to remove liquidity from a pool. +#### `buildCollectFeesCallData` +Generates V4PositionManager.collect() calldata for collecting accrued fees from positions. Automatically fetches position details and constructs the collect parameters with proper recipient and hook data handling. + +**Without this SDK:** Fetch position details from PositionManager, decode position data, construct Position instance, manually set up collect parameters with recipient and hook data, call V4PositionManager.collectCallParameters. + ```ts -const { calldata, value } = await uniDevKit.buildRemoveLiquidityCallData({ - liquidityPercentage: 10_000, // 100% +const { calldata, value } = await uniDevKit.buildCollectFeesCallData({ tokenId: '123', - slippageTolerance: 50, // 0.5% + recipient: "0x...", }); +``` + +### Permit2 Integration + +#### `preparePermit2Data` +Prepares single-token Permit2 approval data for swaps. Returns structured data ready for EIP-712 signing. -const tx = await sendTransaction({ - to: uniDevKit.getContractAddress('positionManager'), - data: calldata, - value +```ts +const permitData = await uniDevKit.preparePermit2Data({ + token: tokenIn, + spender: uniDevKit.getContractAddress('universalRouter'), + owner: userAddress }); ``` -#### Basis Points Reference +#### `preparePermit2BatchData` +Prepares batch Permit2 approval data for multiple tokens. Uses multicall to batch `Permit2.allowance()` calls across all tokens and returns structured data ready for EIP-712 signing. Used for adding liquidity to pools requiring multiple token approvals. + +**Without this SDK:** Call Permit2.allowance() for each token, construct PermitBatch struct manually, fetch current block timestamp to calculate sigDeadline, use Permit2 SDK to prepare typed data with correct domain and values. This method handles all of that setup. + +```ts +const permitData = await uniDevKit.preparePermit2BatchData({ + tokens: [tokenA.address, tokenB.address], + spender: uniDevKit.getContractAddress('positionManager'), + owner: userAddress +}); -Throughout the library, percentages are represented in basis points (bps). For example, when setting a slippage tolerance of 0.5%, you would use `50` bps. Here's a quick reference: +const signature = await signer._signTypedData( + permitData.toSign.domain, + permitData.toSign.types, + permitData.toSign.values +); -| Basis Points (bps) | Fraction | Percentage | -|:------------------:|:--------:|:----------:| -| 1 | 1/10_000 | 0.01% | -| 10 | 10/10_000 | 0.1% | -| 100 | 100/10_000 | 1% | -| 500 | 500/10_000 | 5% | -| 1000 | 1000/10_000 | 10% | -| 10_000 | 10_000/10_000 | 100% | +const permitWithSignature = permitData.buildPermit2BatchDataWithSignature(signature); +``` ## Useful Links - [Uniswap V4 Docs](https://docs.uniswap.org/contracts/v4/overview) diff --git a/package.json b/package.json index c4dd661..f5869b2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "uniswap-dev-kit", - "version": "1.1.0", + "version": "1.1.1", "description": "A modern TypeScript library for integrating Uniswap into your dapp.", "main": "dist/index.js", "types": "dist/index.d.ts",