8.2 How to Swap
Last updated
Last updated
Uniswap v3 bases its logical architecture on a series of smart contracts, including Factory, Router, Pair, PairERC20 and Library.
Each pair of tokens has its own liquidity pool initialized by the Smart Contract Factory, and initial deposits are made to generate the liquidity necessary to ensure smooth exchanges for users.
The exchange rates are calculated based on a formula called the "constant product formula", in which the values of the tokens are evaluated based on supply and demand moving along a predetermined curve in the following graph:
The constant product formula, x * y = k, relates for each pair of tokens a curve in the graph on which the exchange rate between the two assets moves. If, for example, token B is in high demand and the quantities available in the corresponding pool decrease greatly, the price will settle at the extreme left of the graph, increasing the price exponentially.
If instead the opposite happened, with token A in high demand and token B does not have great demand and has wide availability, the relationship of price will move towards right.
In the context of Uniswap v3 a basic swap can take place with the following function:
function swap(uint amount0Out, uint amount1Out, address to, bytes calldata data);
So in the case of receiving tokens the function requires the caller to specify how many output tokens he wants to receive via the amount{0,1} parameter.
For the case of sending tokens the mechanism is slightly more complex: the totals of the pairs are checked at the end of each interaction, and then at the beginning of a new interaction the difference with the saved values is calculated in order to determine the amount of tokens sent to the swap contract with current interaction.
The basic idea is that the tokens that you want to swap are to be sent before the swap is called.
Consequently, to safely use the swap function it must be called by a second smart contract to prevent the non-atomic nature of the transaction consisting of token transaction plus call to the swap function from being vulnerable to arbitrage.
function swapExactTokensForTokens(
uint amountIn,
uint amountOutMin,
address[] calldata path,
address to,
uint deadline
) external returns (uint[] memory amounts);
The methods swapExactTokensForTokens and swapTokensForExactTokens on the Router v3 smart contract can be called to perform direct swaps from ERC20 to ERC20 tokens.
The "exact" terminology used in the method names represents the token desired to be achieved by the swap. In a DAI to ETH swap where a particular amount of ETH is required in return, swapTokensForExactTokens would be used. On the other hand, if an exact amount of DAI is required to be exchanged for the corresponding ETH value, swapExactTokensForTokens is used instead. This convention is used in all Uniswap v3 smart contracts. In addition to direct exchanges, users still have the option to swap between two tokens with ETH as an intermediate token. This becomes useful when there is no pool for the input and output token, but there is a pool between ETH and both tokens. We pick up on the previous example of swapping between DAI and LINK, this time undirected:
The corresponding Solidity methods that allow such transactions are swapExactETHForTokens and swapETHForExactTokens, along with swapTokensForExactETH and swapExactTokensForETH.
Another possibility for undirected swap from ERC20 to ERC20 without common swap pairs is a series of intermediate swaps via other ERC20s to finally arrive at the desired token, as seen in the following figure:
It is worth noting that there is no Uniswap v3 smart contract that implements this particular swap mode to date; rather, it is necessary for higher-level proprietary applications to implement this functionality manually by performing a number of basic Uniswap methods.
Swaps are based on the mechanism of the Dutch auction, or down auction, in which initially the token to be exchanged is placed at a price high enough to deter all bidders and is progressively lowered until there is no one willing to buy at the last current price.