A basic Constant Product Automated Market Maker, a Uniswap V2 Price Oracle and some math related to CPAMM
[![Contributors][contributors-shield]][contributors-url] [![Forks][forks-shield]][forks-url] [![Stargazers][stars-shield]][stars-url] [![Issues][issues-shield]][issues-url] [![MIT License][license-shield]][license-url] [![LinkedIn][linkedin-shield]][linkedin-url]
Constant Product Automated Market Maker
A basic Constant Product AMM
Explore the docs »
View Demo · Report Bug · Request Feature
Table of Contents
About The Project
This project shows a basic Constant Product Automated Market Maker.
You will also find all the Maths used in Uniswap V2 and Uniswap V3
Built With
- [![Hardhat][Hardhat]][Hardhat-url]
- [![Ethers][Ethers.js]][Ethers-url]
Getting Started
To get a local copy up and running follow these simple example steps.
Prerequisites
- npm
npm install npm@latest -g
- hardhat
npm install --save-dev hardhat
npm install @nomiclabs/hardhat-ethers @nomiclabs/hardhat-waffle
run:
npx hardhat
verify:
npx hardhat verify --network goerli "contract address" "pair address"
Installation
- Clone the repo
git clone https://github.com/Aboudoc/Constant-Product-AMM.git
- Install NPM packages
npm install
- Dependencies
npm i @uniswap/v2-core @uniswap/v2-periphery
Usage
If you need testnet funds, use the Alchemy testnet faucet.
This project shows a basic example of Constant Product AMM
Constant Product AMM
Constant product AMM (automated market maker) is a decentralized exchange where 2 tokens are traded.
Why is it called constant product?
Swap - How many dy for dx?
Uniswap trading fee = 0.3%
Add liquidity - How many dx, dy to add?
Add liquidity - How many shares to mint?
Motivation:
What is L0 and L1
Simplify the equation:
Conclusion
Remove liquidity - How many tokens to withdraw?
Time Weighted Average Price
How do we compute the Time Weighted Average Price from Tk to Tn?
Uniswap V2 Price Oracle
We saw the math to calculate TWAP, you can also calculate it in Uniswap V2 using Solidity.
This is the purpose of UniswapV2Twap contract
In Uniswap V2, the numbers are represented in decimals, but Solidity does not have any decimal datatypes, so we'ell be using FixedPoint.sol library
The datatype FixedPoint.uq112x112 represents a decimal number as follows:
range: [0, 2e112 - 1] resolution: 1 / 2e112
The IUniswapV2Pair contract is the pair contract that holds the two tokens and does the swaps
Constructor
Since we're using Solidity 0.6, constructor must be declared as public
- We set the pair first
- We set
token0andtoken1frompair - We record
price0CumulativeLastandprice1CumulativeLastand the last time that these variables were updated:blockTimestampLastusinggetReserves(), it returns 3 outputs but we only need the third
Function update
- To get the current
price0Cumulative, currentprice1CumulativeandblockTimestamp, we callcurrentCumulativePrices()onUniswapV2OracleLibraryby passing in the address of the pair - Calculate how much time has elapsed since the last time we called
update(). This time elapsed must be greather than theperioddefined as state variable - Calculate the price averages (
price0Averageandprice1Average) by taking the current price cumulative, substracting it from the last price cumulative, and dividing it over the time elapsed. Cast it into aFixedPoint
up112x112 is a struct and the input for this struct must be a uint224 => cast the calcul expression
The expression below, used to calculate the price averages, is the exact same equation that we derived from How do we compute the Time Weighted Average Price from Tk to Tn?
price0Average = FixedPoint.uq112x112(
uint224((price0Cumulative - price0CumulativeLast) / timeElapsed)
);
price1Average = FixedPoint.uq112x112(
uint224((price1Cumulative - price1CumulativeLast) / timeElapsed)
);
Keep in mind that we don't care if the numbers overflows This is why we are not using SafeMath
- Finally, update the state variables:
price0CumulativeLast,price1CumulativeLastandblockTimestampLast
Function consult
Giving the token and the amount of token put in, this function will calculate the amount out using the price0Average and price1Average
- The token must be token0 or token1
- Compute
amountOutusingprice0AverageandamountInargument (.mul). We have to put it back to uint using a function calleddecode144()
Additional Reading
You can find more in the following article Using the new Uniswap v2 as oracle
Constant Product AMM Spot Price
P = Y / X from geometry
P = Y / X from calculus
Constant Product AMM Spot Price Examples
Uniswap V2
Uniswap V3
Uniswap V3 Twap
However, virtual reserves are not tracked in Uni V3
The Tick that is used to compute the current price is tracked in Uni V3
Example
Pricing difference between Uni V2 and Uni V3
Uniswap V3 Price Oracle
Constructor
- Set
token0andtoken1 - get the pool by calling
getPool()on,IUniswapV3Factoryand set the state variable - To call
getPool()we need to pass in 3 parameters: address of the tokens, and the fee => set factory address and fee (uint24) - Check that the pool is a valid pool (not address(0)) and finally set it (state variable)
Function estimateAmountOut
- Define
tokenInandtokenOut - Get the average tick: Call
consult()onOracleLibrarylike so:
(int24 tick, ) = OracleLibrary.consult(pool, secondsAgo);
We'll copy paste the code to compute tick directly from the library to save some gas
- Call
GetQuoteAtTick()onOracleLibraryto set theamountOut
Forking mainnet
hardhat.config.js
networks: {
hardhat: {
forking: {
url: https://eth-mainnet.alchemyapi.io/v2/${process.env.ALCHEMYAPIKEY},
},
},
}
.env
ALCHEMYAPIKEY=...
terminal1
ALCHEMYAPIKEY=...
npx hardhat node --fork https://eth-mainnet.g.alchemy.com/v2/$ALCHEMYAPIKEY
terminal2
npx hardhat test --network localhost
Geometric Mean
Example
Comapraison With Arithmetic Mean
Uniswap V3 TWAP and Geometric Mean
Uniswap V3 TWAP Inverse Price
Not in Uniswap V2
Note
This contract assumes that token0 and token1 both have same decimals
Consider fees = 0.3%
Further reading
(...soon)
Sources
Roadmap
- [ ] Uniswap V3 TWAP
- [ ] Further reading
- [ ] Deploy script
- [ ] Unit test
Contributing
Contributions are what make the open source community such an amazing place to learn, inspire, and create. Any contributions you make are greatly appreciated.
If you have a suggestion that would make this better, please fork the repo and create a pull request. You can also simply open an issue with the tag "enhancement". Don't forget to give the project a star! Thanks again!
- Fork the Project
- Create your Feature Branch (
git checkout -b feature/AmazingFeature) - Commit your Changes (
git commit -m 'Add some AmazingFeature') - Push to the Branch (
git push origin feature/AmazingFeature) - Open a Pull Request
License
Distributed under the MIT License. See LICENSE.txt for more information.
Contact
Reda Aboutika - @twitter - reda.aboutika@gmail.com
Project Link: https://github.com/Aboudoc/Constant-Product-AMM.git
Acknowledgments
[contributors-shield]: https://img.shields.io/github/contributors/Aboudoc/Constant-Product-AMM.svg?style=for-the-badge [contributors-url]: https://github.com/Aboudoc/Constant-Product-AMM/graphs/contributors [forks-shield]: https://img.shields.io/github/forks/Aboudoc/Constant-Product-AMM.svg?style=for-the-badge [forks-url]: https://github.com/Aboudoc/Constant-Product-AMM/network/members [stars-shield]: https://img.shields.io/github/stars/Aboudoc/Constant-Product-AMM.svg?style=for-the-badge [stars-url]: https://github.com/Aboudoc/Constant-Product-AMM/stargazers [issues-shield]: https://img.shields.io/github/issues/Aboudoc/Constant-Product-AMM.svg?style=for-the-badge [issues-url]: https://github.com/Aboudoc/Constant-Product-AMM/issues [license-shield]: https://img.shields.io/github/license/Aboudoc/Constant-Product-AMM.svg?style=for-the-badge [license-url]: https://github.com/Aboudoc/Constant-Product-AMM/blob/master/LICENSE.txt [linkedin-shield]: https://img.shields.io/badge/-LinkedIn-black.svg?style=for-the-badge&logo=linkedin&colorB=555 [linkedin-url]: https://www.linkedin.com/in/r%C3%A9da-aboutika-34305453/?originalSubdomain=fr [product-screenshot]: https://ethereum.org/static/28214bb68eb5445dcb063a72535bc90c/9019e/hero.webp [Hardhat]: https://img.shields.io/badge/Hardhat-20232A?style=for-the-badge&logo=hardhat&logoColor=61DAFB [Hardhat-url]: https://hardhat.org/ [Ethers.js]: https://img.shields.io/badge/ethers.js-000000?style=for-the-badge&logo=ethersdotjs&logoColor=white [Ethers-url]: https://docs.ethers.org/v5/ [Vue.js]: https://img.shields.io/badge/Vue.js-35495E?style=for-the-badge&logo=vuedotjs&logoColor=4FC08D [Vue-url]: https://vuejs.org/ [Angular.io]: https://img.shields.io/badge/Angular-DD0031?style=for-the-badge&logo=angular&logoColor=white [Angular-url]: https://angular.io/ [Svelte.dev]: https://img.shields.io/badge/Svelte-4A4A55?style=for-the-badge&logo=svelte&logoColor=FF3E00 [Svelte-url]: https://svelte.dev/ [Laravel.com]: https://img.shields.io/badge/Laravel-FF2D20?style=for-the-badge&logo=laravel&logoColor=white [Laravel-url]: https://laravel.com [Bootstrap.com]: https://img.shields.io/badge/Bootstrap-563D7C?style=for-the-badge&logo=bootstrap&logoColor=white [Bootstrap-url]: https://getbootstrap.com [JQuery.com]: https://img.shields.io/badge/jQuery-0769AD?style=for-the-badge&logo=jquery&logoColor=white [JQuery-url]: https://jquery.com