diff --git a/docs/sdk/v3/guides/liquidity/05-collecting-fees.md b/docs/sdk/v3/guides/liquidity/05-collecting-fees.md index a070af76c..d5eb3a2c0 100644 --- a/docs/sdk/v3/guides/liquidity/05-collecting-fees.md +++ b/docs/sdk/v3/guides/liquidity/05-collecting-fees.md @@ -66,7 +66,3 @@ const txResponse = await position.collectFeesOnChain({ ``` After pressing the button, if someone has traded against our position, we should be able to note how the balance of USDC and DAI increases as we collect fees. - -## Next Steps - -The previous guides detail all the atomic steps needed to create and manage positions. However, these approaches may not use all of your desired currency. To ensure you are using your full funds while minimizing gas prices, check out our guide on [Swapping and Adding Liquidity](./05-swap-and-add-liquidity.md) in a single transaction! diff --git a/docs/sdk/v3/guides/liquidity/06-swap-and-add-liquidity.md b/docs/sdk/v3/guides/liquidity/06-swap-and-add-liquidity.md deleted file mode 100644 index d47e25c11..000000000 --- a/docs/sdk/v3/guides/liquidity/06-swap-and-add-liquidity.md +++ /dev/null @@ -1,212 +0,0 @@ ---- -id: swap-and-add -title: Swapping and Adding Liquidity ---- - -## Introduction - -This guide will cover how to execute a swap-and-add operation in a single atomic transaction. It is based on the [swap-and-add example](https://github.com/Uniswap/examples/tree/main/v3-sdk/swap-and-add-liquidity), found in the Uniswap code examples [repository](https://github.com/Uniswap/examples). To run this example, check out the examples's [README](https://github.com/Uniswap/examples/tree/main/v3-sdk/swap-and-add-liquidity) and follow the setup instructions. - -:::info -If you need a briefer on the SDK and to learn more about how these guides connect to the examples repository, please visit our [background](../01-background.md) page! -::: - -When adding liquidity to a Uniswap v3 pool, you must provide two assets in a particular ratio. In many cases, your contract or the user's wallet hold a different ratio of those two assets. In order to deposit 100% of your assets, you must first swap your assets to the optimal ratio and then add liquidity. - -However, the swap may shift the balance of the pool and thus change the optimal ratio. To avoid that, we can execute this swap-and-add liquidity operation in an atomic fashion, using a router. The inputs to our guide are the **two tokens** that we are pooling for, the **amount** of each token we are pooling for, the **amount** of each token to swap-and-add, and the Pool **fee**. - -The guide will **cover**: - -1. Setup a router instance -2. Configuring our ratio calculation -3. Calculating our currency ratio -4. Constructing and executing our swap-and-add transaction - -At the end of the guide, given the inputs above, we should be able swap-and-add liquidity using 100% of the input assets with the press of a button and see the change reflected in our position and the balance of our tokens. - -:::info -The SDKs that are used in the guide are now published by the [Uniswap Foundation](https://github.com/uniswapfoundation) instead of Uniswap Labs. -You can find a list of supported SDKs [here](https://www.npmjs.com/org/uniswapfoundation). -Make sure you don't mix SDKs published by Uniswap Labs and the Uniswap Foundation to avoid unpredictable behavior. -::: - -For this guide, the following Uniswap packages are used: - -- [`@uniswapfoundation/v3-sdk`](https://www.npmjs.com/package/@uniswapfoundation/v3-sdk) -- [`@uniswapfoundation/sdk-core`](https://www.npmjs.com/package/@uniswapfoundation/sdk-core) -- [`@uniswapfoundation/smart-order-router`](https://www.npmjs.com/package/@uniswapfoundation/smart-order-router) - -The core code of this guide can be found in [`swapAndAddLiquidity()`](https://github.com/Uniswap/examples/blob/main/v3-sdk/swap-and-add-liquidity/src/libs/liquidity.ts#L48). - -:::note -This guide assumes you are familiar with our [Minting a Position](./01-minting-position.md) guide. A minted position is required to add or remove liquidity from, so the buttons will be disabled until a position is minted. - -Also note that we do not need to give approval to the `NonfungiblePositionManager` to transfer our tokens as we will have already done that when minting our position. -::: - -## Setup a router instance - -The first step is to approve the `SwapRouter` smart contract to spend our tokens for us in order for us to add liquidity to our position: - -```typescript -// Give approval to the router contract to transfer tokens - const tokenInApproval = await approveTokenTransfer({ - contractAddress: V3_SWAP_ROUTER_ADDRESS, - tokenAddress: CurrentConfig.tokens.token0.address, - amount: TOKEN_AMOUNT_TO_APPROVE_FOR_TRANSFER, - signer: getWallet(), - }) - - const tokenOutApproval = await approveTokenTransfer({ - contractAddress: V3_SWAP_ROUTER_ADDRESS, - tokenAddress: CurrentConfig.tokens.token1.address, - amount: TOKEN_AMOUNT_TO_APPROVE_FOR_TRANSFER, - signer: getWallet(), - }) -``` - -We described the `getTokenTransferApproval` function [here](./02-minting-position.md#giving-approval-to-transfer-our-tokens). -We defined the address for the swaprouter in our `constants.ts` file. - -Then we can setup our router, the [`AlphaRouter`](https://github.com/Uniswap/smart-order-router/blob/97c1bb7cb64b22ebf3509acda8de60c0445cf250/src/routers/alpha-router/alpha-router.ts#L333), which is part of the [smart-order-router package](https://www.npmjs.com/package/@uniswapfoundation/smart-order-router). The router requires a `chainId` and a `provider` to be initialized. Note that routing is not supported for local forks, so we will use a mainnet provider even when swapping on a local fork: - -```typescript -import { ethers } from 'ethers' -import { AlphaRouter } from '@uniswapfoundation/smart-order-router' - -const provider = new ethers.providers.JsonRpcProvider(rpcUrl) - -const router = new AlphaRouter({ chainId: 1, provider }) -``` - -For a more detailed example, check out our [routing guide](../trading/03-routing.md). - -## Configuring our ratio calculation - -Having created the router, we now need to construct the parameters required to make a call to its `routeToRatio` function, which will ensure the ratio of currency used matches the pool's required ratio to add our total liquidity. This will require the following parameters: - -The first two parameters are the currency amounts we use as input to the `routeToRatio` algorithm: - -```typescript -import { CurrencyAmount } from '@uniswapfoundation/sdk-core' - -const token0CurrencyAmount = CurrencyAmount.fromRawAmount( - token0, - fromReadableAmount( - token0AmountToAdd, - token0.decimals - ) -) - -const token1CurrencyAmount = CurrencyAmount.fromRawAmount( - token1, - fromReadableAmount( - token1AmountToAdd, - token1.decimals - ) -) -``` - -Next, we will create a placeholder position with a liquidity of `1` since liquidity is still unknown and will be set inside the call to `routeToRatio`: - -```typescript -import { Pool, Position, nearestUsableTick } from '@uniswapfoundation/v3-sdk' - -const placeholderPosition = new Position{ - pool, - liquidity: 1, - tickLower: - nearestUsableTick(pool.tickCurrent, pool.tickSpacing) - - pool.tickSpacing * 2, - tickUpper: - nearestUsableTick(pool.tickCurrent, pool.tickSpacing) + - poolInfo.tickSpacing * 2 -} -``` - -We then need to create an instance of `SwapAndAddConfig` which will set additional configuration parameters for the `routeToRatio` algorithm: - -- `ratioErrorTolerance` determines the margin of error the resulting ratio can have from the optimal ratio. -- `maxIterations` determines the maximum times the algorithm will iterate to find a ratio within error tolerance. If max iterations is exceeded, an error is returned. The benefit of running the algorithm more times is that we have more chances to find a route, but more iterations will longer to execute. We've used a default of 6 in our example. - -```typescript -import { Fraction } from '@uniswapfoundation/sdk-core' -import { SwapAndAddConfig } from '@uniswapfoundation/smart-order-router' - -const swapAndAddConfig: SwapAndAddConfig = { - ratioErrorTolerance: new Fraction(1, 100), - maxIterations: 6, -} -``` - -Finally, we will create an instance of `SwapAndAddOptions` to configure which position we are adding liquidity to and our defined swapping parameters in two different objects: - -- **`swapConfig`** configures the `recipient` of leftover dust from swap, `slippageTolerance` and a `deadline` for the swap. -- **`addLiquidityOptions`** must contain a `tokenId` to add to an existing position - -```typescript -import { SwapAndAddOptions } from '@uniswapfoundation/smart-order-router' - -const swapAndAddOptions: SwapAndAddOptions = { - swapOptions: { - type: SwapType.SWAP_ROUTER_02, - recipient: address, - slippageTolerance: new Percent(50, 10_000), - deadline: Math.floor(Date.now() / 1000) + 60 * 20, - }, - addLiquidityOptions: { - tokenId: positionId, - }, -} -``` - -## Calculating our currency ratio - -Having constructed all the parameters we need to call `routeToRatio`, we can now make the call to the function: - -```typescript -import { SwapToRatioResponse } from '@uniswapfoundation/smart-order-router' - -const routeToRatioResponse: SwapToRatioResponse = await router.routeToRatio( - token0CurrencyAmount, - token1CurrencyAmount, - currentPosition, - swapAndAddConfig, - swapAndAddOptions -) -``` - -The return type of the function call is [SwapToRatioResponse](https://github.com/Uniswap/smart-order-router/blob/97c1bb7cb64b22ebf3509acda8de60c0445cf250/src/routers/router.ts#L121). If a route was found successfully, this object will have two fields: the status (success) and the `SwapToRatioRoute` object. We check to make sure that both of those conditions hold true before we construct and submit the transaction: - -```typescript -import { SwapToRatioStatus } from '@uniswapfoundation/smart-order-router' - -if ( - !routeToRatioResponse || - routeToRatioResponse.status !== SwapToRatioStatus.SUCCESS -) { - // Handle Failed Transaction -} -``` - -In case a route was not found, we return from the function a `Failed` state for the transaction. - -## Constructing and executing our swap-and-add transaction - -After making sure that a route was successfully found, we can now construct and send the transaction. The response (`SwapToRatioRoute`) will have the properties we need to construct our transaction object: - -```typescript -import { SwapToRatioRoute } from '@uniswapfoundation/smart-order-router' - -const route: SwapToRatioRoute = routeToRatioResponse.result -const transaction = { - data: route.methodParameters?.calldata, - to: V3_SWAP_ROUTER_ADDRESS, - value: route.methodParameters?.value, - from: address, -} - -const txRes = await wallet.sendTransaction(transaction) -``` - -If the transaction was successful, our swap-and-add will be completed! We should see our input token balances decrease and our position balance should be increased accordingly. diff --git a/docs/sdk/v3/guides/swaps/03-simulate-offchain.md b/docs/sdk/v3/guides/swaps/03-simulate-offchain.md index 738dee715..8bf35fc4e 100644 --- a/docs/sdk/v3/guides/swaps/03-simulate-offchain.md +++ b/docs/sdk/v3/guides/swaps/03-simulate-offchain.md @@ -181,4 +181,4 @@ This function is an abstraction of all the steps laid out in this guide and dire ## Next Steps -Now that you are familiar with simulating Trades and finding optimal routes offchain, we will demonstrate an alternative solution for routing in the [next guide](04-routing.md). +Now that you're familiar with trading, consider checking out our next guides on [pooling liquidity](../liquidity/01-position-data.md) to Uniswap! diff --git a/docs/sdk/v3/guides/swaps/04-routing.md b/docs/sdk/v3/guides/swaps/04-routing.md deleted file mode 100644 index b130ee3ce..000000000 --- a/docs/sdk/v3/guides/swaps/04-routing.md +++ /dev/null @@ -1,180 +0,0 @@ ---- -id: routing -title: Routing a Swap ---- - -## Introduction - -This guide will cover how to use Uniswap's smart order router to compute optimal routes and execute swaps. Rather than trading between a single pool, smart routing may use multiple hops (as many as needed) to ensure that the end result of the swap is the optimal price. It is based on the [routing code example](https://github.com/Uniswap/examples/tree/main/v3-sdk/routing), found in the Uniswap code examples [repository](https://github.com/Uniswap/examples). To run this example, check out the guide's [README](https://github.com/Uniswap/examples/blob/main/v3-sdk/routing/README.md) and follow the setup instructions. - -:::info -If you need a briefer on the SDK and to learn more about how these guides connect to the examples repository, please visit our [background](./01-background.md) page! -::: - -In this example we will trade between **WETH and USDC**, but you can configure your example to us any two currencies and amount of input currency. - -The guide will **cover**: - -1. Creating a router instance -2. Creating a route -3. Swapping using a route - -At the end of the guide, we should be able to create a route and and execute a swap between any two currencies tokens using the example's included UI. - -:::info -The SDKs that are used in the guide are now published by the [Uniswap Foundation](https://github.com/uniswapfoundation) instead of Uniswap Labs. -You can find a list of supported SDKs [here](https://www.npmjs.com/org/uniswapfoundation). -Make sure you don't mix SDKs published by Uniswap Labs and the Uniswap Foundation to avoid unpredictable behavior. -::: - -For this guide, the following Uniswap packages are used: - -- [`@uniswapfoundation/v3-sdk`](https://www.npmjs.com/package/@uniswapfoundation/v3-sdk) -- [`@uniswapfoundation/sdk-core`](https://www.npmjs.com/package/@uniswapfoundation/sdk-core) -- [`@uniswapfoundation/smart-order-router`](https://www.npmjs.com/package/@uniswapfoundation/smart-order-router) - -The core code of this guide can be found in [`routing.ts`](https://github.com/Uniswap/examples/blob/main/v3-sdk/routing/src/libs/routing.ts) - -The config, which we will use in some code snippets in this guides has this structure: - -```typescript -import { Token } from '@uniswapfoundation/sdk-core' - -interface ExampleConfig { - env: Environment - rpc: { - local: string - mainnet: string - } - wallet: { - address: string - privateKey: string - } - tokens: { - in: Token - amountIn: number - out: Token - } -} - -export const CurrentConfig: ExampleConfig = {...} -``` - -## Creating a router instance - -To compute our route, we will use the `@uniswapfoundation/smart-order-router` package, specifically the `AlphaRouter` class which requires a `chainId` and a `provider`. Note that routing is not supported for local forks, so we will use a mainnet provider even when swapping on a local fork: - -```typescript -import { AlphaRouter, ChainId } from '@uniswapfoundation/smart-order-router' - -const provider = new ethers.providers.JsonRpcProvider(rpcUrl) - -const router = new AlphaRouter({ - chainId: ChainId.MAINNET, - provider, -}) -``` - -## Creating a route - -We will use the [SwapRouter02](https://github.com/Uniswap/swap-router-contracts/blob/main/contracts/SwapRouter02.sol) for our trade. -This is a different SwapRouter contract than the one we used in the previous example. -In contrast to the contract we used previously, this on can execute swaps on both V3 Pools and V2 Pairs. -The `smart-order-router` package provides us with a `SwapOptionsSwapRouter02` interface, defining the wallet to use, slippage tolerance, and deadline for the transaction that we need to interact with the contract: - -```typescript -import { SwapOptionsSwapRouter02, SwapType } from '@uniswapfoundation/smart-order-router' -import { Percent } from '@uniswapfoundation/sdk-core' - -const options: SwapOptionsSwapRouter02 = { - recipient: CurrentConfig.wallet.address, - slippageTolerance: new Percent(50, 10_000), - deadline: Math.floor(Date.now() / 1000 + 1800), - type: SwapType.SWAP_ROUTER_02, -} -``` - -Like explained in the [trading guide](./02-trading.md#executing-a-trade), it is important to set the parameters to sensible values. - -Using these options, we can now create a trade (`TradeType.EXACT_INPUT` or `TradeType.EXACT_OUTPUT`) with the currency and the input amount to use to get a quote. For this example, we'll use an `EXACT_INPUT` trade to get a quote outputted in the quote currency. - -```typescript -import { CurrencyAmount, TradeType } from '@uniswapfoundation/sdk-core' - -const rawTokenAmountIn = fromReadableAmount( - CurrentConfig.currencies.amountIn, - CurrentConfig.currencies.in.decimals - ) - const currencyAmountIn = CurrencyAmount.fromRawAmount( - CurrentConfig.currencies.in, - rawTokenAmountIn - ) - -const route = await router.route( - currencyAmountIn, - CurrentConfig.currencies.out, - TradeType.EXACT_INPUT, - options -) -``` - -`route` and `route.methodParameters` are *optional* as the request can fail, for example if **no route exists** between the two Tokens or because of networking issues. -We check if the call was succesful: - -```typescript -if (!route || !route.methodParameters) { - // Handle failed request -} -``` - -Depending on our preferences and reason for the issue we could retry the request or throw an Error. - -## Swapping using a route - -First, we need to give approval to the `SwapRouter02` smart contract to spend our tokens for us: - -```typescript -import { ethers } from 'ethers' -... - -const wallet = new ethers.Wallet(privateKey, provider) -const tokenContract = new ethers.Contract( - CurrentConfig.tokens.in.address, - ERC20ABI, - wallet -) -const tokenApproval = await tokenContract.approve( - V3_SWAP_ROUTER_ADDRESS, - ethers.BigNumber.from(rawTokenAmountIn.toString()) -) -``` - -To be able to spend the tokens of a wallet, a smart contract first needs to get an approval from that wallet. -ERC20 tokens have an `approve` function that accepts the address of the smart contract that we want to allow spending our tokens and the amount the smart contract should be allowed to spend. - -We can get the **V3_SWAP_ROUTER_ADDRESS** for our chain from [Github](https://github.com/Uniswap/v3-periphery/blob/main/deploys.md). -Keep in mind that different chains might have **different deployment addresses** for the same contracts. -The deployment address for local forks of a network are the same as in the network you forked, so for a **fork of mainnet** it would be the address for **Mainnet**. - -We need to wait one block for the approval transaction to be included by the blockchain. - -Once the approval has been granted, we can now execute the trade using the route's computed calldata, values, and gas values: - -```typescript -const txRes = await wallet.sendTransaction({ - data: route.methodParameters.calldata, - to: V3_SWAP_ROUTER_ADDRESS, - value: route.methodParameters.value, - from: wallet.address, - maxFeePerGas: MAX_FEE_PER_GAS, - maxPriorityFeePerGas: MAX_PRIORITY_FEE_PER_GAS, -}) -``` - -After swapping, you should see the currency balances update in the UI shortly after the block is confirmed. - -You can find the full code in [`routing.ts`](https://github.com/Uniswap/examples/blob/main/v3-sdk/routing/src/libs/routing.ts). - -## Next Steps - -Now that you're familiar with trading, consider checking out our next guides on [pooling liquidity](../liquidity/01-position-data.md) to Uniswap!