Router
The router is a smart contract for swapping the tokens through the paths.
Function onUpgrade
This function was introduced in the Facet Swap contracts. It can be used to reset the contract owner and the pause state of the contract. The function does not enforce access control. We were informed by the Facet team that the router contracts will be present in the genesis state and already initialized to version 3. Since the function is decorated with the reinitializer(3)
modifier, it will not be possible to call it and change the owner or pause state.
Since failing to deploy the contracts with the correct version would create a severe security issue, we recommend adding tests to the code that generate the genesis state to ensure all routers are initialized correctly and reject calls to onUpgrade
.
This observation was addressed in commit , which removed the onUpgrade
function from the router contract.
Functions _swapExactTokensForTokens
and swapExactTokensForTokens
The internal function _swapExactTokensForTokens
is functionally equivalent to the external function swapExactTokensForTokens
from the Uniswap codebase. Since the function is now internal, it cannot be directly invoked, but must be invoked through the public swapExactTokensForTokens
function. It performs a swap of an exact quantity of input tokens, in exchange for a minimum desired quantity of output tokens.
The swapExactTokensForTokens
function is the new entry point for this functionality and differs from the original entry point in the following ways:
The initial input or the final output asset must be WETH.
A fee is charged either on the input or output amount, depending on which one is WETH; if both are WETH, the fee is charged on the input.
The fee is determined by the protocolFeeBPS
parameter, which is configurable by the contract owner.
We note that the fee logic is not clear, particularly in the case where both the input and output assets are WETH. We recommend adding an assertion that ensures the WETH balance of the contract at the end of the function execution is greater than or equal to the balance at the start, as a defense-in-depth mechanism to prevent possible exploits from stealing protocol fees. This recommendation was adopted in commit .
Functions _swapTokensForExactTokens
and swapTokensForExactTokens
The internal function _swapTokensForExactTokens
is functionally equivalent to the external function swapTokensForExactTokens
from the Uniswap codebase. Since the function is now internal, it cannot be directly invoked, but must be invoked through the public swapTokensForExactTokens
function. It allows to perform a swap of a maximum given quantity of input tokens, in exchange for a specific quantity of output tokens.
The swapTokensForExactTokens
function is the new entry point for this functionality and differs from the original entry point in the following ways:
The initial input or the final output asset must be WETH.
A fee is charged either on the input or output amount, depending on which one is WETH; if both are WETH, the fee is charged on the input.
The fee is determined by the protocolFeeBPS
parameter, which is configurable by the contract owner.
We note that, similarly to swapExactTokensForTokens
, the fee logic is not clear, particularly in the case where both the input and output assets are WETH. We recommend adding an assertion that ensures the WETH balance of the contract at the end of the function execution is greater than or equal to the balance at the start, as a defense-in-depth mechanism to prevent possible exploits from stealing protocol fees. This recommendation was adopted in commit .
Function initialize
This function can be used to initialize the contract. It does not have a direct counterpart in the original Uniswap contracts.
It does not have security issues that can be exploited without preconditions; however, we note it must be called in the same transaction where the router contract is deployed, or, alternatively, the contract configuration must be checked to ensure no malicious call was made between the time the contract is deployed and the time it is initialized (in which case. the contract must be redeployed and reinitialized).
Functions addLiquidity
and _addLiquidity
The public function addLiquidity
corresponds to the external function addLiquidity
in the Uniswap contracts. It is functionally equivalent and can be used to add liquidity to a pool in exchange for LP tokens.
The internal function _addLiquidity
also matches its counterpart in the original code.
Function removeLiquidity
This function can be used to burn LP tokens for a given pair, getting back the corresponding assets.
Function _swap
This internal function can be used to execute a chain of swaps. It is functionally equivalent to the original Uniswap version, with the notable exception of the addition of a maximum swap path length. By default, the limit is initialized to three by the initialize
function, but it is read from the router contract storage, making it potentially configurable. However, we note that at the moment, there is no functionality that allows to change this limit, besides upgrading the contract.
Function _safeTransferFrom
This function can be used to perform a safeTransfer
ERC-20 call. The function does not match the version used by the original contracts and is less compatible with tokens that do not correctly implement the ERC-20 standard. This is not an issue provided that all assets used with Facet Swap correctly implement the ERC-20 standard.
Functions getAmountsOut
and getAmountsIn
These functions can be used to obtain the results of, respectively, getAmountOut
and getAmountIn
for each swap in a chain of swaps.
The bodies of the functions were inlined from UniswapV2Library. They are almost functionally equivalent to the original functions. The Facet versions introduced a limit on the swap path length.
Function getAmountOut
This function can be used to get the maximum output amount to be expected when swapping a specified amount of input asset, given the values of the pool reserves.
The function was inlined from UniswapV2Library, with modifications required to support the variable swap fee rate, which is obtained from the factory contract.
Function getAmountIn
This function can be used to get the minimum input amount needed to perform a swap resulting in a specified amount of output asset, given the values of the pool reserves.
The function was inlined from UniswapV2Library, with modifications required to support the variable swap fee rate, which is obtained from the factory contract.
Other functions inlined from UniswapV2Library
The following functions were inlined from UniswapV2Library and remain functionally equivalent to the original code. Minor changes were made to support accessing storage in a manner compatible with the chosen upgradability pattern and to replace references to Uniswap with references to Facet Swap.
quote
, which is used to convert an amount of asset into the counterpart, given the current reserves — note that this function does not actually perform a swap and does not account for fees or slippagegetReserves
, which is used to get the value of the reserves (cached balance) for a given pair of assetspairFor
, which is used to get the address of the pool for a given pair of assets from the factory contractsortTokens
, which is used to sort tokens in lexicographic order
Function calculateFeeAmount
This function can be used to compute the fees charged by the router to a given asset amount.
Function updateProtocolFee
This function can be used by the contract owner to update the fee percentage charged by the router.
Function withdrawFees
This function can be used by the contract owner to withdraw fees accrued by the router.
Functions pause
and unpause
These functions can be used to pause and unpause the contract. They were introduced by Facet Swap and work as intended work as intended with respect to the router contract, preventing swaps and adding or removing liquidity through the router.
We note, however, that the pause feature does not prevent calling the pools directly.
Function userStats
This function can be used to retrieve information about a given user related to a token pair:
User balances for the two assets
User LP token balance for the corresponding pool
Address of the pool
Pool reserve values
Names of the two assets
Function factory
This function can be used to retrieve the address of the factory contract. The Uniswap contract did not contain this function explicitly, as it was automatically generated by the compiler due to the factory
state variable being public
. Since the Facet Swap contracts adopt the chosen upgradability pattern, public getters are not generated by the Solidity compiler and had to be manually implemented.