---
title: "युनिस्वॅप-v2 कॉन्ट्रॅक्ट वॉक-थ्रू"
description: "युनिस्वॅप-v2 कॉन्ट्रॅक्ट कसे काम करते? ते तसे का लिहिले आहे?"
author: "ओरी पोमेरँट्झ"
tags:
  - solidity
  - डॅप्स
skill: intermediate
breadcrumb: "युनिस्वॅप v2 वॉक-थ्रू"
published: 2021-05-01
lang: mr
---
## परिचय {#introduction}

[युनिस्वॅप v2](https://app.uniswap.org/whitepaper.pdf) कोणत्याही दोन ERC-20 टोकन्स दरम्यान विनिमय बाजार (exchange market) तयार करू शकते. या लेखामध्ये आपण हा प्रोटोकॉल लागू करणाऱ्या कॉन्ट्रॅक्ट्सच्या सोर्स कोडवर नजर टाकू आणि ते अशा प्रकारे का लिहिले आहेत ते पाहू.

### युनिस्वॅप काय करते? {#what-does-uniswap-do}

मूलतः, वापरकर्त्यांचे दोन प्रकार आहेत: तरलता प्रदाते आणि व्यापारी.

_तरलता प्रदाते_ पूलला दोन टोकन्स प्रदान करतात ज्यांची अदलाबदल केली जाऊ शकते (आपण त्यांना **Token0** आणि **Token1** म्हणू). त्या बदल्यात, त्यांना एक तिसरे टोकन मिळते जे पूलच्या आंशिक मालकीचे प्रतिनिधित्व करते, ज्याला _तरलता टोकन_ म्हटले जाते.

_व्यापारी_ पूलमध्ये एका प्रकारचे टोकन पाठवतात आणि तरलता प्रदात्यांनी प्रदान केलेल्या पूलमधून दुसरे टोकन प्राप्त करतात (उदाहरणार्थ, **Token0** पाठवा आणि **Token1** प्राप्त करा). विनिमय दर पूलमध्ये असलेल्या **Token0** आणि **Token1** च्या सापेक्ष संख्येनुसार निर्धारित केला जातो. याव्यतिरिक्त, पूल तरलता पूलसाठी बक्षीस म्हणून एक छोटी टक्केवारी घेतो.

जेव्हा तरलता प्रदात्यांना त्यांची मालमत्ता परत हवी असते, तेव्हा ते पूल टोकन्स जाळू शकतात आणि बक्षिसांमधील त्यांच्या वाट्यासह त्यांचे टोकन्स परत मिळवू शकतात.

[अधिक सविस्तर वर्णनासाठी येथे क्लिक करा](https://docs.uniswap.org/contracts/v2/concepts/core-concepts/swaps/).

### v2 का? v3 का नाही? {#why-v2}

[युनिस्वॅप v3](https://app.uniswap.org/whitepaper-v3.pdf) हे एक अपग्रेड आहे जे v2 पेक्षा खूपच गुंतागुंतीचे आहे. आधी v2 शिकणे आणि नंतर v3 कडे जाणे सोपे आहे.

### कोर कॉन्ट्रॅक्ट्स विरुद्ध पेरिफेरी कॉन्ट्रॅक्ट्स {#contract-types}

युनिस्वॅप v2 दोन घटकांमध्ये विभागलेले आहे, एक कोर (core) आणि एक पेरिफेरी (periphery). या विभागणीमुळे कोर कॉन्ट्रॅक्ट्स, ज्यांच्याकडे मालमत्ता असते आणि त्यामुळे ते सुरक्षित असणे _आवश्यक_ असते, अधिक सोपे आणि ऑडिट करण्यासाठी सोपे होतात. व्यापाऱ्यांना आवश्यक असलेली सर्व अतिरिक्त कार्यक्षमता नंतर पेरिफेरी कॉन्ट्रॅक्ट्सद्वारे प्रदान केली जाऊ शकते.

## डेटा आणि नियंत्रण प्रवाह {#flows}

जेव्हा तुम्ही युनिस्वॅपच्या (Uniswap) तीन मुख्य क्रिया करता तेव्हा डेटा आणि नियंत्रणाचा प्रवाह असा असतो:

1. वेगवेगळ्या टोकन्समध्ये अदलाबदल करणे
2. मार्केटमध्ये तरलता जोडणे आणि जोडीच्या एक्सचेंजच्या ERC-20 तरलता टोकन्सच्या स्वरूपात बक्षीस मिळवणे
3. ERC-20 तरलता टोकन्स जाळणे आणि जोडीचे एक्सचेंज ट्रेडर्सना ज्या ERC-20 टोकन्सची अदलाबदल करण्याची परवानगी देते ते परत मिळवणे

### अदलाबदल {#swap-flow}

हा सर्वात सामान्य प्रवाह आहे, जो ट्रेडर्सद्वारे वापरला जातो:

#### कॉलर {#caller}

1. पेरिफेरी (periphery) खात्याला अदलाबदल करायच्या रकमेची मंजुरी द्या.
2. पेरिफेरी कॉन्ट्रॅक्टच्या अनेक अदलाबदल फंक्शन्सपैकी एकाला कॉल करा (कोणते फंक्शन कॉल करायचे हे ETH समाविष्ट आहे की नाही, ट्रेडर जमा करायच्या टोकन्सची रक्कम निर्दिष्ट करतो की परत मिळवायच्या टोकन्सची रक्कम निर्दिष्ट करतो, इत्यादींवर अवलंबून असते).
   प्रत्येक अदलाबदल फंक्शन `path` स्वीकारते, जो पार करायच्या एक्सचेंजेसचा एक अ‍ॅरे (array) असतो.

#### पेरिफेरी कॉन्ट्रॅक्टमध्ये (UniswapV2Router02.sol) {#in-the-periphery-contract-uniswapv2router02-sol}

3. मार्गावरील प्रत्येक एक्सचेंजवर ट्रेड केल्या जाणाऱ्या रकमा ओळखा.
4. मार्गावर पुनरावृत्ती (iterate) करते. वाटेतील प्रत्येक एक्सचेंजसाठी ते इनपुट टोकन पाठवते आणि नंतर एक्सचेंजच्या `swap` फंक्शनला कॉल करते.
   बहुतेक प्रकरणांमध्ये टोकन्ससाठी गंतव्य पत्ता हा मार्गावरील पुढील जोडीचा एक्सचेंज असतो. अंतिम एक्सचेंजमध्ये तो ट्रेडरने दिलेला पत्ता असतो.

#### कोअर कॉन्ट्रॅक्टमध्ये (UniswapV2Pair.sol) {#in-the-core-contract-uniswapv2pairsol-2}

5. कोअर कॉन्ट्रॅक्टची फसवणूक होत नाहीये आणि अदलाबदलीनंतर पुरेशी तरलता राखली जाऊ शकते याची पडताळणी करा.
6. ज्ञात राखीव साठ्याव्यतिरिक्त आपल्याकडे किती अतिरिक्त टोकन्स आहेत ते पहा. ती रक्कम म्हणजे आपल्याला एक्सचेंज करण्यासाठी मिळालेल्या इनपुट टोकन्सची संख्या आहे.
7. आउटपुट टोकन्स गंतव्यस्थानावर पाठवा.
8. राखीव रकमा अपडेट करण्यासाठी `_update` ला कॉल करा

#### परत पेरिफेरी कॉन्ट्रॅक्टमध्ये (UniswapV2Router02.sol) {#back-in-the-periphery-contract-uniswapv2router02-sol}

9. कोणतीही आवश्यक स्वच्छता करा (उदाहरणार्थ, ट्रेडरला पाठवण्यासाठी ETH परत मिळवण्यासाठी WETH टोकन्स जाळणे)

### तरलता जोडणे {#add-liquidity-flow}

#### कॉलर {#caller-2}

1. पेरिफेरी खात्याला तरलता पूलमध्ये जोडल्या जाणाऱ्या रकमांची मंजुरी द्या.
2. पेरिफेरी कॉन्ट्रॅक्टच्या `addLiquidity` फंक्शन्सपैकी एकाला कॉल करा.

#### पेरिफेरी कॉन्ट्रॅक्टमध्ये (UniswapV2Router02.sol) {#in-the-periphery-contract-uniswapv2router02sol-2}

3. आवश्यक असल्यास नवीन जोडीचा एक्सचेंज तयार करा
4. जर आधीपासूनच जोडीचा एक्सचेंज अस्तित्वात असेल, तर जोडायच्या टोकन्सच्या रकमेची गणना करा. हे दोन्ही टोकन्ससाठी समान मूल्य असणे अपेक्षित आहे, त्यामुळे नवीन टोकन्सचे विद्यमान टोकन्सशी समान गुणोत्तर असावे.
5. रकमा स्वीकार्य आहेत का ते तपासा (कॉलर्स किमान रक्कम निर्दिष्ट करू शकतात ज्याच्या खाली ते तरलता जोडणे पसंत करणार नाहीत)
6. कोअर कॉन्ट्रॅक्टला कॉल करा.

#### कोअर कॉन्ट्रॅक्टमध्ये (UniswapV2Pair.sol) {#in-the-core-contract-uniswapv2pairsol-2-2}

7. तरलता टोकन्स मिंट करा आणि ते कॉलरला पाठवा
8. राखीव रकमा अपडेट करण्यासाठी `_update` ला कॉल करा

### तरलता काढून टाकणे {#remove-liquidity-flow}

#### कॉलर {#caller-3}

1. पेरिफेरी खात्याला अंतर्निहित टोकन्सच्या बदल्यात जाळल्या जाणाऱ्या तरलता टोकन्सची मंजुरी द्या.
2. पेरिफेरी कॉन्ट्रॅक्टच्या `removeLiquidity` फंक्शन्सपैकी एकाला कॉल करा.

#### पेरिफेरी कॉन्ट्रॅक्टमध्ये (UniswapV2Router02.sol) {#in-the-periphery-contract-uniswapv2router02sol-3}

3. तरलता टोकन्स जोडीच्या एक्सचेंजला पाठवा

#### कोअर कॉन्ट्रॅक्टमध्ये (UniswapV2Pair.sol) {#in-the-core-contract-uniswapv2pairsol-3}

4. गंतव्य पत्त्यावर जाळलेल्या टोकन्सच्या प्रमाणात अंतर्निहित टोकन्स पाठवा. उदाहरणार्थ, जर पूलमध्ये 1000 A टोकन्स, 500 B टोकन्स आणि 90 तरलता टोकन्स असतील आणि आपल्याला जाळण्यासाठी 9 टोकन्स मिळाले, तर आपण 10% तरलता टोकन्स जाळत आहोत, त्यामुळे आपण वापरकर्त्याला 100 A टोकन्स आणि 50 B टोकन्स परत पाठवतो.
5. तरलता टोकन्स जाळा
6. राखीव रकमा अपडेट करण्यासाठी `_update` ला कॉल करा

## मुख्य कॉन्ट्रॅक्ट्स {#core-contracts}

हे सुरक्षित कॉन्ट्रॅक्ट्स आहेत जे तरलता धारण करतात.

### UniswapV2Pair.sol {#uniswapv2pair}

[हे कॉन्ट्रॅक्ट](https://github.com/Uniswap/uniswap-v2-core/blob/master/contracts/UniswapV2Pair.sol) टोकन्सची अदलाबदल करणारा प्रत्यक्ष पूल लागू करते. ही मुख्य युनिस्वॅप कार्यक्षमता आहे.

```solidity
pragma solidity =0.5.16;

import './interfaces/IUniswapV2Pair.sol';
import './UniswapV2ERC20.sol';
import './libraries/Math.sol';
import './libraries/UQ112x112.sol';
import './interfaces/IERC20.sol';
import './interfaces/IUniswapV2Factory.sol';
import './interfaces/IUniswapV2Callee.sol';
```

हे सर्व इंटरफेसेस आहेत ज्यांची माहिती कॉन्ट्रॅक्टला असणे आवश्यक आहे, एकतर कारण कॉन्ट्रॅक्ट त्यांची अंमलबजावणी करते (`IUniswapV2Pair` आणि `UniswapV2ERC20`) किंवा कारण ते त्यांची अंमलबजावणी करणाऱ्या कॉन्ट्रॅक्ट्सना कॉल करते.

```solidity
contract UniswapV2Pair is IUniswapV2Pair, UniswapV2ERC20 {
```

हे कॉन्ट्रॅक्ट `UniswapV2ERC20` कडून इनहेरिट करते, जे तरलता टोकन्ससाठी ERC-20 फंक्शन्स प्रदान करते.

```solidity
    using SafeMath  for uint;
```

ओव्हरफ्लो आणि अंडरफ्लो टाळण्यासाठी [SafeMath लायब्ररी](https://docs.openzeppelin.com/contracts/2.x/api/math) वापरली जाते. हे महत्त्वाचे आहे कारण अन्यथा अशी परिस्थिती उद्भवू शकते जिथे मूल्य `-1` असायला हवे, परंतु त्याऐवजी ते `2^256-1` असते.

```solidity
    using UQ112x112 for uint224;
```

पूल कॉन्ट्रॅक्टमधील अनेक गणनेसाठी अपूर्णांकांची आवश्यकता असते. तथापि, EVM द्वारे अपूर्णांकांना समर्थन दिले जात नाही.
युनिस्वॅपने शोधलेला उपाय म्हणजे 224 बिट मूल्यांचा वापर करणे, ज्यामध्ये पूर्णांक भागासाठी 112 बिट्स आणि अपूर्णांकासाठी 112 बिट्स असतात. त्यामुळे `1.0` हे `2^112` म्हणून दर्शविले जाते, `1.5` हे `2^112 + 2^111` म्हणून दर्शविले जाते, इ.

या लायब्ररीबद्दल अधिक तपशील [दस्तऐवजात पुढे](#fixedpoint) उपलब्ध आहेत.

#### व्हेरिएबल्स {#pair-vars}

```solidity
    uint public constant MINIMUM_LIQUIDITY = 10**3;
```

शून्याने भाग जाण्याची प्रकरणे टाळण्यासाठी, नेहमी अस्तित्वात असलेल्या तरलता टोकन्सची किमान संख्या असते (परंतु ती शून्य खात्याच्या मालकीची असतात). ती संख्या **MINIMUM_LIQUIDITY** आहे, एक हजार.

```solidity
    bytes4 private constant SELECTOR = bytes4(keccak256(bytes('transfer(address,uint256)')));
```

हा ERC-20 हस्तांतरण फंक्शनसाठी ABI सिलेक्टर आहे. याचा वापर दोन टोकन खात्यांमध्ये ERC-20 टोकन्स हस्तांतरित करण्यासाठी केला जातो.

```solidity
    address public factory;
```

हे फॅक्टरी कॉन्ट्रॅक्ट आहे ज्याने हा पूल तयार केला आहे. प्रत्येक पूल हा दोन ERC-20 टोकन्समधील अदलाबदल आहे, फॅक्टरी हा एक मध्यवर्ती बिंदू आहे जो या सर्व पूल्सना जोडतो.

```solidity
    address public token0;
    address public token1;
```

या पूलद्वारे अदलाबदल करता येणाऱ्या दोन प्रकारच्या ERC-20 टोकन्ससाठी कॉन्ट्रॅक्ट्सचे पत्ते येथे आहेत.

```solidity
    uint112 private reserve0;           // सिंगल स्टोरेज स्लॉट वापरते, getReserves द्वारे प्रवेशयोग्य
    uint112 private reserve1;           // सिंगल स्टोरेज स्लॉट वापरते, getReserves द्वारे प्रवेशयोग्य
```

प्रत्येक टोकन प्रकारासाठी पूलकडे असलेले रिझर्व्ह. आम्ही असे गृहीत धरतो की दोन्ही समान मूल्याचे प्रतिनिधित्व करतात आणि म्हणून प्रत्येक token0 चे मूल्य reserve1/reserve0 token1 इतके आहे.

```solidity
    uint32  private blockTimestampLast; // सिंगल स्टोरेज स्लॉट वापरते, getReserves द्वारे प्रवेशयोग्य
```

ज्या शेवटच्या ब्लॉकमध्ये अदलाबदल झाली त्याचा टाइमस्टॅम्प, जो वेळेनुसार विनिमय दरांचा मागोवा घेण्यासाठी वापरला जातो.

इथेरियम कॉन्ट्रॅक्ट्सच्या सर्वात मोठ्या गॅस खर्चांपैकी एक म्हणजे स्टोरेज, जे कॉन्ट्रॅक्टच्या एका कॉलपासून दुसऱ्या कॉलपर्यंत टिकून राहते. प्रत्येक स्टोरेज सेल 256 बिट्स लांब असतो. त्यामुळे तीन व्हेरिएबल्स, `reserve0`, `reserve1`, आणि `blockTimestampLast`, अशा प्रकारे वाटप केले जातात की एकाच स्टोरेज मूल्यामध्ये त्या तिन्हींचा समावेश होऊ शकतो (112+112+32=256).

```solidity
    uint public price0CumulativeLast;
    uint public price1CumulativeLast;
```

हे व्हेरिएबल्स प्रत्येक टोकनसाठी (प्रत्येक एकमेकांच्या संदर्भात) एकत्रित खर्च धारण करतात. त्यांचा वापर ठराविक कालावधीत सरासरी विनिमय दर मोजण्यासाठी केला जाऊ शकतो.

```solidity
    uint public kLast; // reserve0 * reserve1, सर्वात अलीकडील तरलता घटनेनंतर लगेचच
```

token0 आणि token1 मधील विनिमय दर ठरवण्याची पेअर एक्सचेंजची पद्धत म्हणजे व्यापारादरम्यान दोन रिझर्व्हचा गुणाकार स्थिर ठेवणे. `kLast` हे ते मूल्य आहे. जेव्हा तरलता प्रदाता टोकन्स जमा करतो किंवा काढतो तेव्हा ते बदलते आणि 0.3% मार्केट फीमुळे ते थोडे वाढते.

येथे एक साधे उदाहरण आहे. लक्षात घ्या की साधेपणासाठी टेबलमध्ये दशांश चिन्हानंतर फक्त तीन अंक आहेत आणि आम्ही 0.3% ट्रेडिंग फीकडे दुर्लक्ष करतो त्यामुळे आकडे अचूक नाहीत.

| घटना | reserve0 | reserve1 | reserve0 \* reserve1 | सरासरी विनिमय दर (token1 / token0) |
| ------------------------------------------- | --------: | --------: | -------------------: | --------------------------------------- |
| प्रारंभिक सेटअप | 1,000.000 | 1,000.000 | 1,000,000 | |
| ट्रेडर A 47.619 token1 साठी 50 token0 ची अदलाबदल करतो | 1,050.000 | 952.381 | 1,000,000 | 0.952 |
| ट्रेडर B 8.984 token1 साठी 10 token0 ची अदलाबदल करतो | 1,060.000 | 943.396 | 1,000,000 | 0.898 |
| ट्रेडर C 34.305 token1 साठी 40 token0 ची अदलाबदल करतो | 1,100.000 | 909.090 | 1,000,000 | 0.858 |
| ट्रेडर D 109.01 token0 साठी 100 token1 ची अदलाबदल करतो | 990.990 | 1,009.090 | 1,000,000 | 0.917 |
| ट्रेडर E 10.079 token1 साठी 10 token0 ची अदलाबदल करतो | 1,000.990 | 999.010 | 1,000,000 | 1.008 |

जसे ट्रेडर्स अधिक token0 प्रदान करतात, तसे पुरवठा आणि मागणीच्या आधारावर token1 चे सापेक्ष मूल्य वाढते आणि याउलट.

#### लॉक {#pair-lock}

```solidity
    uint private unlocked = 1;
```

सुरक्षा भेद्यतेचा एक वर्ग आहे जो [पुनर्प्रवेश गैरवापरावर](https://medium.com/coinmonks/ethernaut-lvl-10-re-entrancy-walkthrough-how-to-abuse-execution-ordering-and-reproduce-the-dao-7ec88b912c14) आधारित आहे. युनिस्वॅपला अनियंत्रित ERC-20 टोकन्स हस्तांतरित करण्याची आवश्यकता असते, ज्याचा अर्थ अशा ERC-20 कॉन्ट्रॅक्ट्सना कॉल करणे जे त्यांना कॉल करणाऱ्या युनिस्वॅप मार्केटचा गैरवापर करण्याचा प्रयत्न करू शकतात.
कॉन्ट्रॅक्टचा भाग म्हणून `unlocked` व्हेरिएबल ठेवून, आपण फंक्शन्स चालू असताना (एकाच व्यवहारामध्ये) त्यांना कॉल करण्यापासून रोखू शकतो.

```solidity
    modifier lock() {
```

हे फंक्शन एक [मॉडिफायर](https://docs.soliditylang.org/en/v0.8.3/contracts.html#function-modifiers) आहे, एक फंक्शन जे सामान्य फंक्शनच्या भोवती गुंडाळले जाते जेणेकरून त्याचे वर्तन काही प्रकारे बदलता येईल.

```solidity
        require(unlocked == 1, 'UniswapV2: LOCKED');
        unlocked = 0;
```

जर `unlocked` एक च्या बरोबरीचे असेल, तर ते शून्य वर सेट करा. जर ते आधीच शून्य असेल तर कॉल पूर्ववत करा, तो अयशस्वी करा.

```solidity
        _;
```

मॉडिफायरमध्ये `_;` हा मूळ फंक्शन कॉल असतो (सर्व पॅरामीटर्ससह). येथे याचा अर्थ असा आहे की फंक्शन कॉल तेव्हाच होतो जेव्हा कॉल केला गेला तेव्हा `unlocked` एक होता आणि ते चालू असताना `unlocked` चे मूल्य शून्य असते.

```solidity
        unlocked = 1;
    }
```

मुख्य फंक्शन परत आल्यानंतर, लॉक सोडा.

#### संकीर्ण फंक्शन्स {#pair-misc}

```solidity
    function getReserves() public view returns (uint112 _reserve0, uint112 _reserve1, uint32 _blockTimestampLast) {
        _reserve0 = reserve0;
        _reserve1 = reserve1;
        _blockTimestampLast = blockTimestampLast;
    }
```

हे फंक्शन कॉलर्सना एक्सचेंजची वर्तमान स्थिती प्रदान करते. लक्षात घ्या की Solidity फंक्शन्स [अनेक मूल्ये परत करू शकतात](https://docs.soliditylang.org/en/v0.8.3/contracts.html#returning-multiple-values).

```solidity
    function _safeTransfer(address token, address to, uint value) private {
        (bool success, bytes memory data) = token.call(abi.encodeWithSelector(SELECTOR, to, value));
```

हे अंतर्गत फंक्शन एक्सचेंजमधून दुसऱ्या कोणालातरी ERC20 टोकन्सची रक्कम हस्तांतरित करते. `SELECTOR` हे निर्दिष्ट करते की आपण कॉल करत असलेले फंक्शन `transfer(address,uint)` आहे (वरील व्याख्या पहा).

टोकन फंक्शनसाठी इंटरफेस आयात करणे टाळण्यासाठी, आम्ही [ABI फंक्शन्सपैकी](https://docs.soliditylang.org/en/v0.8.3/units-and-global-variables.html#abi-encoding-and-decoding-functions) एकाचा वापर करून "मॅन्युअली" कॉल तयार करतो.

```solidity
        require(success && (data.length == 0 || abi.decode(data, (bool))), 'UniswapV2: TRANSFER_FAILED');
    }
```

ERC-20 हस्तांतरण कॉल अपयश नोंदवू शकण्याचे दोन मार्ग आहेत:

1. पूर्ववत करणे. जर बाह्य कॉन्ट्रॅक्टचा कॉल पूर्ववत झाला, तर बुलियन रिटर्न व्हॅल्यू `false` असते
2. सामान्यपणे समाप्त होणे परंतु अपयश नोंदवणे. त्या बाबतीत रिटर्न व्हॅल्यू बफरची लांबी शून्येतर असते आणि जेव्हा बुलियन व्हॅल्यू म्हणून डीकोड केले जाते तेव्हा ते `false` असते

यापैकी कोणतीही परिस्थिती उद्भवल्यास, पूर्ववत करा.

#### घटना {#pair-events}

```solidity
    event Mint(address indexed sender, uint amount0, uint amount1);
    event Burn(address indexed sender, uint amount0, uint amount1, address indexed to);
```

जेव्हा तरलता प्रदाता एकतर तरलता जमा करतो (`Mint`) किंवा ती काढतो (`Burn`) तेव्हा या दोन घटना उत्सर्जित केल्या जातात. दोन्ही प्रकरणांमध्ये, जमा केलेल्या किंवा काढलेल्या token0 आणि token1 ची रक्कम घटनेचा भाग असते, तसेच आम्हाला कॉल करणाऱ्या खात्याची ओळख (`sender`) देखील असते. रक्कम काढण्याच्या बाबतीत, घटनेमध्ये टोकन्स प्राप्त करणारे लक्ष्य (`to`) देखील समाविष्ट असते, जे प्रेषकासारखे नसू शकते.

```solidity
    event Swap(
        address indexed sender,
        uint amount0In,
        uint amount1In,
        uint amount0Out,
        uint amount1Out,
        address indexed to
    );
```

जेव्हा एखादा ट्रेडर एका टोकनची दुसऱ्या टोकनसाठी अदलाबदल करतो तेव्हा ही घटना उत्सर्जित केली जाते. पुन्हा, प्रेषक आणि गंतव्यस्थान समान नसू शकतात.
प्रत्येक टोकन एकतर एक्सचेंजला पाठवले जाऊ शकते किंवा त्यातून प्राप्त केले जाऊ शकते.

```solidity
    event Sync(uint112 reserve0, uint112 reserve1);
```

शेवटी, नवीनतम रिझर्व्ह माहिती (आणि म्हणून विनिमय दर) प्रदान करण्यासाठी, कारण काहीही असले तरी, प्रत्येक वेळी टोकन्स जोडले किंवा काढले जातात तेव्हा `Sync` उत्सर्जित केले जाते.

#### सेटअप फंक्शन्स {#pair-setup}

जेव्हा नवीन पेअर एक्सचेंज सेट केले जाते तेव्हा या फंक्शन्सना एकदा कॉल करणे अपेक्षित असते.

```solidity
    constructor() public {
        factory = msg.sender;
    }
```

कन्स्ट्रक्टर हे सुनिश्चित करतो की आम्ही पेअर तयार करणाऱ्या फॅक्टरीच्या पत्त्याचा मागोवा ठेवू. ही माहिती `initialize` आणि फॅक्टरी फीसाठी (जर अस्तित्वात असेल तर) आवश्यक आहे

```solidity
    // डिप्लॉयमेंटच्या वेळी फॅक्टरीद्वारे एकदाच कॉल केले जाते
    function initialize(address _token0, address _token1) external {
        require(msg.sender == factory, 'UniswapV2: FORBIDDEN'); // पुरेशी तपासणी
        token0 = _token0;
        token1 = _token1;
    }
```

हे फंक्शन फॅक्टरीला (आणि फक्त फॅक्टरीला) दोन ERC-20 टोकन्स निर्दिष्ट करण्याची परवानगी देते ज्यांची ही पेअर अदलाबदल करेल.

#### अंतर्गत अपडेट फंक्शन्स {#pair-update-internal}

##### \_update

```solidity
    // रिझर्व्ह अपडेट करा आणि, प्रति ब्लॉक पहिल्या कॉलवर, प्राईस अ‍ॅक्युम्युलेटर्स
    function _update(uint balance0, uint balance1, uint112 _reserve0, uint112 _reserve1) private {
```

प्रत्येक वेळी टोकन्स जमा केल्यावर किंवा काढल्यावर या फंक्शनला कॉल केला जातो.

```solidity
        require(balance0 <= uint112(-1) && balance1 <= uint112(-1), 'UniswapV2: OVERFLOW');
```

जर balance0 किंवा balance1 (uint256) पैकी कोणतेही uint112(-1) (=2^112-1) पेक्षा जास्त असेल (त्यामुळे ते ओव्हरफ्लो होते आणि uint112 मध्ये रूपांतरित केल्यावर 0 वर परत येते) तर ओव्हरफ्लो टाळण्यासाठी \_update सुरू ठेवण्यास नकार द्या. 10^18 युनिट्समध्ये विभागल्या जाऊ शकणाऱ्या सामान्य टोकनसह, याचा अर्थ प्रत्येक एक्सचेंज प्रत्येक टोकनच्या सुमारे 5.1\*10^15 पर्यंत मर्यादित आहे. आतापर्यंत ही समस्या उद्भवलेली नाही.

```solidity
        uint32 blockTimestamp = uint32(block.timestamp % 2**32);
        uint32 timeElapsed = blockTimestamp - blockTimestampLast; // ओव्हरफ्लो अपेक्षित आहे
        if (timeElapsed > 0 && _reserve0 != 0 && _reserve1 != 0) {
```

जर गेलेला वेळ शून्य नसेल, तर याचा अर्थ असा की आम्ही या ब्लॉकवरील पहिला एक्सचेंज व्यवहार आहोत. त्या बाबतीत, आम्हाला कॉस्ट ॲक्युम्युलेटर्स अपडेट करणे आवश्यक आहे.

```solidity
            // * कधीही ओव्हरफ्लो होत नाही, आणि + ओव्हरफ्लो अपेक्षित आहे
            price0CumulativeLast += uint(UQ112x112.encode(_reserve1).uqdiv(_reserve0)) * timeElapsed;
            price1CumulativeLast += uint(UQ112x112.encode(_reserve0).uqdiv(_reserve1)) * timeElapsed;
        }
```

प्रत्येक कॉस्ट ॲक्युम्युलेटर नवीनतम कॉस्ट (दुसऱ्या टोकनचा रिझर्व्ह/या टोकनचा रिझर्व्ह) गुणिले सेकंदांमध्ये गेलेला वेळ यासह अपडेट केला जातो. सरासरी किंमत मिळवण्यासाठी, तुम्ही वेळेच्या दोन बिंदूंमधील एकत्रित किंमत वाचता आणि त्यांच्यातील वेळेच्या फरकाने भागता. उदाहरणार्थ, घटनांचा हा क्रम गृहीत धरा:

| घटना | reserve0 | reserve1 | टाइमस्टॅम्प | किरकोळ विनिमय दर (reserve1 / reserve0) | price0CumulativeLast |
| -------------------------------------------------------- | --------: | --------: | --------- | -------------------------------------------: | -------------------------: |
| प्रारंभिक सेटअप | 1,000.000 | 1,000.000 | 5,000 | 1.000 | 0 |
| ट्रेडर A 50 token0 जमा करतो आणि 47.619 token1 परत मिळवतो | 1,050.000 | 952.381 | 5,020 | 0.907 | 20 |
| ट्रेडर B 10 token0 जमा करतो आणि 8.984 token1 परत मिळवतो | 1,060.000 | 943.396 | 5,030 | 0.890 | 20+10\*0.907 = 29.07 |
| ट्रेडर C 40 token0 जमा करतो आणि 34.305 token1 परत मिळवतो | 1,100.000 | 909.090 | 5,100 | 0.826 | 29.07+70\*0.890 = 91.37 |
| ट्रेडर D 100 token1 जमा करतो आणि 109.01 token0 परत मिळवतो | 990.990 | 1,009.090 | 5,110 | 1.018 | 91.37+10\*0.826 = 99.63 |
| ट्रेडर E 10 token0 जमा करतो आणि 10.079 token1 परत मिळवतो | 1,000.990 | 999.010 | 5,150 | 0.998 | 99.63+40\*1.1018 = 143.702 |

समजा आपल्याला 5,030 आणि 5,150 या टाइमस्टॅम्प्स दरम्यान **Token0** ची सरासरी किंमत मोजायची आहे. `price0Cumulative` च्या मूल्यातील फरक 143.702-29.07=114.632 आहे. ही दोन मिनिटांची (120 सेकंद) सरासरी आहे. त्यामुळे सरासरी किंमत 114.632/120 = 0.955 आहे.

या किंमतीच्या गणनेमुळेच आपल्याला जुन्या रिझर्व्ह आकारांची माहिती असणे आवश्यक आहे.

```solidity
        reserve0 = uint112(balance0);
        reserve1 = uint112(balance1);
        blockTimestampLast = blockTimestamp;
        emit Sync(reserve0, reserve1);
    }
```

शेवटी, ग्लोबल व्हेरिएबल्स अपडेट करा आणि `Sync` घटना उत्सर्जित करा.

##### \_mintFee

```solidity
    // जर फी चालू असेल, तर sqrt(k) मधील वाढीच्या 1/6 च्या समतुल्य तरलता मिंट करा
    function _mintFee(uint112 _reserve0, uint112 _reserve1) private returns (bool feeOn) {
```

युनिस्वॅप 2.0 मध्ये ट्रेडर्स मार्केट वापरण्यासाठी 0.30% फी भरतात. त्या फीचा बहुतांश भाग (ट्रेडचा 0.25%) नेहमी तरलता प्रदात्यांना जातो. उर्वरित 0.05% एकतर तरलता प्रदात्यांना किंवा फॅक्टरीद्वारे प्रोटोकॉल फी म्हणून निर्दिष्ट केलेल्या पत्त्यावर जाऊ शकतो, जो युनिस्वॅपला त्यांच्या विकास प्रयत्नांसाठी पैसे देतो.

गणने कमी करण्यासाठी (आणि त्यामुळे गॅस खर्च), ही फी प्रत्येक व्यवहाराऐवजी केवळ तेव्हाच मोजली जाते जेव्हा पूलमधून तरलता जोडली जाते किंवा काढली जाते.

```solidity
        address feeTo = IUniswapV2Factory(factory).feeTo();
        feeOn = feeTo != address(0);
```

फॅक्टरीचे फी गंतव्यस्थान वाचा. जर ते शून्य असेल तर कोणतीही प्रोटोकॉल फी नाही आणि ती फी मोजण्याची गरज नाही.

```solidity
        uint _kLast = kLast; // गॅस बचत
```

`kLast` स्थिती व्हेरिएबल स्टोरेजमध्ये स्थित आहे, त्यामुळे कॉन्ट्रॅक्टच्या वेगवेगळ्या कॉल्स दरम्यान त्याचे मूल्य असेल.
स्टोरेजमध्ये प्रवेश करणे हे कॉन्ट्रॅक्टचा फंक्शन कॉल संपल्यावर सोडल्या जाणाऱ्या अस्थिर मेमरीमध्ये प्रवेश करण्यापेक्षा खूप महाग आहे, त्यामुळे गॅस वाचवण्यासाठी आम्ही अंतर्गत व्हेरिएबल वापरतो.

```solidity
        if (feeOn) {
            if (_kLast != 0) {
```

तरलता प्रदात्यांना त्यांचा वाटा केवळ त्यांच्या तरलता टोकन्सच्या वाढीमुळे मिळतो. परंतु प्रोटोकॉल फीसाठी नवीन तरलता टोकन्स मिंट करणे आणि `feeTo` पत्त्यावर प्रदान करणे आवश्यक आहे.

```solidity
                uint rootK = Math.sqrt(uint(_reserve0).mul(_reserve1));
                uint rootKLast = Math.sqrt(_kLast);
                if (rootK > rootKLast) {
```

जर नवीन तरलता असेल ज्यावर प्रोटोकॉल फी गोळा करायची असेल. तुम्ही वर्गमूळ फंक्शन [या लेखात पुढे](#math) पाहू शकता

```solidity
                    uint numerator = totalSupply.mul(rootK.sub(rootKLast));
                    uint denominator = rootK.mul(5).add(rootKLast);
                    uint liquidity = numerator / denominator;
```

फीची ही गुंतागुंतीची गणना [श्वेतपत्रिकेच्या](https://app.uniswap.org/whitepaper.pdf) पृष्ठ 5 वर स्पष्ट केली आहे. आम्हाला माहित आहे की `kLast` मोजल्या गेलेल्या वेळेपासून ते आतापर्यंत कोणतीही तरलता जोडली किंवा काढली गेली नाही (कारण आम्ही प्रत्येक वेळी तरलता जोडली किंवा काढली जाते तेव्हा, ती प्रत्यक्षात बदलण्यापूर्वी ही गणना चालवतो), त्यामुळे `reserve0 * reserve1` मधील कोणताही बदल व्यवहार फीमधून आला पाहिजे (त्यांच्याशिवाय आम्ही `reserve0 * reserve1` स्थिर ठेवू).

```solidity
                    if (liquidity > 0) _mint(feeTo, liquidity);
                }
            }
```

अतिरिक्त तरलता टोकन्स प्रत्यक्षात तयार करण्यासाठी आणि त्यांना `feeTo` ला नियुक्त करण्यासाठी `UniswapV2ERC20._mint` फंक्शन वापरा.

```solidity
        } else if (_kLast != 0) {
            kLast = 0;
        }
    }
```

जर कोणतीही फी नसेल तर `kLast` शून्य वर सेट करा (जर ते आधीच नसेल). जेव्हा हे कॉन्ट्रॅक्ट लिहिले गेले तेव्हा एक [गॅस परतावा वैशिष्ट्य](https://eips.ethereum.org/EIPS/eip-3298) होते ज्याने कॉन्ट्रॅक्ट्सना त्यांना आवश्यक नसलेले स्टोरेज शून्य करून इथेरियम स्थितीचा एकूण आकार कमी करण्यास प्रोत्साहित केले.
शक्य असेल तेव्हा या कोडला तो परतावा मिळतो.

#### बाह्यरित्या प्रवेशयोग्य फंक्शन्स {#pair-external}

लक्षात घ्या की कोणताही व्यवहार किंवा कॉन्ट्रॅक्ट या फंक्शन्सना कॉल _करू शकतो_, तरीही ते पेरिफेरी कॉन्ट्रॅक्टमधून कॉल करण्यासाठी डिझाइन केलेले आहेत. जर तुम्ही त्यांना थेट कॉल केले तर तुम्ही पेअर एक्सचेंजची फसवणूक करू शकणार नाही, परंतु चुकीमुळे तुमचे मूल्य गमावू शकता.

##### मिंट

```solidity
    // हे लो-लेव्हल फंक्शन अशा कॉन्ट्रॅक्टमधून कॉल केले जावे जे महत्त्वाच्या सुरक्षितता तपासण्या करते
    function mint(address to) external lock returns (uint liquidity) {
```

जेव्हा तरलता प्रदाता पूलमध्ये तरलता जोडतो तेव्हा या फंक्शनला कॉल केला जातो. हे बक्षीस म्हणून अतिरिक्त तरलता टोकन्स मिंट करते. याला [एका पेरिफेरी कॉन्ट्रॅक्टमधून](#uniswapv2router02) कॉल केले जावे जे एकाच व्यवहारामध्ये तरलता जोडल्यानंतर त्याला कॉल करते (जेणेकरून कायदेशीर मालकापूर्वी नवीन तरलतेवर दावा करणारा व्यवहार इतर कोणीही सबमिट करू शकणार नाही).

```solidity
        (uint112 _reserve0, uint112 _reserve1,) = getReserves(); // गॅस बचत
```

अनेक मूल्ये परत करणाऱ्या Solidity फंक्शनचे परिणाम वाचण्याचा हा मार्ग आहे. आम्ही शेवटची परत केलेली मूल्ये, ब्लॉक टाइमस्टॅम्प टाकून देतो, कारण आम्हाला त्याची आवश्यकता नाही.

```solidity
        uint balance0 = IERC20(token0).balanceOf(address(this));
        uint balance1 = IERC20(token1).balanceOf(address(this));
        uint amount0 = balance0.sub(_reserve0);
        uint amount1 = balance1.sub(_reserve1);
```

सध्याचे बॅलन्स मिळवा आणि प्रत्येक टोकन प्रकाराचे किती जोडले गेले ते पहा.

```solidity
        bool feeOn = _mintFee(_reserve0, _reserve1);
```

गोळा करण्यासाठी प्रोटोकॉल फीची गणना करा, जर काही असेल तर, आणि त्यानुसार तरलता टोकन्स मिंट करा. कारण `_mintFee` चे पॅरामीटर्स जुनी रिझर्व्ह मूल्ये आहेत, फीची गणना केवळ फीमुळे होणाऱ्या पूल बदलांवर आधारित अचूकपणे केली जाते.

```solidity
        uint _totalSupply = totalSupply; // गॅस बचत, येथे परिभाषित करणे आवश्यक आहे कारण totalSupply _mintFee मध्ये अपडेट होऊ शकते
        if (_totalSupply == 0) {
            liquidity = Math.sqrt(amount0.mul(amount1)).sub(MINIMUM_LIQUIDITY);
           _mint(address(0), MINIMUM_LIQUIDITY); // पहिले MINIMUM_LIQUIDITY टोकन्स कायमचे लॉक करा
```

जर ही पहिली ठेव असेल, तर `MINIMUM_LIQUIDITY` टोकन्स तयार करा आणि त्यांना लॉक करण्यासाठी शून्य पत्त्यावर पाठवा. ते कधीही रिडीम केले जाऊ शकत नाहीत, ज्याचा अर्थ पूल कधीही पूर्णपणे रिकामा होणार नाही (हे आम्हाला काही ठिकाणी शून्याने भाग जाण्यापासून वाचवते). `MINIMUM_LIQUIDITY` चे मूल्य एक हजार आहे, जे बहुतेक ERC-20 टोकनच्या 10^-18 व्या युनिट्समध्ये विभागलेले आहेत हे लक्षात घेता, जसे ETH Wei मध्ये विभागले गेले आहे, एकाच टोकनच्या मूल्याच्या 10^-15 आहे. जास्त किंमत नाही.

पहिल्या ठेवीच्या वेळी आम्हाला दोन टोकन्सचे सापेक्ष मूल्य माहित नसते, त्यामुळे आम्ही फक्त रकमांचा गुणाकार करतो आणि वर्गमूळ काढतो, असे गृहीत धरून की ठेव आम्हाला दोन्ही टोकन्समध्ये समान मूल्य प्रदान करते.

आम्ही यावर विश्वास ठेवू शकतो कारण आर्बिट्रेजमुळे मूल्य गमावणे टाळण्यासाठी समान मूल्य प्रदान करणे हे ठेवीदाराच्या हिताचे आहे.
समजा दोन टोकन्सचे मूल्य समान आहे, परंतु आमच्या ठेवीदाराने **Token0** च्या तुलनेत **Token1** ची चार पट जास्त ठेव ठेवली आहे. पेअर एक्सचेंजला असे वाटते की **Token0** अधिक मौल्यवान आहे या वस्तुस्थितीचा वापर करून एखादा ट्रेडर त्यातून मूल्य काढू शकतो.

| घटना | reserve0 | reserve1 | reserve0 \* reserve1 | पूलचे मूल्य (reserve0 + reserve1) |
| ------------------------------------------------------------ | -------: | -------: | -------------------: | --------------------------------------: |
| प्रारंभिक सेटअप | 8 | 32 | 256 | 40 |
| ट्रेडर 8 **Token0** टोकन्स जमा करतो, 16 **Token1** परत मिळवतो | 16 | 16 | 256 | 32 |

जसे तुम्ही पाहू शकता, ट्रेडरने अतिरिक्त 8 टोकन्स मिळवले, जे पूलच्या मूल्यातील कपातीतून येतात, ज्यामुळे त्याच्या मालक असलेल्या ठेवीदाराचे नुकसान होते.

```solidity
        } else {
            liquidity = Math.min(amount0.mul(_totalSupply) / _reserve0, amount1.mul(_totalSupply) / _reserve1);
```

प्रत्येक त्यानंतरच्या ठेवीसह आम्हाला दोन मालमत्तांमधील विनिमय दर आधीच माहित असतो आणि आम्ही तरलता प्रदात्यांनी दोन्हीमध्ये समान मूल्य प्रदान करण्याची अपेक्षा करतो. जर त्यांनी तसे केले नाही, तर आम्ही त्यांना शिक्षा म्हणून त्यांनी प्रदान केलेल्या कमी मूल्यावर आधारित तरलता टोकन्स देतो.

ती प्रारंभिक ठेव असो किंवा त्यानंतरची, आम्ही प्रदान करत असलेल्या तरलता टोकन्सची संख्या `reserve0*reserve1` मधील बदलाच्या वर्गमुळाच्या बरोबरीची असते आणि तरलता टोकनचे मूल्य बदलत नाही (जोपर्यंत आम्हाला अशी ठेव मिळत नाही ज्यामध्ये दोन्ही प्रकारांची समान मूल्ये नाहीत, अशा परिस्थितीत "दंड" वितरित केला जातो). येथे समान मूल्य असलेल्या दोन टोकन्सचे आणखी एक उदाहरण आहे, ज्यामध्ये तीन चांगल्या ठेवी आणि एक वाईट ठेव आहे (केवळ एका टोकन प्रकाराची ठेव, त्यामुळे ती कोणतीही तरलता टोकन्स तयार करत नाही).

| घटना | reserve0 | reserve1 | reserve0 \* reserve1 | पूल मूल्य (reserve0 + reserve1) | या ठेवीसाठी मिंट केलेले तरलता टोकन्स | एकूण तरलता टोकन्स | प्रत्येक तरलता टोकनचे मूल्य |
| ------------------------- | -------: | -------: | -------------------: | -------------------------------: | ---------------------------------------: | ---------------------: | ----------------------------: |
| प्रारंभिक सेटअप | 8.000 | 8.000 | 64 | 16.000 | 8 | 8 | 2.000 |
| प्रत्येक प्रकाराचे चार जमा करा | 12.000 | 12.000 | 144 | 24.000 | 4 | 12 | 2.000 |
| प्रत्येक प्रकाराचे दोन जमा करा | 14.000 | 14.000 | 196 | 28.000 | 2 | 14 | 2.000 |
| असमान मूल्याची ठेव | 18.000 | 14.000 | 252 | 32.000 | 0 | 14 | ~2.286 |
| आर्बिट्रेजनंतर | ~15.874 | ~15.874 | 252 | ~31.748 | 0 | 14 | ~2.267 |

```solidity
        }
        require(liquidity > 0, 'UniswapV2: INSUFFICIENT_LIQUIDITY_MINTED');
        _mint(to, liquidity);
```

अतिरिक्त तरलता टोकन्स प्रत्यक्षात तयार करण्यासाठी आणि त्यांना योग्य खात्यात देण्यासाठी `UniswapV2ERC20._mint` फंक्शन वापरा.

```solidity

        _update(balance0, balance1, _reserve0, _reserve1);
        if (feeOn) kLast = uint(reserve0).mul(reserve1); // reserve0 आणि reserve1 अद्ययावत आहेत
        emit Mint(msg.sender, amount0, amount1);
    }
```

स्थिती व्हेरिएबल्स अपडेट करा (`reserve0`, `reserve1`, आणि आवश्यक असल्यास `kLast`) आणि योग्य घटना उत्सर्जित करा.

##### जाळणे

```solidity
    // हे लो-लेव्हल फंक्शन अशा कॉन्ट्रॅक्टमधून कॉल केले जावे जे महत्त्वाच्या सुरक्षितता तपासण्या करते
    function burn(address to) external lock returns (uint amount0, uint amount1) {
```

जेव्हा तरलता काढली जाते आणि योग्य तरलता टोकन्स जाळणे आवश्यक असते तेव्हा या फंक्शनला कॉल केला जातो.
याला [पेरिफेरी खात्यातून](#uniswapv2router02) देखील कॉल केले जावे.

```solidity
        (uint112 _reserve0, uint112 _reserve1,) = getReserves(); // गॅस बचत
        address _token0 = token0;                                // गॅस बचत
        address _token1 = token1;                                // गॅस बचत
        uint balance0 = IERC20(_token0).balanceOf(address(this));
        uint balance1 = IERC20(_token1).balanceOf(address(this));
        uint liquidity = balanceOf[address(this)];
```

पेरिफेरी कॉन्ट्रॅक्टने कॉल करण्यापूर्वी जाळायची तरलता या कॉन्ट्रॅक्टमध्ये हस्तांतरित केली. अशा प्रकारे आम्हाला माहित असते की किती तरलता जाळायची आहे आणि आम्ही ती जाळली जाईल याची खात्री करू शकतो.

```solidity
        bool feeOn = _mintFee(_reserve0, _reserve1);
        uint _totalSupply = totalSupply; // गॅस बचत, येथे परिभाषित करणे आवश्यक आहे कारण totalSupply _mintFee मध्ये अपडेट होऊ शकते
        amount0 = liquidity.mul(balance0) / _totalSupply; // बॅलन्स वापरल्याने प्रमाणबद्ध (pro-rata) वितरण सुनिश्चित होते
        amount1 = liquidity.mul(balance1) / _totalSupply; // बॅलन्स वापरल्याने प्रमाणबद्ध (pro-rata) वितरण सुनिश्चित होते
        require(amount0 > 0 && amount1 > 0, 'UniswapV2: INSUFFICIENT_LIQUIDITY_BURNED');
```

तरलता प्रदात्याला दोन्ही टोकन्सचे समान मूल्य मिळते. अशा प्रकारे आम्ही विनिमय दर बदलत नाही.

```solidity
        _burn(address(this), liquidity);
        _safeTransfer(_token0, to, amount0);
        _safeTransfer(_token1, to, amount1);
        balance0 = IERC20(_token0).balanceOf(address(this));
        balance1 = IERC20(_token1).balanceOf(address(this));

        _update(balance0, balance1, _reserve0, _reserve1);
        if (feeOn) kLast = uint(reserve0).mul(reserve1); // reserve0 आणि reserve1 अद्ययावत आहेत
        emit Burn(msg.sender, amount0, amount1, to);
    }

```

उर्वरित `burn` फंक्शन हे वरील `mint` फंक्शनची आरशातील प्रतिमा आहे.

##### अदलाबदल

```solidity
    // हे लो-लेव्हल फंक्शन अशा कॉन्ट्रॅक्टमधून कॉल केले जावे जे महत्त्वाच्या सुरक्षितता तपासण्या करते
    function swap(uint amount0Out, uint amount1Out, address to, bytes calldata data) external lock {
```

या फंक्शनला देखील [एका पेरिफेरी कॉन्ट्रॅक्टमधून](#uniswapv2router02) कॉल करणे अपेक्षित आहे.

```solidity
        require(amount0Out > 0 || amount1Out > 0, 'UniswapV2: INSUFFICIENT_OUTPUT_AMOUNT');
        (uint112 _reserve0, uint112 _reserve1,) = getReserves(); // गॅस बचत
        require(amount0Out < _reserve0 && amount1Out < _reserve1, 'UniswapV2: INSUFFICIENT_LIQUIDITY');

        uint balance0;
        uint balance1;
        { // _token{0,1} साठी स्कोप, स्टॅक टू डीप (stack too deep) त्रुटी टाळतो
```

स्थानिक व्हेरिएबल्स एकतर मेमरीमध्ये किंवा, जर ते खूप जास्त नसतील तर, थेट स्टॅकवर साठवले जाऊ शकतात.
जर आपण संख्या मर्यादित करू शकलो जेणेकरून आपण स्टॅक वापरू, तर आपण कमी गॅस वापरू. अधिक तपशीलांसाठी [येलो पेपर, औपचारिक इथेरियम तपशील](https://ethereum.github.io/yellowpaper/paper.pdf), पृष्ठ 26, समीकरण 298 पहा.

```solidity
            address _token0 = token0;
            address _token1 = token1;
            require(to != _token0 && to != _token1, 'UniswapV2: INVALID_TO');
            if (amount0Out > 0) _safeTransfer(_token0, to, amount0Out); // आशावादीपणे टोकन्सचे हस्तांतरण करा
            if (amount1Out > 0) _safeTransfer(_token1, to, amount1Out); // आशावादीपणे टोकन्सचे हस्तांतरण करा
```

हे हस्तांतरण आशावादी आहे, कारण सर्व अटी पूर्ण झाल्याची खात्री होण्यापूर्वी आम्ही हस्तांतरण करतो. इथेरियममध्ये हे ठीक आहे कारण जर कॉलमध्ये नंतर अटी पूर्ण झाल्या नाहीत तर आम्ही त्यातून आणि त्याने तयार केलेल्या कोणत्याही बदलांमधून पूर्ववत करतो.

```solidity
            if (data.length > 0) IUniswapV2Callee(to).uniswapV2Call(msg.sender, amount0Out, amount1Out, data);
```

विनंती केल्यास प्राप्तकर्त्याला अदलाबदलीबद्दल माहिती द्या.

```solidity
            balance0 = IERC20(_token0).balanceOf(address(this));
            balance1 = IERC20(_token1).balanceOf(address(this));
        }
```

सध्याचे बॅलन्स मिळवा. अदलाबदलीसाठी आम्हाला कॉल करण्यापूर्वी पेरिफेरी कॉन्ट्रॅक्ट आम्हाला टोकन्स पाठवते. यामुळे कॉन्ट्रॅक्टला त्याची फसवणूक होत नाही हे तपासणे सोपे होते, एक तपासणी जी मुख्य कॉन्ट्रॅक्टमध्ये _होणे आवश्यक_ आहे (कारण आम्हाला आमच्या पेरिफेरी कॉन्ट्रॅक्ट व्यतिरिक्त इतर संस्थांद्वारे कॉल केले जाऊ शकते).

```solidity
        uint amount0In = balance0 > _reserve0 - amount0Out ? balance0 - (_reserve0 - amount0Out) : 0;
        uint amount1In = balance1 > _reserve1 - amount1Out ? balance1 - (_reserve1 - amount1Out) : 0;
        require(amount0In > 0 || amount1In > 0, 'UniswapV2: INSUFFICIENT_INPUT_AMOUNT');
        { // reserve{0,1}Adjusted साठी स्कोप, स्टॅक टू डीप (stack too deep) त्रुटी टाळतो
            uint balance0Adjusted = balance0.mul(1000).sub(amount0In.mul(3));
            uint balance1Adjusted = balance1.mul(1000).sub(amount1In.mul(3));
            require(balance0Adjusted.mul(balance1Adjusted) >= uint(_reserve0).mul(_reserve1).mul(1000**2), 'UniswapV2: K');
```

अदलाबदलीतून आमचे नुकसान होणार नाही याची खात्री करण्यासाठी ही एक सॅनिटी चेक आहे. अशी कोणतीही परिस्थिती नाही ज्यामध्ये अदलाबदलीने `reserve0*reserve1` कमी केले पाहिजे. येथेच आम्ही हे देखील सुनिश्चित करतो की अदलाबदलीवर 0.3% फी पाठविली जात आहे; K च्या मूल्याची सॅनिटी तपासणी करण्यापूर्वी, आम्ही दोन्ही बॅलन्सना 1000 ने गुणतो आणि त्यातून 3 ने गुणलेल्या रकमा वजा करतो, याचा अर्थ K मूल्याची सध्याच्या रिझर्व्हच्या K मूल्याशी तुलना करण्यापूर्वी बॅलन्समधून 0.3% (3/1000 = 0.003 = 0.3%) वजा केले जात आहेत.

```solidity
        }

        _update(balance0, balance1, _reserve0, _reserve1);
        emit Swap(msg.sender, amount0In, amount1In, amount0Out, amount1Out, to);
    }
```

`reserve0` आणि `reserve1` अपडेट करा, आणि आवश्यक असल्यास किंमत ॲक्युम्युलेटर्स आणि टाइमस्टॅम्प अपडेट करा आणि एक घटना उत्सर्जित करा.

##### समक्रमण किंवा स्किम

पेअर एक्सचेंजला वाटत असलेल्या रिझर्व्हच्या तुलनेत वास्तविक बॅलन्स समक्रमणाबाहेर जाणे शक्य आहे.
कॉन्ट्रॅक्टच्या संमतीशिवाय टोकन्स काढण्याचा कोणताही मार्ग नाही, परंतु ठेवी ही वेगळी बाब आहे. एखादे खाते `mint` किंवा `swap` पैकी कोणालाही कॉल न करता एक्सचेंजमध्ये टोकन्स हस्तांतरित करू शकते.

त्या बाबतीत दोन उपाय आहेत:

- `sync`, रिझर्व्ह सध्याच्या बॅलन्सवर अपडेट करा
- `skim`, अतिरिक्त रक्कम काढा. लक्षात घ्या की कोणत्याही खात्याला `skim` कॉल करण्याची परवानगी आहे कारण आम्हाला माहित नाही की टोकन्स कोणी जमा केले. ही माहिती एका घटनेमध्ये उत्सर्जित केली जाते, परंतु घटना ब्लॉकचेनवरून प्रवेशयोग्य नसतात.

```solidity
    // बॅलन्सला रिझर्व्हशी जुळण्यासाठी सक्ती करा
    function skim(address to) external lock {
        address _token0 = token0; // गॅस बचत
        address _token1 = token1; // गॅस बचत
        _safeTransfer(_token0, to, IERC20(_token0).balanceOf(address(this)).sub(reserve0));
        _safeTransfer(_token1, to, IERC20(_token1).balanceOf(address(this)).sub(reserve1));
    }



    // रिझर्व्हला बॅलन्सशी जुळण्यासाठी सक्ती करा
    function sync() external lock {
        _update(IERC20(token0).balanceOf(address(this)), IERC20(token1).balanceOf(address(this)), reserve0, reserve1);
    }
}
```

### UniswapV2Factory.sol {#uniswapv2factory}

[हे कॉन्ट्रॅक्ट](https://github.com/Uniswap/uniswap-v2-core/blob/master/contracts/UniswapV2Factory.sol) पेअर एक्सचेंजेस तयार करते.

```solidity
pragma solidity =0.5.16;

import './interfaces/IUniswapV2Factory.sol';
import './UniswapV2Pair.sol';

contract UniswapV2Factory is IUniswapV2Factory {
    address public feeTo;
    address public feeToSetter;
```

प्रोटोकॉल फी लागू करण्यासाठी हे स्थिती व्हेरिएबल्स आवश्यक आहेत ([श्वेतपत्रिका](https://app.uniswap.org/whitepaper.pdf), पृष्ठ 5 पहा).
`feeTo` पत्ता प्रोटोकॉल फीसाठी तरलता टोकन्स जमा करतो आणि `feeToSetter` हा `feeTo` ला वेगळ्या पत्त्यावर बदलण्याची परवानगी असलेला पत्ता आहे.

```solidity
    mapping(address => mapping(address => address)) public getPair;
    address[] public allPairs;
```

हे व्हेरिएबल्स पेअर्सचा, दोन टोकन प्रकारांमधील एक्सचेंजेसचा मागोवा ठेवतात.

पहिले, `getPair`, एक मॅपिंग आहे जे पेअर एक्सचेंज कॉन्ट्रॅक्टची ओळख ते अदलाबदल करत असलेल्या दोन ERC-20 टोकन्सच्या आधारे करते. ERC-20 टोकन्सची ओळख त्यांची अंमलबजावणी करणाऱ्या कॉन्ट्रॅक्ट्सच्या पत्त्यांद्वारे केली जाते, त्यामुळे कीज आणि मूल्य हे सर्व पत्ते आहेत. `tokenA` मधून `tokenB` मध्ये रूपांतरित करू देणाऱ्या पेअर एक्सचेंजचा पत्ता मिळवण्यासाठी, तुम्ही `getPair[<tokenA address>][<tokenB address>]` वापरता (किंवा उलट).

दुसरे व्हेरिएबल, `allPairs`, एक ॲरे आहे ज्यामध्ये या फॅक्टरीने तयार केलेल्या पेअर एक्सचेंजेसचे सर्व पत्ते समाविष्ट आहेत. इथेरियममध्ये तुम्ही मॅपिंगच्या सामग्रीवर पुनरावृत्ती करू शकत नाही किंवा सर्व कीजची सूची मिळवू शकत नाही, त्यामुळे ही फॅक्टरी कोणते एक्सचेंजेस व्यवस्थापित करते हे जाणून घेण्याचा हा व्हेरिएबल एकमेव मार्ग आहे.

टीप: तुम्ही मॅपिंगच्या सर्व कीजवर पुनरावृत्ती करू शकत नाही याचे कारण असे आहे की कॉन्ट्रॅक्ट डेटा स्टोरेज _महाग_ आहे, त्यामुळे आपण त्याचा जितका कमी वापर करू तितके चांगले आणि आपण ते जितक्या कमी वेळा बदलू तितके चांगले. तुम्ही [पुनरावृत्तीला समर्थन देणारी मॅपिंग्ज](https://github.com/ethereum/dapp-bin/blob/master/library/iterable_mapping.sol) तयार करू शकता, परंतु त्यांना कीजच्या सूचीसाठी अतिरिक्त स्टोरेजची आवश्यकता असते. बहुतेक ॲप्लिकेशन्समध्ये तुम्हाला त्याची आवश्यकता नसते.

```solidity
    event PairCreated(address indexed token0, address indexed token1, address pair, uint);
```

जेव्हा नवीन पेअर एक्सचेंज तयार केले जाते तेव्हा ही घटना उत्सर्जित केली जाते. यामध्ये टोकन्सचे पत्ते, पेअर एक्सचेंजचा पत्ता आणि फॅक्टरीद्वारे व्यवस्थापित केलेल्या एक्सचेंजेसची एकूण संख्या समाविष्ट असते.

```solidity
    constructor(address _feeToSetter) public {
        feeToSetter = _feeToSetter;
    }
```

कन्स्ट्रक्टर फक्त एकच गोष्ट करतो ती म्हणजे `feeToSetter` निर्दिष्ट करणे. फॅक्टरीज फीशिवाय सुरू होतात आणि फक्त `feeSetter` ते बदलू शकतो.

```solidity
    function allPairsLength() external view returns (uint) {
        return allPairs.length;
    }
```

हे फंक्शन एक्सचेंज पेअर्सची संख्या परत करते.

```solidity
    function createPair(address tokenA, address tokenB) external returns (address pair) {
```

दोन ERC-20 टोकन्समधील पेअर एक्सचेंज तयार करणे हे फॅक्टरीचे मुख्य फंक्शन आहे. लक्षात घ्या की कोणीही या फंक्शनला कॉल करू शकतो. नवीन पेअर एक्सचेंज तयार करण्यासाठी तुम्हाला युनिस्वॅपकडून परवानगीची आवश्यकता नाही.

```solidity
        require(tokenA != tokenB, 'UniswapV2: IDENTICAL_ADDRESSES');
        (address token0, address token1) = tokenA < tokenB ? (tokenA, tokenB) : (tokenB, tokenA);
```

आम्हाला नवीन एक्सचेंजचा पत्ता निश्चित असावा असे वाटते, जेणेकरून त्याची आगाऊ साखळीबाह्य गणना केली जाऊ शकेल (हे [स्तर २ (l2) व्यवहारांसाठी](/developers/docs/scaling/) उपयुक्त ठरू शकते).
हे करण्यासाठी आम्हाला टोकन पत्त्यांचा एक सुसंगत क्रम असणे आवश्यक आहे, मग ते आम्हाला कोणत्याही क्रमाने प्राप्त झाले असले तरीही, त्यामुळे आम्ही त्यांना येथे क्रमवारी लावतो.

```solidity
        require(token0 != address(0), 'UniswapV2: ZERO_ADDRESS');
        require(getPair[token0][token1] == address(0), 'UniswapV2: PAIR_EXISTS'); // एकच तपासणी पुरेशी आहे
```

मोठे तरलता पूल लहान पूल्सपेक्षा चांगले असतात, कारण त्यांच्या किंमती अधिक स्थिर असतात. आम्हाला टोकन्सच्या प्रति पेअर एकापेक्षा जास्त तरलता पूल नको आहे. जर आधीच एखादे एक्सचेंज असेल, तर त्याच पेअरसाठी दुसरे तयार करण्याची गरज नाही.

```solidity
        bytes memory bytecode = type(UniswapV2Pair).creationCode;
```

नवीन कॉन्ट्रॅक्ट तयार करण्यासाठी आम्हाला ते तयार करणारा कोड आवश्यक आहे (कन्स्ट्रक्टर फंक्शन आणि प्रत्यक्ष कॉन्ट्रॅक्टचा EVM बाइटकोड मेमरीमध्ये लिहिणारा कोड दोन्ही). साधारणपणे Solidity मध्ये आम्ही फक्त `addr = new <name of contract>(<constructor parameters>)` वापरतो आणि कंपायलर आमच्यासाठी सर्व गोष्टींची काळजी घेतो, परंतु निश्चित कॉन्ट्रॅक्ट पत्ता मिळवण्यासाठी आम्हाला [CREATE2 ऑपकोड](https://eips.ethereum.org/EIPS/eip-1014) वापरण्याची आवश्यकता आहे.
जेव्हा हा कोड लिहिला गेला तेव्हा त्या ऑपकोडला Solidity द्वारे अद्याप समर्थन दिले गेले नव्हते, त्यामुळे मॅन्युअली कोड मिळवणे आवश्यक होते. ही आता समस्या राहिलेली नाही, कारण [Solidity आता CREATE2 ला समर्थन देते](https://docs.soliditylang.org/en/v0.8.3/control-structures.html#salted-contract-creations-create2).

```solidity
        bytes32 salt = keccak256(abi.encodePacked(token0, token1));
        assembly {
            pair := create2(0, add(bytecode, 32), mload(bytecode), salt)
        }
```

जेव्हा एखाद्या ऑपकोडला Solidity द्वारे अद्याप समर्थन दिले जात नाही तेव्हा आम्ही [इनलाइन असेंब्ली](https://docs.soliditylang.org/en/v0.8.3/assembly.html) वापरून त्याला कॉल करू शकतो.

```solidity
        IUniswapV2Pair(pair).initialize(token0, token1);
```

नवीन एक्सचेंजला ते कोणत्या दोन टोकन्सची अदलाबदल करते हे सांगण्यासाठी `initialize` फंक्शनला कॉल करा.

```solidity
        getPair[token0][token1] = pair;
        getPair[token1][token0] = pair; // उलट दिशेने मॅपिंग पॉप्युलेट करा
        allPairs.push(pair);
        emit PairCreated(token0, token1, pair, allPairs.length);
    }
```

नवीन पेअरची माहिती स्थिती व्हेरिएबल्समध्ये सेव्ह करा आणि जगाला नवीन पेअर एक्सचेंजची माहिती देण्यासाठी एक घटना उत्सर्जित करा.

```solidity
    function setFeeTo(address _feeTo) external {
        require(msg.sender == feeToSetter, 'UniswapV2: FORBIDDEN');
        feeTo = _feeTo;
    }

    function setFeeToSetter(address _feeToSetter) external {
        require(msg.sender == feeToSetter, 'UniswapV2: FORBIDDEN');
        feeToSetter = _feeToSetter;
    }
}
```

ही दोन फंक्शन्स `feeSetter` ला फी प्राप्तकर्त्यावर (जर कोणी असेल तर) नियंत्रण ठेवण्याची आणि `feeSetter` ला नवीन पत्त्यावर बदलण्याची परवानगी देतात.

### UniswapV2ERC20.sol {#uniswapv2erc20}

[हे कॉन्ट्रॅक्ट](https://github.com/Uniswap/uniswap-v2-core/blob/master/contracts/UniswapV2ERC20.sol) ERC-20 तरलता टोकन लागू करते. हे [ओपनझेपलिन ERC-20 कॉन्ट्रॅक्टसारखे](/developers/tutorials/erc20-annotated-code) आहे, त्यामुळे मी फक्त जो भाग वेगळा आहे, `permit` कार्यक्षमता, तोच स्पष्ट करेन.

इथेरियमवरील व्यवहारांसाठी इथर (ETH) खर्च होतो, जे खऱ्या पैशाच्या समतुल्य आहे. जर तुमच्याकडे ERC-20 टोकन्स असतील परंतु ETH नसेल, तर तुम्ही व्यवहार पाठवू शकत नाही, त्यामुळे तुम्ही त्यांच्यासोबत काहीही करू शकत नाही. ही समस्या टाळण्यासाठी एक उपाय म्हणजे [मेटा-व्यवहार](https://docs.uniswap.org/contracts/v2/guides/smart-contract-integration/supporting-meta-transactions).
टोकन्सचा मालक एका व्यवहारावर स्वाक्षरी करतो जो दुसऱ्या कोणालातरी साखळीबाह्य टोकन्स काढण्याची परवानगी देतो आणि इंटरनेटचा वापर करून तो प्राप्तकर्त्याला पाठवतो. प्राप्तकर्ता, ज्याच्याकडे ETH आहे, तो नंतर मालकाच्या वतीने परवाना सबमिट करतो.

```solidity
    bytes32 public DOMAIN_SEPARATOR;
    // keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)");
    bytes32 public constant PERMIT_TYPEHASH = 0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9;
```

हा हॅश [व्यवहार प्रकारासाठी आयडेंटिफायर](https://eips.ethereum.org/EIPS/eip-712#rationale-for-typehash) आहे. आम्ही येथे समर्थन देत असलेला एकमेव प्रकार म्हणजे या पॅरामीटर्ससह `Permit`.

```solidity
    mapping(address => uint) public nonces;
```

प्राप्तकर्त्यासाठी बनावट डिजिटल स्वाक्षरी करणे शक्य नाही. तथापि, तोच व्यवहार दोनदा पाठवणे क्षुल्लक आहे (हा [रिप्ले ॲटॅकचा](https://wikipedia.org/wiki/Replay_attack) एक प्रकार आहे). हे टाळण्यासाठी, आम्ही [नॉन्स](https://wikipedia.org/wiki/Cryptographic_nonce) वापरतो. जर नवीन `Permit` चा नॉन्स वापरलेल्या शेवटच्या नॉन्सपेक्षा एकने जास्त नसेल, तर आम्ही तो अवैध असल्याचे गृहीत धरतो.

```solidity
    constructor() public {
        uint chainId;
        assembly {
            chainId := chainid
        }
```

[चेन आयडेंटिफायर](https://chainid.network/) पुनर्प्राप्त करण्यासाठी हा कोड आहे. हे [Yul](https://docs.soliditylang.org/en/v0.8.4/yul.html) नावाच्या EVM असेंब्ली बोलीचा वापर करते. लक्षात घ्या की Yul च्या सध्याच्या आवृत्तीमध्ये तुम्हाला `chainid()` वापरावे लागेल, `chainid` नाही.

```solidity
        DOMAIN_SEPARATOR = keccak256(
            abi.encode(
                keccak256('EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)'),
                keccak256(bytes(name)),
                keccak256(bytes('1')),
                chainId,
                address(this)
            )
        );
    }
```

EIP-712 साठी [डोमेन सेपरेटरची](https://eips.ethereum.org/EIPS/eip-712#rationale-for-domainseparator) गणना करा.

```solidity
    function permit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) external {
```

हे फंक्शन आहे जे परवानग्या लागू करते. हे संबंधित फील्ड्स आणि [स्वाक्षरीसाठी](https://yos.io/2018/11/16/ethereum-signatures/) तीन स्केलर मूल्ये (v, r, आणि s) पॅरामीटर्स म्हणून प्राप्त करते.

```solidity
        require(deadline >= block.timestamp, 'UniswapV2: EXPIRED');
```

अंतिम मुदतीनंतर व्यवहार स्वीकारू नका.

```solidity
        bytes32 digest = keccak256(
            abi.encodePacked(
                '\x19\x01',
                DOMAIN_SEPARATOR,
                keccak256(abi.encode(PERMIT_TYPEHASH, owner, spender, value, nonces[owner]++, deadline))
            )
        );
```

`abi.encodePacked(...)` हा संदेश आम्हाला मिळण्याची अपेक्षा आहे. नॉन्स काय असावा हे आम्हाला माहित आहे, त्यामुळे आम्हाला तो पॅरामीटर म्हणून मिळवण्याची गरज नाही.

इथेरियम स्वाक्षरी अल्गोरिदमला स्वाक्षरी करण्यासाठी 256 बिट्स मिळण्याची अपेक्षा असते, त्यामुळे आम्ही `keccak256` हॅश फंक्शन वापरतो.

```solidity
        address recoveredAddress = ecrecover(digest, v, r, s);
```

डायजेस्ट आणि स्वाक्षरीवरून आम्ही [ecrecover](https://coders-errand.com/ecrecover-signature-verification-ethereum/) वापरून स्वाक्षरी केलेल्याचा पत्ता मिळवू शकतो.

```solidity
        require(recoveredAddress != address(0) && recoveredAddress == owner, 'UniswapV2: INVALID_SIGNATURE');
        _approve(owner, spender, value);
    }

```

जर सर्व काही ठीक असेल, तर याला [ERC-20 मंजूर करणे](https://eips.ethereum.org/EIPS/eip-20#approve) म्हणून माना.

## पेरिफेरी कॉन्ट्रॅक्ट्स {#periphery-contracts}

पेरिफेरी कॉन्ट्रॅक्ट्स हे युनिस्वॅपसाठी API (ॲप्लिकेशन प्रोग्राम इंटरफेस) आहेत. ते बाह्य कॉल्ससाठी उपलब्ध आहेत, मग ते इतर कॉन्ट्रॅक्ट्समधून असोत किंवा विकेंद्रित ॲप्लिकेशन्समधून. तुम्ही थेट कोर कॉन्ट्रॅक्ट्सना कॉल करू शकता, परंतु ते अधिक गुंतागुंतीचे आहे आणि जर तुम्ही चूक केली तर तुमचे मूल्य गमावले जाऊ शकते. कोर कॉन्ट्रॅक्ट्समध्ये केवळ त्यांची फसवणूक होणार नाही याची खात्री करण्यासाठी चाचण्या असतात, इतर कोणासाठीही सॅनिटी चेक्स नसतात. ते पेरिफेरीमध्ये असतात जेणेकरून आवश्यकतेनुसार ते अपडेट केले जाऊ शकतील.

### UniswapV2Router01.sol {#uniswapv2router01}

[या कॉन्ट्रॅक्टमध्ये](https://github.com/Uniswap/uniswap-v2-periphery/blob/master/contracts/UniswapV2Router01.sol) समस्या आहेत, आणि [यापुढे त्याचा वापर केला जाऊ नये](https://docs.uniswap.org/contracts/v2/reference/smart-contracts/router-01). सुदैवाने, पेरिफेरी कॉन्ट्रॅक्ट्स स्टेटलेस असतात आणि त्यांच्याकडे कोणतीही मालमत्ता नसते, त्यामुळे ते नाकारणे आणि लोकांना त्याऐवजी `UniswapV2Router02` हा पर्याय वापरण्याचा सल्ला देणे सोपे आहे.

### UniswapV2Router02.sol {#uniswapv2router02}

बहुतांश प्रकरणांमध्ये तुम्ही [या कॉन्ट्रॅक्टद्वारे](https://github.com/Uniswap/uniswap-v2-periphery/blob/master/contracts/UniswapV2Router02.sol) युनिस्वॅप वापराल.
तुम्ही ते कसे वापरायचे ते [येथे](https://docs.uniswap.org/contracts/v2/reference/smart-contracts/router-02) पाहू शकता.

```solidity
pragma solidity =0.6.6;

import '@uniswap/v2-core/contracts/interfaces/IUniswapV2Factory.sol';
import '@uniswap/lib/contracts/libraries/TransferHelper.sol';

import './interfaces/IUniswapV2Router02.sol';
import './libraries/UniswapV2Library.sol';
import './libraries/SafeMath.sol';
import './interfaces/IERC20.sol';
import './interfaces/IWETH.sol';
```

यापैकी बहुतांश गोष्टी आपण यापूर्वी पाहिल्या आहेत किंवा त्या अगदी स्पष्ट आहेत. याला एक अपवाद म्हणजे `IWETH.sol`. युनिस्वॅप v2 कोणत्याही ERC-20 टोकन्सच्या जोडीसाठी अदलाबदल करण्यास अनुमती देते, परंतु इथर (ETH) स्वतः एक ERC-20 टोकन नाही. ते या मानकापूर्वीचे आहे आणि अद्वितीय यंत्रणेद्वारे त्याचे हस्तांतरण केले जाते. ERC-20 टोकन्सना लागू होणाऱ्या कॉन्ट्रॅक्ट्समध्ये ETH चा वापर सक्षम करण्यासाठी लोकांनी [रॅप्ड इथर (weth)](https://weth.tkn.eth.limo/) कॉन्ट्रॅक्ट तयार केले. तुम्ही या कॉन्ट्रॅक्टला ETH पाठवता आणि ते तुम्हाला समतुल्य प्रमाणात WETH मिंट करून देते. किंवा तुम्ही WETH जाळू शकता आणि ETH परत मिळवू शकता.

```solidity
contract UniswapV2Router02 is IUniswapV2Router02 {
    using SafeMath for uint;

    address public immutable override factory;
    address public immutable override WETH;
```

राउटरला कोणती फॅक्टरी वापरायची हे माहित असणे आवश्यक आहे, आणि ज्या व्यवहारांसाठी WETH आवश्यक आहे त्यांच्यासाठी कोणते WETH कॉन्ट्रॅक्ट वापरायचे हे माहित असणे आवश्यक आहे. ही मूल्ये [अपरिवर्तनीय](https://docs.soliditylang.org/en/v0.8.3/contracts.html#constant-and-immutable-state-variables) आहेत, याचा अर्थ ती केवळ कन्स्ट्रक्टरमध्ये सेट केली जाऊ शकतात. यामुळे वापरकर्त्यांना असा विश्वास मिळतो की कोणीही त्यांना कमी प्रामाणिक कॉन्ट्रॅक्ट्सकडे निर्देशित करण्यासाठी बदलू शकणार नाही.

```solidity
    modifier ensure(uint deadline) {
        require(deadline >= block.timestamp, 'UniswapV2Router: EXPIRED');
        _;
    }
```

हा मॉडिफायर हे सुनिश्चित करतो की वेळेची मर्यादा असलेले व्यवहार ("शक्य असल्यास Y वेळेपूर्वी X करा") त्यांच्या वेळेच्या मर्यादेनंतर होणार नाहीत.

```solidity
    constructor(address _factory, address _WETH) public {
        factory = _factory;
        WETH = _WETH;
    }
```

कन्स्ट्रक्टर फक्त अपरिवर्तनीय स्थिती व्हेरिएबल्स सेट करतो.

```solidity
    receive() external payable {
        assert(msg.sender == WETH); // फक्त WETH कॉन्ट्रॅक्टमधून फॉलबॅकद्वारे ETH स्वीकारा
    }
```

जेव्हा आपण WETH कॉन्ट्रॅक्टमधून टोकन्स परत ETH मध्ये रिडीम करतो तेव्हा या फंक्शनला कॉल केला जातो. केवळ आपण वापरत असलेल्या WETH कॉन्ट्रॅक्टलाच तसे करण्याचा अधिकार आहे.

#### तरलता जोडणे {#add-liquidity}

ही फंक्शन्स जोडीच्या अदलाबदलीमध्ये टोकन्स जोडतात, ज्यामुळे तरलता पूल वाढतो.

```solidity

    // **** तरलता जोडा ****
    function _addLiquidity(
```

या फंक्शनचा वापर जोडीच्या अदलाबदलीमध्ये जमा केल्या जाणाऱ्या A आणि B टोकन्सच्या प्रमाणाची गणना करण्यासाठी केला जातो.

```solidity
        address tokenA,
        address tokenB,
```

हे ERC-20 टोकन कॉन्ट्रॅक्ट्सचे पत्ते आहेत.

```solidity
        uint amountADesired,
        uint amountBDesired,
```

ही ती रक्कम आहे जी तरलता प्रदाता जमा करू इच्छितो. ती जमा केली जाणारी A आणि B ची कमाल रक्कम देखील आहे.

```solidity
        uint amountAMin,
        uint amountBMin
```

जमा करण्यासाठी ही किमान स्वीकार्य रक्कम आहे. जर या रकमेसह किंवा त्याहून अधिक रकमेसह व्यवहार होऊ शकत नसेल, तर तो पूर्ववत करा. जर तुम्हाला हे वैशिष्ट्य नको असेल, तर फक्त शून्य निर्दिष्ट करा.

तरलता प्रदाता सामान्यतः किमान रक्कम निर्दिष्ट करतात, कारण त्यांना व्यवहार सध्याच्या विनिमय दराच्या जवळ असलेल्या दरापर्यंत मर्यादित ठेवायचा असतो. जर विनिमय दरात खूप चढ-उतार होत असेल तर याचा अर्थ असा असू शकतो की मूळ मूल्ये बदलणाऱ्या बातम्या आहेत, आणि त्यांना काय करायचे ते मॅन्युअली ठरवायचे असते.

उदाहरणार्थ, अशा प्रकरणाची कल्पना करा जिथे विनिमय दर एकास एक आहे आणि तरलता प्रदाता ही मूल्ये निर्दिष्ट करतो:

| पॅरामीटर      | मूल्य |
| -------------- | ----: |
| amountADesired |  1000 |
| amountBDesired |  1000 |
| amountAMin     |   900 |
| amountBMin     |   800 |

जोपर्यंत विनिमय दर 0.9 आणि 1.25 च्या दरम्यान राहतो, तोपर्यंत व्यवहार होतो. जर विनिमय दर त्या श्रेणीच्या बाहेर गेला, तर व्यवहार रद्द होतो.

या खबरदारीचे कारण असे आहे की व्यवहार त्वरित होत नाहीत, तुम्ही ते सबमिट करता आणि शेवटी एक प्रमाणक त्यांना ब्लॉकमध्ये समाविष्ट करणार असतो (जोपर्यंत तुमची गॅसची किंमत खूप कमी नसेल, अशा परिस्थितीत तुम्हाला तो ओव्हरराइट करण्यासाठी त्याच नॉन्ससह आणि उच्च गॅसच्या किंमतीसह दुसरा व्यवहार सबमिट करावा लागेल). सबमिशन आणि समावेशाच्या दरम्यानच्या काळात काय होते यावर तुमचे नियंत्रण नसते.

```solidity
    ) internal virtual returns (uint amountA, uint amountB) {
```

हे फंक्शन ती रक्कम परत करते जी तरलता प्रदात्याने जमा केली पाहिजे जेणेकरून रिझर्व्हमधील सध्याच्या गुणोत्तराइतके गुणोत्तर मिळेल.

```solidity
        // जर जोडी अद्याप अस्तित्वात नसेल तर ती तयार करा
        if (IUniswapV2Factory(factory).getPair(tokenA, tokenB) == address(0)) {
            IUniswapV2Factory(factory).createPair(tokenA, tokenB);
        }
```

जर या टोकन जोडीसाठी अद्याप कोणतीही अदलाबदल नसेल, तर ती तयार करा.

```solidity
        (uint reserveA, uint reserveB) = UniswapV2Library.getReserves(factory, tokenA, tokenB);
```

जोडीमधील सध्याचे रिझर्व्ह मिळवा.

```solidity
        if (reserveA == 0 && reserveB == 0) {
            (amountA, amountB) = (amountADesired, amountBDesired);
```

जर सध्याचे रिझर्व्ह रिक्त असतील तर ही एक नवीन जोडीची अदलाबदल आहे. जमा केली जाणारी रक्कम तरलता प्रदाता देऊ इच्छित असलेल्या रकमेइतकीच असली पाहिजे.

```solidity
        } else {
            uint amountBOptimal = UniswapV2Library.quote(amountADesired, reserveA, reserveB);
```

जर आपल्याला रक्कम किती असेल हे पाहण्याची आवश्यकता असेल, तर आपण [या फंक्शनचा](https://github.com/Uniswap/uniswap-v2-periphery/blob/master/contracts/libraries/UniswapV2Library.sol#L35) वापर करून इष्टतम रक्कम मिळवतो. आपल्याला सध्याच्या रिझर्व्हसारखेच गुणोत्तर हवे आहे.

```solidity
            if (amountBOptimal <= amountBDesired) {
                require(amountBOptimal >= amountBMin, 'UniswapV2Router: INSUFFICIENT_B_AMOUNT');
                (amountA, amountB) = (amountADesired, amountBOptimal);
```

जर `amountBOptimal` तरलता प्रदाता जमा करू इच्छित असलेल्या रकमेपेक्षा लहान असेल, तर याचा अर्थ असा की टोकन B सध्या तरलता जमा करणाऱ्याच्या विचारापेक्षा अधिक मौल्यवान आहे, त्यामुळे कमी रक्कम आवश्यक आहे.

```solidity
            } else {
                uint amountAOptimal = UniswapV2Library.quote(amountBDesired, reserveB, reserveA);
                assert(amountAOptimal <= amountADesired);
                require(amountAOptimal >= amountAMin, 'UniswapV2Router: INSUFFICIENT_A_AMOUNT');
                (amountA, amountB) = (amountAOptimal, amountBDesired);
```

जर इष्टतम B रक्कम इच्छित B रकमेपेक्षा जास्त असेल, तर याचा अर्थ असा की B टोकन्स सध्या तरलता जमा करणाऱ्याच्या विचारापेक्षा कमी मौल्यवान आहेत, त्यामुळे जास्त रक्कम आवश्यक आहे. तथापि, इच्छित रक्कम ही कमाल रक्कम आहे, त्यामुळे आपण तसे करू शकत नाही. त्याऐवजी आपण B टोकन्सच्या इच्छित रकमेसाठी A टोकन्सच्या इष्टतम संख्येची गणना करतो.

हे सर्व एकत्र केल्यावर आपल्याला हा आलेख मिळतो. समजा तुम्ही एक हजार A टोकन्स (निळी रेषा) आणि एक हजार B टोकन्स (लाल रेषा) जमा करण्याचा प्रयत्न करत आहात. x अक्ष हा विनिमय दर आहे, A/B. जर x=1 असेल, तर त्यांचे मूल्य समान आहे आणि तुम्ही प्रत्येकी एक हजार जमा करता. जर x=2 असेल, तर A चे मूल्य B च्या दुप्पट आहे (तुम्हाला प्रत्येक A टोकनसाठी दोन B टोकन्स मिळतात) त्यामुळे तुम्ही एक हजार B टोकन्स जमा करता, परंतु फक्त 500 A टोकन्स. जर x=0.5 असेल, तर परिस्थिती उलट असते, एक हजार A टोकन्स आणि पाचशे B टोकन्स.

![Graph](liquidityProviderDeposit.png)

तुम्ही थेट कोर कॉन्ट्रॅक्टमध्ये तरलता जमा करू शकता ([UniswapV2Pair::mint](https://github.com/Uniswap/uniswap-v2-core/blob/master/contracts/UniswapV2Pair.sol#L110) वापरून), परंतु कोर कॉन्ट्रॅक्ट केवळ स्वतःची फसवणूक होत नाही ना हे तपासते, त्यामुळे तुम्ही तुमचा व्यवहार सबमिट करता आणि तो कार्यान्वित होतो या दरम्यान विनिमय दर बदलल्यास तुमचे मूल्य गमावण्याचा धोका असतो. जर तुम्ही पेरिफेरी कॉन्ट्रॅक्ट वापरत असाल, तर ते तुम्ही किती रक्कम जमा करावी हे ठरवते आणि ती त्वरित जमा करते, त्यामुळे विनिमय दर बदलत नाही आणि तुमचे काहीही नुकसान होत नाही.

```solidity
    function addLiquidity(
        address tokenA,
        address tokenB,
        uint amountADesired,
        uint amountBDesired,
        uint amountAMin,
        uint amountBMin,
        address to,
        uint deadline
```

तरलता जमा करण्यासाठी व्यवहाराद्वारे या फंक्शनला कॉल केला जाऊ शकतो. बहुतांश पॅरामीटर्स वरील `_addLiquidity` प्रमाणेच आहेत, दोन अपवाद वगळता:

. `to` हा तो पत्ता आहे ज्याला पूलमधील तरलता प्रदात्याचा वाटा दर्शविण्यासाठी नवीन तरलता टोकन्स मिंट करून मिळतात
. `deadline` ही व्यवहारावरील वेळेची मर्यादा आहे

```solidity
    ) external virtual override ensure(deadline) returns (uint amountA, uint amountB, uint liquidity) {
        (amountA, amountB) = _addLiquidity(tokenA, tokenB, amountADesired, amountBDesired, amountAMin, amountBMin);
        address pair = UniswapV2Library.pairFor(factory, tokenA, tokenB);
```

आपण प्रत्यक्षात जमा करावयाच्या रकमेची गणना करतो आणि नंतर तरलता पूलचा पत्ता शोधतो. गॅस वाचवण्यासाठी आपण हे फॅक्टरीला विचारून करत नाही, तर `pairFor` हे लायब्ररी फंक्शन वापरून करतो (खाली लायब्ररीमध्ये पहा)

```solidity
        TransferHelper.safeTransferFrom(tokenA, msg.sender, pair, amountA);
        TransferHelper.safeTransferFrom(tokenB, msg.sender, pair, amountB);
```

वापरकर्त्याकडून जोडीच्या अदलाबदलीमध्ये टोकन्सची योग्य रक्कम हस्तांतरित करा.

```solidity
        liquidity = IUniswapV2Pair(pair).mint(to);
    }
```

त्या बदल्यात पूलच्या आंशिक मालकीसाठी `to` पत्त्याला तरलता टोकन्स द्या. कोर कॉन्ट्रॅक्टचे `mint` फंक्शन पाहते की त्याच्याकडे किती अतिरिक्त टोकन्स आहेत (गेल्या वेळी तरलता बदलली तेव्हा त्याच्याकडे जे होते त्याच्या तुलनेत) आणि त्यानुसार तरलता मिंट करते.

```solidity
    function addLiquidityETH(
        address token,
        uint amountTokenDesired,
```

जेव्हा एखादा तरलता प्रदाता टोकन/ETH जोडीच्या अदलाबदलीसाठी तरलता प्रदान करू इच्छितो, तेव्हा काही फरक असतात. कॉन्ट्रॅक्ट तरलता प्रदात्यासाठी ETH रॅप करण्याचे काम हाताळते. वापरकर्त्याला किती ETH जमा करायचे आहेत हे निर्दिष्ट करण्याची आवश्यकता नाही, कारण वापरकर्ता ते फक्त व्यवहारासोबत पाठवतो (ही रक्कम `msg.value` मध्ये उपलब्ध असते).

```solidity
        uint amountTokenMin,
        uint amountETHMin,
        address to,
        uint deadline
    ) external virtual override payable ensure(deadline) returns (uint amountToken, uint amountETH, uint liquidity) {
        (amountToken, amountETH) = _addLiquidity(
            token,
            WETH,
            amountTokenDesired,
            msg.value,
            amountTokenMin,
            amountETHMin
        );
        address pair = UniswapV2Library.pairFor(factory, token, WETH);
        TransferHelper.safeTransferFrom(token, msg.sender, pair, amountToken);
        IWETH(WETH).deposit{value: amountETH}();
        assert(IWETH(WETH).transfer(pair, amountETH));
```

ETH जमा करण्यासाठी कॉन्ट्रॅक्ट प्रथम त्याला WETH मध्ये रॅप करते आणि नंतर WETH जोडीमध्ये हस्तांतरित करते. लक्षात घ्या की हस्तांतरण `assert` मध्ये रॅप केलेले आहे. याचा अर्थ असा की जर हस्तांतरण अयशस्वी झाले तर हा कॉन्ट्रॅक्ट कॉल देखील अयशस्वी होतो, आणि त्यामुळे रॅपिंग खरोखर होत नाही.

```solidity
        liquidity = IUniswapV2Pair(pair).mint(to);
        // डस्ट eth असल्यास, परतावा द्या
        if (msg.value > amountETH) TransferHelper.safeTransferETH(msg.sender, msg.value - amountETH);
    }
```

वापरकर्त्याने आपल्याला आधीच ETH पाठवले आहेत, त्यामुळे जर काही अतिरिक्त उरले असेल (कारण दुसरे टोकन वापरकर्त्याच्या विचारापेक्षा कमी मौल्यवान आहे), तर आपल्याला परतावा जारी करणे आवश्यक आहे.

#### तरलता काढून घेणे {#remove-liquidity}

ही फंक्शन्स तरलता काढून घेतील आणि तरलता प्रदात्याला परतफेड करतील.

```solidity
    // **** तरलता काढा ****
    function removeLiquidity(
        address tokenA,
        address tokenB,
        uint liquidity,
        uint amountAMin,
        uint amountBMin,
        address to,
        uint deadline
    ) public virtual override ensure(deadline) returns (uint amountA, uint amountB) {
```

तरलता काढून घेण्याचे सर्वात सोपे प्रकरण. प्रत्येक टोकनची किमान रक्कम असते जी तरलता प्रदाता स्वीकारण्यास सहमत असतो, आणि ते अंतिम मुदतीपूर्वी होणे आवश्यक आहे.

```solidity
        address pair = UniswapV2Library.pairFor(factory, tokenA, tokenB);
        IUniswapV2Pair(pair).transferFrom(msg.sender, pair, liquidity); // जोडीला तरलता पाठवा
        (uint amount0, uint amount1) = IUniswapV2Pair(pair).burn(to);
```

कोर कॉन्ट्रॅक्टचे `burn` फंक्शन वापरकर्त्याला टोकन्स परत देण्याचे काम हाताळते.

```solidity
        (address token0,) = UniswapV2Library.sortTokens(tokenA, tokenB);
```

जेव्हा एखादे फंक्शन अनेक मूल्ये परत करते, परंतु आपल्याला त्यापैकी काहींमध्येच स्वारस्य असते, तेव्हा आपण केवळ तीच मूल्ये अशा प्रकारे मिळवतो. मूल्य वाचून त्याचा कधीही वापर न करण्यापेक्षा गॅसच्या दृष्टीने हे काहीसे स्वस्त आहे.

```solidity
        (amountA, amountB) = tokenA == token0 ? (amount0, amount1) : (amount1, amount0);
```

कोर कॉन्ट्रॅक्ट ज्या प्रकारे रक्कम परत करते (कमी पत्त्याचे टोकन प्रथम) त्यावरून वापरकर्त्याला ज्या प्रकारे अपेक्षित आहे ( `tokenA` आणि `tokenB` शी संबंधित) त्यामध्ये रकमेचे भाषांतर करा.

```solidity
        require(amountA >= amountAMin, 'UniswapV2Router: INSUFFICIENT_A_AMOUNT');
        require(amountB >= amountBMin, 'UniswapV2Router: INSUFFICIENT_B_AMOUNT');
    }
```

प्रथम हस्तांतरण करणे आणि नंतर ते कायदेशीर आहे की नाही हे सत्यापित करणे ठीक आहे, कारण जर ते नसेल तर आपण सर्व स्थिती बदल पूर्ववत करू.

```solidity
    function removeLiquidityETH(
        address token,
        uint liquidity,
        uint amountTokenMin,
        uint amountETHMin,
        address to,
        uint deadline
    ) public virtual override ensure(deadline) returns (uint amountToken, uint amountETH) {
        (amountToken, amountETH) = removeLiquidity(
            token,
            WETH,
            liquidity,
            amountTokenMin,
            amountETHMin,
            address(this),
            deadline
        );
        TransferHelper.safeTransfer(token, to, amountToken);
        IWETH(WETH).withdraw(amountETH);
        TransferHelper.safeTransferETH(to, amountETH);
    }
```

ETH साठी तरलता काढून घेणे जवळजवळ सारखेच आहे, फक्त एवढाच फरक आहे की आपल्याला WETH टोकन्स मिळतात आणि नंतर आपण तरलता प्रदात्याला परत देण्यासाठी त्यांना ETH साठी रिडीम करतो.

```solidity
    function removeLiquidityWithPermit(
        address tokenA,
        address tokenB,
        uint liquidity,
        uint amountAMin,
        uint amountBMin,
        address to,
        uint deadline,
        bool approveMax, uint8 v, bytes32 r, bytes32 s
    ) external virtual override returns (uint amountA, uint amountB) {
        address pair = UniswapV2Library.pairFor(factory, tokenA, tokenB);
        uint value = approveMax ? uint(-1) : liquidity;
        IUniswapV2Pair(pair).permit(msg.sender, address(this), value, deadline, v, r, s);
        (amountA, amountB) = removeLiquidity(tokenA, tokenB, liquidity, amountAMin, amountBMin, to, deadline);
    }


    function removeLiquidityETHWithPermit(
        address token,
        uint liquidity,
        uint amountTokenMin,
        uint amountETHMin,
        address to,
        uint deadline,
        bool approveMax, uint8 v, bytes32 r, bytes32 s
    ) external virtual override returns (uint amountToken, uint amountETH) {
        address pair = UniswapV2Library.pairFor(factory, token, WETH);
        uint value = approveMax ? uint(-1) : liquidity;
        IUniswapV2Pair(pair).permit(msg.sender, address(this), value, deadline, v, r, s);
        (amountToken, amountETH) = removeLiquidityETH(token, liquidity, amountTokenMin, amountETHMin, to, deadline);
    }
```

ही फंक्शन्स इथर नसलेल्या वापरकर्त्यांना [परमिट यंत्रणेचा](#uniswapv2erc20) वापर करून पूलमधून रक्कम काढण्याची अनुमती देण्यासाठी मेटा-व्यवहार रिले करतात.

```solidity

    // **** तरलता काढा (फी-ऑन-हस्तांतरण टोकन्सना समर्थन देत आहे) ****
    function removeLiquidityETHSupportingFeeOnTransferTokens(
        address token,
        uint liquidity,
        uint amountTokenMin,
        uint amountETHMin,
        address to,
        uint deadline
    ) public virtual override ensure(deadline) returns (uint amountETH) {
        (, amountETH) = removeLiquidity(
            token,
            WETH,
            liquidity,
            amountTokenMin,
            amountETHMin,
            address(this),
            deadline
        );
        TransferHelper.safeTransfer(token, to, IERC20(token).balanceOf(address(this)));
        IWETH(WETH).withdraw(amountETH);
        TransferHelper.safeTransferETH(to, amountETH);
    }

```

हे फंक्शन अशा टोकन्ससाठी वापरले जाऊ शकते ज्यांचे हस्तांतरण किंवा स्टोरेज शुल्क आहे. जेव्हा एखाद्या टोकनला असे शुल्क असते तेव्हा आपल्याला किती टोकन परत मिळतील हे सांगण्यासाठी आपण `removeLiquidity` फंक्शनवर अवलंबून राहू शकत नाही, त्यामुळे आपल्याला प्रथम रक्कम काढावी लागते आणि नंतर शिल्लक मिळवावी लागते.

```solidity


    function removeLiquidityETHWithPermitSupportingFeeOnTransferTokens(
        address token,
        uint liquidity,
        uint amountTokenMin,
        uint amountETHMin,
        address to,
        uint deadline,
        bool approveMax, uint8 v, bytes32 r, bytes32 s
    ) external virtual override returns (uint amountETH) {
        address pair = UniswapV2Library.pairFor(factory, token, WETH);
        uint value = approveMax ? uint(-1) : liquidity;
        IUniswapV2Pair(pair).permit(msg.sender, address(this), value, deadline, v, r, s);
        amountETH = removeLiquidityETHSupportingFeeOnTransferTokens(
            token, liquidity, amountTokenMin, amountETHMin, to, deadline
        );
    }
```

अंतिम फंक्शन स्टोरेज शुल्काला मेटा-व्यवहारांसह एकत्र करते.

#### व्यापार {#trade}

```solidity
    // **** अदलाबदल ****
    // प्रारंभिक रक्कम पहिल्या जोडीला आधीच पाठवली असणे आवश्यक आहे
    function _swap(uint[] memory amounts, address[] memory path, address _to) internal virtual {
```

हे फंक्शन अंतर्गत प्रक्रिया करते जी व्यापाऱ्यांसाठी उघड केलेल्या फंक्शन्ससाठी आवश्यक असते.

```solidity
        for (uint i; i < path.length - 1; i++) {
```

मी हे लिहित असताना [388,160 ERC-20 टोकन्स](https://eth.blockscout.com/tokens) आहेत. जर प्रत्येक टोकन जोडीसाठी जोडीची अदलाबदल असती, तर 150 अब्जांहून अधिक जोडीच्या अदलाबदली झाल्या असत्या. सध्या संपूर्ण चेनवर [त्या संख्येच्या केवळ 0.1% खाती आहेत](https://eth.blockscout.com/stats/accountsGrowth). त्याऐवजी, स्वॅप फंक्शन्स मार्गाच्या संकल्पनेला समर्थन देतात. एक व्यापारी A ची B साठी, B ची C साठी आणि C ची D साठी अदलाबदल करू शकतो, त्यामुळे थेट A-D जोडीच्या अदलाबदलीची आवश्यकता नाही.

या बाजारांमधील किमती समक्रमित (synchronized) असतात, कारण जेव्हा त्या समक्रमित नसतात तेव्हा ते आर्बिट्रेजसाठी (arbitrage) संधी निर्माण करते. उदाहरणार्थ, A, B आणि C या तीन टोकन्सची कल्पना करा. प्रत्येक जोडीसाठी एक अशा तीन जोडीच्या अदलाबदली आहेत.

1. सुरुवातीची परिस्थिती
2. एक व्यापारी 24.695 A टोकन्स विकतो आणि 25.305 B टोकन्स मिळवतो.
3. व्यापारी 24.695 B टोकन्स 25.305 C टोकन्ससाठी विकतो, आणि अंदाजे 0.61 B टोकन्स नफा म्हणून ठेवतो.
4. त्यानंतर व्यापारी 24.695 C टोकन्स 25.305 A टोकन्ससाठी विकतो, आणि अंदाजे 0.61 C टोकन्स नफा म्हणून ठेवतो. व्यापाऱ्याकडे 0.61 अतिरिक्त A टोकन्स देखील असतात (व्यापाऱ्याकडे शेवटी उरलेले 25.305, वजा 24.695 ची मूळ गुंतवणूक).

| पायरी | A-B अदलाबदल                | B-C अदलाबदल                | A-C अदलाबदल                |
| ---- | --------------------------- | --------------------------- | --------------------------- |
| 1    | A:1000 B:1050 A/B=1.05      | B:1000 C:1050 B/C=1.05      | A:1050 C:1000 C/A=1.05      |
| 2    | A:1024.695 B:1024.695 A/B=1 | B:1000 C:1050 B/C=1.05      | A:1050 C:1000 C/A=1.05      |
| 3    | A:1024.695 B:1024.695 A/B=1 | B:1024.695 C:1024.695 B/C=1 | A:1050 C:1000 C/A=1.05      |
| 4    | A:1024.695 B:1024.695 A/B=1 | B:1024.695 C:1024.695 B/C=1 | A:1024.695 C:1024.695 C/A=1 |

```solidity
            (address input, address output) = (path[i], path[i + 1]);
            (address token0,) = UniswapV2Library.sortTokens(input, output);
            uint amountOut = amounts[i + 1];
```

आपण सध्या हाताळत असलेली जोडी मिळवा, तिची क्रमवारी लावा (जोडीसोबत वापरण्यासाठी) आणि अपेक्षित आउटपुट रक्कम मिळवा.

```solidity
            (uint amount0Out, uint amount1Out) = input == token0 ? (uint(0), amountOut) : (amountOut, uint(0));
```

जोडीच्या अदलाबदलीला ज्या प्रकारे अपेक्षित आहे त्या प्रकारे क्रमवारी लावलेली अपेक्षित आउटपुट रक्कम मिळवा.

```solidity
            address to = i < path.length - 2 ? UniswapV2Library.pairFor(factory, output, path[i + 2]) : _to;
```

ही शेवटची अदलाबदल आहे का? तसे असल्यास, व्यापारासाठी मिळालेले टोकन्स गंतव्यस्थानावर पाठवा. नसल्यास, ते पुढील जोडीच्या अदलाबदलीकडे पाठवा.

```solidity

            IUniswapV2Pair(UniswapV2Library.pairFor(factory, input, output)).swap(
                amount0Out, amount1Out, to, new bytes(0)
            );
        }
    }
```

टोकन्सची अदलाबदल करण्यासाठी प्रत्यक्षात जोडीच्या अदलाबदलीला कॉल करा. अदलाबदलीबद्दल सांगण्यासाठी आपल्याला कॉलबॅकची आवश्यकता नाही, त्यामुळे आपण त्या फील्डमध्ये कोणतेही बाइट्स पाठवत नाही.

```solidity
    function swapExactTokensForTokens(
```

एका टोकनची दुसऱ्या टोकनशी अदलाबदल करण्यासाठी व्यापाऱ्यांद्वारे या फंक्शनचा थेट वापर केला जातो.

```solidity
        uint amountIn,
        uint amountOutMin,
        address[] calldata path,
```

या पॅरामीटरमध्ये ERC-20 कॉन्ट्रॅक्ट्सचे पत्ते असतात. वर स्पष्ट केल्याप्रमाणे, हा एक ॲरे आहे कारण तुमच्याकडे असलेल्या मालमत्तेवरून तुम्हाला हव्या असलेल्या मालमत्तेपर्यंत पोहोचण्यासाठी तुम्हाला अनेक जोडीच्या अदलाबदलींमधून जावे लागू शकते.

Solidity मधील फंक्शन पॅरामीटर एकतर `memory` मध्ये किंवा `calldata` मध्ये साठवले जाऊ शकते. जर फंक्शन हे कॉन्ट्रॅक्टचा एंट्री पॉईंट असेल, ज्याला वापरकर्त्याकडून (व्यवहार वापरून) किंवा वेगळ्या कॉन्ट्रॅक्टमधून थेट कॉल केला असेल, तर पॅरामीटरचे मूल्य थेट कॉल डेटामधून घेतले जाऊ शकते. जर फंक्शनला अंतर्गत कॉल केला असेल, जसे की वरील `_swap`, तर पॅरामीटर्स `memory` मध्ये साठवावे लागतात. कॉल केलेल्या कॉन्ट्रॅक्टच्या दृष्टिकोनातून `calldata` हे केवळ वाचनीय (read only) असते.

`uint` किंवा `address` सारख्या स्केलर प्रकारांसह कंपायलर आपल्यासाठी स्टोरेजची निवड हाताळतो, परंतु ॲरेसह, जे मोठे आणि अधिक महाग असतात, आपण वापरल्या जाणाऱ्या स्टोरेजचा प्रकार निर्दिष्ट करतो.

```solidity
        address to,
        uint deadline
    ) external virtual override ensure(deadline) returns (uint[] memory amounts) {
```

रिटर्न व्हॅल्यूज नेहमी मेमरीमध्ये परत केल्या जातात.

```solidity
        amounts = UniswapV2Library.getAmountsOut(factory, amountIn, path);
        require(amounts[amounts.length - 1] >= amountOutMin, 'UniswapV2Router: INSUFFICIENT_OUTPUT_AMOUNT');
```

प्रत्येक अदलाबदलीमध्ये खरेदी करावयाच्या रकमेची गणना करा. जर परिणाम व्यापारी स्वीकारण्यास तयार असलेल्या किमान रकमेपेक्षा कमी असेल, तर व्यवहार पूर्ववत करा.

```solidity
        TransferHelper.safeTransferFrom(
            path[0], msg.sender, UniswapV2Library.pairFor(factory, path[0], path[1]), amounts[0]
        );
        _swap(amounts, path, to);
    }
```

शेवटी, पहिल्या जोडीच्या अदलाबदलीसाठी प्रारंभिक ERC-20 टोकन खात्यात हस्तांतरित करा आणि `_swap` ला कॉल करा. हे सर्व एकाच व्यवहारात घडत आहे, त्यामुळे जोडीच्या अदलाबदलीला माहित असते की कोणतेही अनपेक्षित टोकन्स या हस्तांतरणाचा भाग आहेत.

```solidity
    function swapTokensForExactTokens(
        uint amountOut,
        uint amountInMax,
        address[] calldata path,
        address to,
        uint deadline
    ) external virtual override ensure(deadline) returns (uint[] memory amounts) {
        amounts = UniswapV2Library.getAmountsIn(factory, amountOut, path);
        require(amounts[0] <= amountInMax, 'UniswapV2Router: EXCESSIVE_INPUT_AMOUNT');
        TransferHelper.safeTransferFrom(
            path[0], msg.sender, UniswapV2Library.pairFor(factory, path[0], path[1]), amounts[0]
        );
        _swap(amounts, path, to);
    }
```

मागील फंक्शन, `swapTokensForTokens`, व्यापाऱ्याला तो देऊ इच्छित असलेल्या इनपुट टोकन्सची अचूक संख्या आणि त्या बदल्यात तो प्राप्त करण्यास तयार असलेल्या आउटपुट टोकन्सची किमान संख्या निर्दिष्ट करण्याची अनुमती देते. हे फंक्शन उलट अदलाबदल करते, ते व्यापाऱ्याला त्याला हव्या असलेल्या आउटपुट टोकन्सची संख्या आणि त्यासाठी तो देऊ इच्छित असलेल्या इनपुट टोकन्सची कमाल संख्या निर्दिष्ट करू देते.

दोन्ही प्रकरणांमध्ये, व्यापाऱ्याला प्रथम या पेरिफेरी कॉन्ट्रॅक्टला ते हस्तांतरित करण्याची अनुमती देण्यासाठी मंजुरी द्यावी लागते.

```solidity
    function swapExactETHForTokens(uint amountOutMin, address[] calldata path, address to, uint deadline)
        external
        virtual
        override
        payable
        ensure(deadline)
        returns (uint[] memory amounts)
    {
        require(path[0] == WETH, 'UniswapV2Router: INVALID_PATH');
        amounts = UniswapV2Library.getAmountsOut(factory, msg.value, path);
        require(amounts[amounts.length - 1] >= amountOutMin, 'UniswapV2Router: INSUFFICIENT_OUTPUT_AMOUNT');
        IWETH(WETH).deposit{value: amounts[0]}();
        assert(IWETH(WETH).transfer(UniswapV2Library.pairFor(factory, path[0], path[1]), amounts[0]));
        _swap(amounts, path, to);
    }


    function swapTokensForExactETH(uint amountOut, uint amountInMax, address[] calldata path, address to, uint deadline)
        external
        virtual
        override
        ensure(deadline)
        returns (uint[] memory amounts)
    {
        require(path[path.length - 1] == WETH, 'UniswapV2Router: INVALID_PATH');
        amounts = UniswapV2Library.getAmountsIn(factory, amountOut, path);
        require(amounts[0] <= amountInMax, 'UniswapV2Router: EXCESSIVE_INPUT_AMOUNT');
        TransferHelper.safeTransferFrom(
            path[0], msg.sender, UniswapV2Library.pairFor(factory, path[0], path[1]), amounts[0]
        );
        _swap(amounts, path, address(this));
        IWETH(WETH).withdraw(amounts[amounts.length - 1]);
        TransferHelper.safeTransferETH(to, amounts[amounts.length - 1]);
    }



    function swapExactTokensForETH(uint amountIn, uint amountOutMin, address[] calldata path, address to, uint deadline)
        external
        virtual
        override
        ensure(deadline)
        returns (uint[] memory amounts)
    {
        require(path[path.length - 1] == WETH, 'UniswapV2Router: INVALID_PATH');
        amounts = UniswapV2Library.getAmountsOut(factory, amountIn, path);
        require(amounts[amounts.length - 1] >= amountOutMin, 'UniswapV2Router: INSUFFICIENT_OUTPUT_AMOUNT');
        TransferHelper.safeTransferFrom(
            path[0], msg.sender, UniswapV2Library.pairFor(factory, path[0], path[1]), amounts[0]
        );
        _swap(amounts, path, address(this));
        IWETH(WETH).withdraw(amounts[amounts.length - 1]);
        TransferHelper.safeTransferETH(to, amounts[amounts.length - 1]);
    }


    function swapETHForExactTokens(uint amountOut, address[] calldata path, address to, uint deadline)
        external
        virtual
        override
        payable
        ensure(deadline)
        returns (uint[] memory amounts)
    {
        require(path[0] == WETH, 'UniswapV2Router: INVALID_PATH');
        amounts = UniswapV2Library.getAmountsIn(factory, amountOut, path);
        require(amounts[0] <= msg.value, 'UniswapV2Router: EXCESSIVE_INPUT_AMOUNT');
        IWETH(WETH).deposit{value: amounts[0]}();
        assert(IWETH(WETH).transfer(UniswapV2Library.pairFor(factory, path[0], path[1]), amounts[0]));
        _swap(amounts, path, to);
        // डस्ट eth असल्यास, परतावा द्या
        if (msg.value > amounts[0]) TransferHelper.safeTransferETH(msg.sender, msg.value - amounts[0]);
    }
```

या चारही प्रकारांमध्ये ETH आणि टोकन्स यांच्यातील व्यापाराचा समावेश आहे. फरक एवढाच आहे की आपण एकतर व्यापाऱ्याकडून ETH प्राप्त करतो आणि WETH मिंट करण्यासाठी त्याचा वापर करतो, किंवा आपण मार्गातील शेवटच्या अदलाबदलीकडून WETH प्राप्त करतो आणि ते जाळतो, आणि परिणामी ETH व्यापाऱ्याला परत पाठवतो.

```solidity
    // **** अदलाबदल (फी-ऑन-हस्तांतरण टोकन्सना समर्थन देत आहे) ****
    // प्रारंभिक रक्कम पहिल्या जोडीला आधीच पाठवली असणे आवश्यक आहे
    function _swapSupportingFeeOnTransferTokens(address[] memory path, address _to) internal virtual {
```

([ही समस्या](https://github.com/Uniswap/uniswap-interface/issues/835)) सोडवण्यासाठी हस्तांतरण किंवा स्टोरेज शुल्क असलेल्या टोकन्सची अदलाबदल करण्यासाठी हे अंतर्गत फंक्शन आहे.

```solidity
        for (uint i; i < path.length - 1; i++) {
            (address input, address output) = (path[i], path[i + 1]);
            (address token0,) = UniswapV2Library.sortTokens(input, output);
            IUniswapV2Pair pair = IUniswapV2Pair(UniswapV2Library.pairFor(factory, input, output));
            uint amountInput;
            uint amountOutput;
            { // स्टॅक टू डीप (stack too deep) त्रुटी टाळण्यासाठी स्कोप
            (uint reserve0, uint reserve1,) = pair.getReserves();
            (uint reserveInput, uint reserveOutput) = input == token0 ? (reserve0, reserve1) : (reserve1, reserve0);
            amountInput = IERC20(input).balanceOf(address(pair)).sub(reserveInput);
            amountOutput = UniswapV2Library.getAmountOut(amountInput, reserveInput, reserveOutput);
```

हस्तांतरण शुल्कामुळे आपण प्रत्येक हस्तांतरणातून आपल्याला किती मिळते हे सांगण्यासाठी `getAmountsOut` फंक्शनवर अवलंबून राहू शकत नाही (जसे आपण मूळ `_swap` ला कॉल करण्यापूर्वी करतो). त्याऐवजी आपल्याला प्रथम हस्तांतरण करावे लागते आणि नंतर आपल्याला किती टोकन्स परत मिळाले ते पहावे लागते.

टीप: सैद्धांतिकदृष्ट्या आपण `_swap` ऐवजी फक्त हे फंक्शन वापरू शकतो, परंतु काही प्रकरणांमध्ये (उदाहरणार्थ, जर आवश्यक किमान रक्कम पूर्ण करण्यासाठी शेवटी पुरेशी रक्कम नसल्यामुळे हस्तांतरण पूर्ववत झाले) तर त्यासाठी अधिक गॅस खर्च होईल. ट्रान्सफर फी टोकन्स खूप दुर्मिळ आहेत, त्यामुळे आपल्याला त्यांना सामावून घेण्याची आवश्यकता असली तरी, सर्व अदलाबदलींनी ते किमान एका ट्रान्सफर फी टोकनमधून जातील असे गृहीत धरण्याची आवश्यकता नाही.

```solidity
            }
            (uint amount0Out, uint amount1Out) = input == token0 ? (uint(0), amountOutput) : (amountOutput, uint(0));
            address to = i < path.length - 2 ? UniswapV2Library.pairFor(factory, output, path[i + 2]) : _to;
            pair.swap(amount0Out, amount1Out, to, new bytes(0));
        }
    }


    function swapExactTokensForTokensSupportingFeeOnTransferTokens(
        uint amountIn,
        uint amountOutMin,
        address[] calldata path,
        address to,
        uint deadline
    ) external virtual override ensure(deadline) {
        TransferHelper.safeTransferFrom(
            path[0], msg.sender, UniswapV2Library.pairFor(factory, path[0], path[1]), amountIn
        );
        uint balanceBefore = IERC20(path[path.length - 1]).balanceOf(to);
        _swapSupportingFeeOnTransferTokens(path, to);
        require(
            IERC20(path[path.length - 1]).balanceOf(to).sub(balanceBefore) >= amountOutMin,
            'UniswapV2Router: INSUFFICIENT_OUTPUT_AMOUNT'
        );
    }


    function swapExactETHForTokensSupportingFeeOnTransferTokens(
        uint amountOutMin,
        address[] calldata path,
        address to,
        uint deadline
    )
        external
        virtual
        override
        payable
        ensure(deadline)
    {
        require(path[0] == WETH, 'UniswapV2Router: INVALID_PATH');
        uint amountIn = msg.value;
        IWETH(WETH).deposit{value: amountIn}();
        assert(IWETH(WETH).transfer(UniswapV2Library.pairFor(factory, path[0], path[1]), amountIn));
        uint balanceBefore = IERC20(path[path.length - 1]).balanceOf(to);
        _swapSupportingFeeOnTransferTokens(path, to);
        require(
            IERC20(path[path.length - 1]).balanceOf(to).sub(balanceBefore) >= amountOutMin,
            'UniswapV2Router: INSUFFICIENT_OUTPUT_AMOUNT'
        );
    }


    function swapExactTokensForETHSupportingFeeOnTransferTokens(
        uint amountIn,
        uint amountOutMin,
        address[] calldata path,
        address to,
        uint deadline
    )
        external
        virtual
        override
        ensure(deadline)
    {
        require(path[path.length - 1] == WETH, 'UniswapV2Router: INVALID_PATH');
        TransferHelper.safeTransferFrom(
            path[0], msg.sender, UniswapV2Library.pairFor(factory, path[0], path[1]), amountIn
        );
        _swapSupportingFeeOnTransferTokens(path, address(this));
        uint amountOut = IERC20(WETH).balanceOf(address(this));
        require(amountOut >= amountOutMin, 'UniswapV2Router: INSUFFICIENT_OUTPUT_AMOUNT');
        IWETH(WETH).withdraw(amountOut);
        TransferHelper.safeTransferETH(to, amountOut);
    }
```

हे सामान्य टोकन्ससाठी वापरले जाणारे तेच प्रकार आहेत, परंतु ते त्याऐवजी `_swapSupportingFeeOnTransferTokens` ला कॉल करतात.

```solidity
    // **** लायब्ररी फंक्शन्स ****
    function quote(uint amountA, uint reserveA, uint reserveB) public pure virtual override returns (uint amountB) {
        return UniswapV2Library.quote(amountA, reserveA, reserveB);
    }

    function getAmountOut(uint amountIn, uint reserveIn, uint reserveOut)
        public
        pure
        virtual
        override
        returns (uint amountOut)
    {
        return UniswapV2Library.getAmountOut(amountIn, reserveIn, reserveOut);
    }

    function getAmountIn(uint amountOut, uint reserveIn, uint reserveOut)
        public
        pure
        virtual
        override
        returns (uint amountIn)
    {
        return UniswapV2Library.getAmountIn(amountOut, reserveIn, reserveOut);
    }

    function getAmountsOut(uint amountIn, address[] memory path)
        public
        view
        virtual
        override
        returns (uint[] memory amounts)
    {
        return UniswapV2Library.getAmountsOut(factory, amountIn, path);
    }

    function getAmountsIn(uint amountOut, address[] memory path)
        public
        view
        virtual
        override
        returns (uint[] memory amounts)
    {
        return UniswapV2Library.getAmountsIn(factory, amountOut, path);
    }
}
```

ही फंक्शन्स फक्त प्रॉक्सी आहेत जी [UniswapV2Library फंक्शन्सना](#uniswapv2library) कॉल करतात.

### UniswapV2Migrator.sol {#uniswapv2migrator}

या कॉन्ट्रॅक्टचा वापर जुन्या v1 वरून v2 मध्ये अदलाबदली स्थलांतरित करण्यासाठी केला गेला होता. आता ते स्थलांतरित झाले असल्याने, ते यापुढे संबंधित नाही.

## लायब्ररी {#libraries}

[SafeMath लायब्ररी](https://docs.openzeppelin.com/contracts/2.x/api/math) चांगल्या प्रकारे दस्तऐवजीकरण केलेली आहे, त्यामुळे येथे तिचे दस्तऐवजीकरण करण्याची आवश्यकता नाही.

### Math {#math}

या लायब्ररीमध्ये काही गणितीय फंक्शन्स आहेत ज्यांची सामान्यतः Solidity कोडमध्ये आवश्यकता नसते, त्यामुळे ते या भाषेचा भाग नाहीत.

```solidity
pragma solidity =0.5.16;

// विविध गणितीय ऑपरेशन्स करण्यासाठी एक लायब्ररी

library Math {
    function min(uint x, uint y) internal pure returns (uint z) {
        z = x < y ? x : y;
    }

    // बॅबिलोनियन पद्धत (https://wikipedia.org/wiki/Methods_of_computing_square_roots#Babylonian_method)
    function sqrt(uint y) internal pure returns (uint z) {
        if (y > 3) {
            z = y;
            uint x = y / 2 + 1;
```

x ने असा अंदाज म्हणून सुरुवात करा जो वर्गमुळापेक्षा जास्त असेल (याच कारणामुळे आपल्याला 1-3 ला विशेष प्रकरणे म्हणून हाताळण्याची आवश्यकता आहे).

```solidity
            while (x < z) {
                z = x;
                x = (y / x + x) / 2;
```

अधिक जवळचा अंदाज मिळवा, मागील अंदाज आणि ज्या संख्येचे वर्गमूळ आपण शोधण्याचा प्रयत्न करत आहोत त्या संख्येला मागील अंदाजाने भागून मिळणारी सरासरी. जोपर्यंत नवीन अंदाज विद्यमान अंदाजापेक्षा कमी होत नाही तोपर्यंत पुनरावृत्ती करा. अधिक तपशीलांसाठी, [येथे पहा](https://wikipedia.org/wiki/Methods_of_computing_square_roots#Babylonian_method).

```solidity
            }
        } else if (y != 0) {
            z = 1;
```

आपल्याला शून्याच्या वर्गमुळाची कधीही आवश्यकता भासू नये. एक, दोन आणि तीनचे वर्गमूळ साधारणपणे एक असते (आपण पूर्णांक वापरतो, त्यामुळे आपण अपूर्णांकाकडे दुर्लक्ष करतो).

```solidity
        }
    }
}
```

### फिक्स्ड पॉइंट फ्रॅक्शन्स (UQ112x112) {#fixedpoint}

ही लायब्ररी अपूर्णांक हाताळते, जे सामान्यतः इथेरियम अंकगणिताचा भाग नसतात. ती _x_ या संख्येला _x\*2^112_ म्हणून एन्कोड करून हे करते. यामुळे आपल्याला मूळ बेरीज आणि वजाबाकी ऑपकोड्स कोणत्याही बदलाशिवाय वापरता येतात.

```solidity
pragma solidity =0.5.16;

// बायनरी फिक्स्ड पॉईंट नंबर्स हाताळण्यासाठी एक लायब्ररी (https://wikipedia.org/wiki/Q_(number_format))

// श्रेणी: [0, 2**112 - 1]
// रिझोल्यूशन: 1 / 2**112

library UQ112x112 {
    uint224 constant Q112 = 2**112;
```

`Q112` हे एकासाठी एन्कोडिंग आहे.

```solidity
    // uint112 ला UQ112x112 म्हणून एन्कोड करा
    function encode(uint112 y) internal pure returns (uint224 z) {
        z = uint224(y) * Q112; // कधीही ओव्हरफ्लो होत नाही
    }
```

कारण y हे `uint112` आहे, ते जास्तीत जास्त 2^112-1 असू शकते. ती संख्या अजूनही `UQ112x112` म्हणून एन्कोड केली जाऊ शकते.

```solidity
    // UQ112x112 ला uint112 ने भागा, UQ112x112 परत करते
    function uqdiv(uint224 x, uint112 y) internal pure returns (uint224 z) {
        z = x / uint224(y);
    }
}
```

जर आपण दोन `UQ112x112` मूल्यांना भागले, तर परिणामाला यापुढे 2^112 ने गुणले जात नाही. त्यामुळे त्याऐवजी आपण छेदासाठी एक पूर्णांक घेतो. गुणाकार करण्यासाठी आपल्याला अशीच युक्ती वापरावी लागली असती, परंतु आपल्याला `UQ112x112` मूल्यांचा गुणाकार करण्याची आवश्यकता नाही.

### UniswapV2Library {#uniswapv2library}

ही लायब्ररी केवळ पेरिफेरी (periphery) कॉन्ट्रॅक्ट्सद्वारे वापरली जाते

```solidity
pragma solidity >=0.5.0;

import '@uniswap/v2-core/contracts/interfaces/IUniswapV2Pair.sol';

import "./SafeMath.sol";

library UniswapV2Library {
    using SafeMath for uint;

    // क्रमवारी लावलेले टोकन पत्ते परत करते, या क्रमाने क्रमवारी लावलेल्या जोड्यांमधून रिटर्न व्हॅल्यूज हाताळण्यासाठी वापरले जाते
    function sortTokens(address tokenA, address tokenB) internal pure returns (address token0, address token1) {
        require(tokenA != tokenB, 'UniswapV2Library: IDENTICAL_ADDRESSES');
        (token0, token1) = tokenA < tokenB ? (tokenA, tokenB) : (tokenB, tokenA);
        require(token0 != address(0), 'UniswapV2Library: ZERO_ADDRESS');
    }
```

दोन टोकन्स त्यांच्या पत्त्यानुसार क्रमवारी लावा, जेणेकरून आपल्याला त्यांच्यासाठी पेअर एक्सचेंजचा पत्ता मिळवता येईल. हे आवश्यक आहे कारण अन्यथा आपल्याकडे दोन शक्यता असतील, एक A,B पॅरामीटर्ससाठी आणि दुसरी B,A पॅरामीटर्ससाठी, ज्यामुळे एकाऐवजी दोन एक्सचेंजेस तयार होतील.

```solidity
    // कोणतेही बाह्य कॉल्स न करता जोडीसाठी CREATE2 पत्ता मोजते
    function pairFor(address factory, address tokenA, address tokenB) internal pure returns (address pair) {
        (address token0, address token1) = sortTokens(tokenA, tokenB);
        pair = address(uint(keccak256(abi.encodePacked(
                hex'ff',
                factory,
                keccak256(abi.encodePacked(token0, token1)),
                hex'96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f' // init कोड हॅश
            ))));
    }
```

हे फंक्शन दोन टोकन्ससाठी पेअर एक्सचेंजच्या पत्त्याची गणना करते. हे कॉन्ट्रॅक्ट [CREATE2 ऑपकोड](https://eips.ethereum.org/EIPS/eip-1014) वापरून तयार केले आहे, त्यामुळे जर आपल्याला ते वापरत असलेले पॅरामीटर्स माहित असतील तर आपण त्याच अल्गोरिदमचा वापर करून पत्त्याची गणना करू शकतो. फॅक्टरीला विचारण्यापेक्षा हे खूप स्वस्त आहे, आणि

```solidity
    // जोडीसाठी रिझर्व्ह मिळवते आणि क्रमवारी लावते
    function getReserves(address factory, address tokenA, address tokenB) internal view returns (uint reserveA, uint reserveB) {
        (address token0,) = sortTokens(tokenA, tokenB);
        (uint reserve0, uint reserve1,) = IUniswapV2Pair(pairFor(factory, tokenA, tokenB)).getReserves();
        (reserveA, reserveB) = tokenA == token0 ? (reserve0, reserve1) : (reserve1, reserve0);
    }
```

हे फंक्शन पेअर एक्सचेंजकडे असलेल्या दोन टोकन्सचे रिझर्व्ह परत करते. लक्षात घ्या की ते कोणत्याही क्रमाने टोकन्स प्राप्त करू शकते आणि अंतर्गत वापरासाठी त्यांची क्रमवारी लावते.

```solidity
    // मालमत्तेची काही रक्कम आणि जोडीचे रिझर्व्ह दिले असता, दुसऱ्या मालमत्तेची समतुल्य रक्कम परत करते
    function quote(uint amountA, uint reserveA, uint reserveB) internal pure returns (uint amountB) {
        require(amountA > 0, 'UniswapV2Library: INSUFFICIENT_AMOUNT');
        require(reserveA > 0 && reserveB > 0, 'UniswapV2Library: INSUFFICIENT_LIQUIDITY');
        amountB = amountA.mul(reserveB) / reserveA;
    }
```

जर कोणतेही शुल्क समाविष्ट नसेल तर हे फंक्शन तुम्हाला टोकन A च्या बदल्यात मिळणाऱ्या टोकन B ची रक्कम देते. ही गणना हे लक्षात घेते की हस्तांतरणामुळे विनिमय दर बदलतो.

```solidity
    // मालमत्तेची इनपुट रक्कम आणि जोडीचे रिझर्व्ह दिले असता, दुसऱ्या मालमत्तेची कमाल आउटपुट रक्कम परत करते
    function getAmountOut(uint amountIn, uint reserveIn, uint reserveOut) internal pure returns (uint amountOut) {
```

जर पेअर एक्सचेंज वापरण्यासाठी कोणतेही शुल्क नसेल तर वरील `quote` फंक्शन उत्तम काम करते. तथापि, जर 0.3% एक्सचेंज शुल्क असेल तर तुम्हाला प्रत्यक्षात मिळणारी रक्कम कमी असते. हे फंक्शन एक्सचेंज शुल्कानंतरच्या रकमेची गणना करते.

```solidity

        require(amountIn > 0, 'UniswapV2Library: INSUFFICIENT_INPUT_AMOUNT');
        require(reserveIn > 0 && reserveOut > 0, 'UniswapV2Library: INSUFFICIENT_LIQUIDITY');
        uint amountInWithFee = amountIn.mul(997);
        uint numerator = amountInWithFee.mul(reserveOut);
        uint denominator = reserveIn.mul(1000).add(amountInWithFee);
        amountOut = numerator / denominator;
    }
```

Solidity मूळतः अपूर्णांक हाताळत नाही, त्यामुळे आपण रकमेला फक्त 0.997 ने गुणू शकत नाही. त्याऐवजी, आपण अंशाला 997 ने आणि छेदाला 1000 ने गुणतो, ज्यामुळे तोच परिणाम साध्य होतो.

```solidity
    // मालमत्तेची आउटपुट रक्कम आणि जोडीचे रिझर्व्ह दिले असता, दुसऱ्या मालमत्तेची आवश्यक इनपुट रक्कम परत करते
    function getAmountIn(uint amountOut, uint reserveIn, uint reserveOut) internal pure returns (uint amountIn) {
        require(amountOut > 0, 'UniswapV2Library: INSUFFICIENT_OUTPUT_AMOUNT');
        require(reserveIn > 0 && reserveOut > 0, 'UniswapV2Library: INSUFFICIENT_LIQUIDITY');
        uint numerator = reserveIn.mul(amountOut).mul(1000);
        uint denominator = reserveOut.sub(amountOut).mul(997);
        amountIn = (numerator / denominator).add(1);
    }
```

हे फंक्शन साधारणपणे तेच काम करते, परंतु ते आउटपुट रक्कम मिळवते आणि इनपुट प्रदान करते.

```solidity

    // कोणत्याही संख्येच्या जोड्यांवर साखळीबद्ध getAmountOut गणना करते
    function getAmountsOut(address factory, uint amountIn, address[] memory path) internal view returns (uint[] memory amounts) {
        require(path.length >= 2, 'UniswapV2Library: INVALID_PATH');
        amounts = new uint[](path.length);
        amounts[0] = amountIn;
        for (uint i; i < path.length - 1; i++) {
            (uint reserveIn, uint reserveOut) = getReserves(factory, path[i], path[i + 1]);
            amounts[i + 1] = getAmountOut(amounts[i], reserveIn, reserveOut);
        }
    }

    // कोणत्याही संख्येच्या जोड्यांवर साखळीबद्ध getAmountIn गणना करते
    function getAmountsIn(address factory, uint amountOut, address[] memory path) internal view returns (uint[] memory amounts) {
        require(path.length >= 2, 'UniswapV2Library: INVALID_PATH');
        amounts = new uint[](path.length);
        amounts[amounts.length - 1] = amountOut;
        for (uint i = path.length - 1; i > 0; i--) {
            (uint reserveIn, uint reserveOut) = getReserves(factory, path[i - 1], path[i]);
            amounts[i - 1] = getAmountIn(amounts[i], reserveIn, reserveOut);
        }
    }
}
```

जेव्हा अनेक पेअर एक्सचेंजेसमधून जाणे आवश्यक असते तेव्हा ही दोन फंक्शन्स मूल्ये ओळखण्याचे काम हाताळतात.

### ट्रान्सफर हेल्पर {#transfer-helper}

[ही लायब्ररी](https://github.com/Uniswap/uniswap-lib/blob/master/contracts/libraries/TransferHelper.sol) ERC-20 आणि इथेरियम हस्तांतरणाभोवती यश तपासणी जोडते जेणेकरून पूर्ववत करणे आणि `false` मूल्य परत येणे या दोन्ही गोष्टींना एकाच प्रकारे हाताळता येईल.

```solidity
// SPDX-License-Identifier: GPL-3.0-or-later

pragma solidity >=0.6.0;

// ERC-20 टोकन्सशी संवाद साधण्यासाठी आणि ETH पाठवण्यासाठी हेल्पर पद्धती ज्या सातत्याने true/false परत करत नाहीत
library TransferHelper {
    function safeApprove(
        address token,
        address to,
        uint256 value
    ) internal {
        // bytes4(keccak256(bytes('approve(address,uint256)')));
        (bool success, bytes memory data) = token.call(abi.encodeWithSelector(0x095ea7b3, to, value));

```

आपण दोनपैकी एका मार्गाने वेगळ्या कॉन्ट्रॅक्टला कॉल करू शकतो:

- फंक्शन कॉल तयार करण्यासाठी इंटरफेस व्याख्येचा वापर करा
- कॉल तयार करण्यासाठी "मॅन्युअली" [अॅप्लिकेशन बायनरी इंटरफेस (ABI)](https://docs.soliditylang.org/en/v0.8.3/abi-spec.html) वापरा. कोडच्या लेखकाने हेच करण्याचे ठरवले आहे.

```solidity
        require(
            success && (data.length == 0 || abi.decode(data, (bool))),
            'TransferHelper::safeApprove: approve failed'
        );
    }
```

ERC-20 मानकापूर्वी तयार केलेल्या टोकनसह बॅकवर्ड कंपॅटिबिलिटीसाठी, ERC-20 कॉल एकतर पूर्ववत करून (ज्या प्रकरणात `success` हे `false` असते) किंवा यशस्वी होऊन आणि `false` मूल्य परत करून (ज्या प्रकरणात आउटपुट डेटा असतो, आणि जर तुम्ही त्याला बुलियन म्हणून डीकोड केले तर तुम्हाला `false` मिळते) अयशस्वी होऊ शकतो.

```solidity


    function safeTransfer(
        address token,
        address to,
        uint256 value
    ) internal {
        // bytes4(keccak256(bytes('transfer(address,uint256)')));
        (bool success, bytes memory data) = token.call(abi.encodeWithSelector(0xa9059cbb, to, value));
        require(
            success && (data.length == 0 || abi.decode(data, (bool))),
            'TransferHelper::safeTransfer: transfer failed'
        );
    }
```

हे फंक्शन [ERC-20 ची हस्तांतरण कार्यक्षमता](https://eips.ethereum.org/EIPS/eip-20#transfer) लागू करते, जे एका खात्याला वेगळ्या खात्याने प्रदान केलेली मंजुरी खर्च करण्याची परवानगी देते.

```solidity

    function safeTransferFrom(
        address token,
        address from,
        address to,
        uint256 value
    ) internal {
        // bytes4(keccak256(bytes('transferFrom(address,address,uint256)')));
        (bool success, bytes memory data) = token.call(abi.encodeWithSelector(0x23b872dd, from, to, value));
        require(
            success && (data.length == 0 || abi.decode(data, (bool))),
            'TransferHelper::transferFrom: transferFrom failed'
        );
    }
```

हे फंक्शन [ERC-20 ची transferFrom कार्यक्षमता](https://eips.ethereum.org/EIPS/eip-20#transferfrom) लागू करते, जे एका खात्याला वेगळ्या खात्याने प्रदान केलेली मंजुरी खर्च करण्याची परवानगी देते.

```solidity

    function safeTransferETH(address to, uint256 value) internal {
        (bool success, ) = to.call{value: value}(new bytes(0));
        require(success, 'TransferHelper::safeTransferETH: ETH transfer failed');
    }
}
```

हे फंक्शन खात्यात इथर हस्तांतरित करते. वेगळ्या कॉन्ट्रॅक्टला केलेला कोणताही कॉल इथर पाठवण्याचा प्रयत्न करू शकतो. कारण आपल्याला प्रत्यक्षात कोणत्याही फंक्शनला कॉल करण्याची आवश्यकता नाही, आपण कॉलसोबत कोणताही डेटा पाठवत नाही.

## निष्कर्ष {#conclusion}

हा सुमारे 50 पानांचा एक मोठा लेख आहे. जर तुम्ही इथपर्यंत पोहोचला असाल, तर तुमचे अभिनंदन! आशा आहे की आतापर्यंत तुम्हाला वास्तविक जीवनातील अॅप्लिकेशन (लहान नमुना प्रोग्राम्सच्या तुलनेत) लिहिताना कोणत्या गोष्टींचा विचार करावा लागतो हे समजले असेल आणि तुम्ही तुमच्या स्वतःच्या वापरासाठी कॉन्ट्रॅक्ट्स अधिक चांगल्या प्रकारे लिहू शकाल.

आता जा आणि काहीतरी उपयुक्त लिहा आणि आम्हाला थक्क करा.

[माझ्या अधिक कामासाठी येथे पहा](https://cryptodocguy.pro/).