We’re continuing our series of educational articles and today we’re going to look at how to securely integrate AMM into your project!
We continue our series of educational articles and today we’ll look at AMM (Automatic Market Makers) integration tips. Using Uniswap model as an example, this article will look at the advice you’ll need to integrate AMM into your project as well as audit and even develop such integrations.
However, there will also be advice for other integrations. I promise it will be very interesting, informative, and concise all at the same time — we know you don’t have a lot of spare time, and we respect that!
We understand and respect your limited time, therefore we created a specific cheat sheet with nothing superfluous only for you! Check it out!
Though it is not the talk about the math under AMM models but a talk about some specific tricks within Uniswap V2 & Uniswap V3 contracts (as an example) which can be used to ensure a successful integration, you must first understand what we are connecting to. You will also find a list of tools and research for self-study, and we strongly recommend that you read it separately for better understanding!
I will also share our own observations and give some advice in the next article in our series, since our team has been working since 2016, we have accumulated quite a few of them!
But, first and foremost, I would like to express our sincere gratitude to the authors of all the resource materials, and of course our pessimistic.io auditors who have helped me so much by revealing needed information and lifting the curtain of secrecy.
We believe there is no one who doubts that the basis of any secure integration is a special approach to writing code. Consequently, this article will be focused only on those aspects that can be really useful for making your code safe and secure.
Therefore, below you will see not a typical article, but a Systematization of Knowledge — SoK, in which I will rely on Authors that I myself trust in this matter, and of course our pessimistic.io auditors! Let’s get started!
By the way, there are some vacant slots now so if your project needs an audit — feel free to write to us, visit our public reports page here. Let’s get in touch: gm@pessimistic.io!
An Automated Market Maker (AMM) is the underlying protocol that powers a decentralized exchange (DEX) by enabling assets to be traded by using crypto liquidity pools as counterparties, instead of a traditional market of buyers and sellers.
In AMMs, digital assets can be traded without requiring permission, which eliminates the need for middlemen. Smart contracts, liquidity pools, and liquidity providers are used by the decentralized exchange protocols to facilitate transactions.
Automated market makers were initially introduced by Vitalik Buterin in 2017. A few years later, the first AMM models were launched. Not only have they severely improved the capabilities of existing decentralized exchanges, but AMMs have also made it possible for DeFi to exist in the first place. Attractive yields for providing liquidity were one of the main reasons why market participants switched to DeFi at all.
Resource | Uniswap is a prime example of how automated market makers work
There are many types of AMMs, the most popular one being Constant Product Automated Market Maker (CPAMM), which is also used by Uniswap. In the following article piece, I’ll give audit and development tips, as well as put together well-known resources in the SoK! Let’s get moving forward!
Since our team has been working since 2016, we have accumulated quite a few observations, which we will share below along with several security advices.
We understand and respect your limited time, therefore we created a specific cheat sheet with nothing superfluous only for you! Check it out!
uniswapV3SwapCallback should check that it is called by the correct uniswap3 pool (otherwise one can pretend/spoof to be a pool and take the money). The standard way to check is to call Factory. An alternative way is to calculate this address via CREATE2 (Check out more on this topic here);
User safety when interacting with Uniswap is ensured by Router, Pair contract is more difficult to use it directly (easy to make integration mistakes, e.g. not checking slippage or not taking all the money). EoA cannot in general work securely with Pair!
You don’t need to rely on the balance (or rebalancing) of a pair. It can be manipulated via flash-loan, or a cyclic swap path (AB>BC>CA>AB). Attack example;
You don’t have to rely on the pair’s reserves. They can be manipulated by normal swap (unbalancing the pool, then calling a vulnerable contract, and finally putting the pool back in place);
If UniswapV2Pair.swap() is being done, the token_in must be sent to the pair first, since transferFrom() is not done there, otherwise it will revert due to insufficient balance.
Don’t forget that decimals can vary!
You can’t hardcode or limit the price — because of a potential bug or delay some of the tokens may start costing 0, the system must adequately handle this;
For all swaps, the minReturn parameter must not equal 0;
Sometimes it is possible to do swaps in pools that have not yet been finalized (liquidity in only one token). This may allow one to exchange all liquidity for 0 tokens, or conversely, lose the tokens it contributes. Check out balancer audit report by trail of bits, issue 1;
Pools should not allow deflationary tokens (take commissions for transfers) as this opens up arbitrage opportunities. Example of an attack;
Also, elastic supply tokens should not be allowed for the same reason. A blocklist of tokens can be implemented to counter this issue;
The contract may have a surplus of tokens, and the contract allows you to take them out. transfer(balanceOf(this) — oldBalance). Make sure that oldBalance is correct (e.g., cannot be 0 if the contract stores these tokens). Check out this example;
Visibility of private and internal does not hide data. Variable values are readable from off-chain. Check out an article on this topic;
Public visibility for variables creates getters and the code of getters takes up space in the contract. When optimizing gas, you can remove Public visibility for some variables (unused or already read in some other functions) and thus reduce gas consumption during contract execution;
If the project allows users to create pools, the commission percentage for swaps should be limited to something adequate (10% in Balancer), or prohibit changing the commission after creation. A pool creator can spot a big swap and front-run the commission by 99%.
In recent months we have been actively developing our own Slither detectors to help with code review and audit process. More recently, we have released several new detectors and we encourage you to use them for your initial internal audit, particularly the Read-Only Reentrancy and UniswapV2 Integration detectors.
Please let us know if you have discovered an issue/bug/vulnerability via our custom Slither detectors. You may contact us via opening a PR/Issue or directly, whichever is more convenient for you!
But let’s now get back to the point of our conversation today… Simply put, our detectors are a kind of automation of the checks implemented in the checklist, their main purpose is to look for issues and assist the code auditor!
So, UniswapV2 Integration detector is disabled by default. Use --detect pess-uni-v2
to enable the detector and it will check the correctness of UniswapV2 integration in the contract.
Let’s check out test scenario and each of the presented detectors in-depth:
Impact — High;
Description — The pair balance value (or its’ change) can be manipulated with a flash-loan or a cyclical swap path;
Recommendation — Do not rely directly on the pair balance value.
Impact — Medium;
Description — The protocol is secured with the Router contract. The direct usage of the Pair is dangerous due to its complexity;
Recommendation — Use the Router contract instead of using the Pair contract directly.
Impact — Medium;
Description — All swaps must not have parameter minReturn equal to 0;
Recommendation — Put a minReturn parameter of swaps different from 0.
Impact — Medium;
Description — All swaps must not have parameter maxReturn equal to max;
Recommendation — Put a maxReturn parameter of swaps different from max.
Impact — Medium;
Description — Pools must not allow deflationary (tokens that take fees for transfer) and elastic supply tokens which make arbitrage possible;
Recommendation — Do not ever use deflationary and rebase tokens in UniswapV2 integrations.
We won’t go into length about each of our detectors in this post, but we will offer a couple of links at the conclusion for you to investigate further, so, check out:
If you have any further questions or suggestions, please join our Discord Server or Telegram chat. We hope to see you there, and we intend to support the community and its initiatives!
How Uniswap Works: An overview of the Uniswap Exchange protocol Token Swap Smart Contracts
Simple, intentionally-limited versions of web3 protocols & apps
Decentralised Finance and Automated Market Making: Execution and Speculation
Decentralized Exchanges: The Profitability Frontier of Constant Product Market Makers
We hope that this article was informative and useful for you! Thank you for reading!
What instruments should we review? What would you be interested in reading about? Please leave your comments, we will be happy to answer them, and the best answers and questions may be included in the next article!
By the way, there are some vacant slots now so if your project needs an audit — feel free to write to us, visit our public reports page here. Let’s get in touch: gm@pessimistic.io!
Support is very important to me, with it I can do what I love — educating users!
If you want to support my work, please, consider donating me:
0xB25C5E8fA1E53eEb9bE3421C59F6A66B786ED77A or officercia.eth — ETH, BSC, Polygon, Optimism, Zk, Fantom, etc
4AhpUrDtfVSWZMJcRMJkZoPwDSdVG6puYBE3ajQABQo6T533cVvx5vJRc5fX7sktJe67mXu1CcDmr7orn1CrGrqsT3ptfds — Monero XMR
The cover for this article — made by my good friend and artist (contemporary & NET.art) — RegulLion