---
title: "यूनिस्वैप-v2 अनुबंध वॉक-थ्रू"
description: "यूनिस्वैप-v2 अनुबंध कैसे काम करता है? इसे इस तरह क्यों लिखा गया है?"
author: "ओरी पोमेरेंट्ज़"
tags:
  - solidity
  - डैप्स
skill: intermediate
breadcrumb: "यूनिस्वैप v2 वॉकथ्रू"
published: 2021-05-01
lang: hi
---
## परिचय {#introduction}

[यूनिस्वैप v2](https://app.uniswap.org/whitepaper.pdf) किन्हीं भी दो ERC-20 टोकनों के बीच एक एक्सचेंज मार्केट बना सकता है। इस लेख में हम उन अनुबंधों के स्रोत कोड (source code) पर नज़र डालेंगे जो इस प्रोटोकॉल को लागू करते हैं और देखेंगे कि उन्हें इस तरह क्यों लिखा गया है।

### यूनिस्वैप क्या करता है? {#what-does-uniswap-do}

मूल रूप से, दो प्रकार के उपयोगकर्ता होते हैं: तरलता प्रदाता और ट्रेडर।

_तरलता प्रदाता_ पूल को दो टोकन प्रदान करते हैं जिनका विनिमय (exchange) किया जा सकता है (हम उन्हें **Token0** और **Token1** कहेंगे)। इसके बदले में, उन्हें एक तीसरा टोकन मिलता है जो पूल के आंशिक स्वामित्व को दर्शाता है, जिसे _तरलता टोकन_ कहा जाता है।

_ट्रेडर_ पूल में एक प्रकार का टोकन भेजते हैं और तरलता प्रदाताओं द्वारा प्रदान किए गए पूल से दूसरा टोकन प्राप्त करते हैं (उदाहरण के लिए, **Token0** भेजें और **Token1** प्राप्त करें)। विनिमय दर (exchange rate) पूल में मौजूद **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 पर जाना आसान है।

### कोर अनुबंध बनाम परिधि (Periphery) अनुबंध {#contract-types}

यूनिस्वैप v2 को दो घटकों में विभाजित किया गया है, एक कोर (core) और एक परिधि (periphery)। यह विभाजन कोर अनुबंधों को, जो संपत्ति रखते हैं और इसलिए उनका सुरक्षित होना _आवश्यक_ है, सरल और ऑडिट करने में आसान बनाता है। ट्रेडरों द्वारा आवश्यक सभी अतिरिक्त कार्यक्षमता फिर परिधि अनुबंधों द्वारा प्रदान की जा सकती है।

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

यह डेटा और नियंत्रण का प्रवाह है जो तब होता है जब आप यूनिस्वैप के तीन मुख्य कार्य करते हैं:

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;
```

पूल अनुबंध में कई गणनाओं के लिए भिन्नों (fractions) की आवश्यकता होती है। हालाँकि, EVM द्वारा भिन्नों का समर्थन नहीं किया जाता है।
यूनिस्वैप ने जो समाधान खोजा है वह 224 बिट मानों का उपयोग करना है, जिसमें पूर्णांक भाग के लिए 112 बिट्स और भिन्न के लिए 112 बिट्स हैं। इसलिए `1.0` को `2^112` के रूप में दर्शाया गया है, `1.5` को `2^112 + 2^111` के रूप में दर्शाया गया है, आदि।

इस लाइब्रेरी के बारे में अधिक विवरण [दस्तावेज़ में आगे](#fixedpoint) उपलब्ध हैं।

#### चर (Variables) {#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;
```

सुरक्षा कमजोरियों का एक वर्ग है जो [पुन:प्रवेश (reentrancy) के दुरुपयोग](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) {
```

यदि बीता हुआ समय शून्य नहीं है, तो इसका मतलब है कि हम इस ब्लॉक पर पहला एक्सचेंज लेन-देन हैं। उस स्थिति में, हमें लागत संचायकों (cost accumulators) को अपडेट करने की आवश्यकता है।

```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}

ध्यान दें कि हालांकि कोई भी लेन-देन या अनुबंध इन फ़ंक्शनों को कॉल _कर सकता है_, उन्हें परिधि (periphery) अनुबंध से कॉल किए जाने के लिए डिज़ाइन किया गया है। यदि आप उन्हें सीधे कॉल करते हैं तो आप पेयर एक्सचेंज को धोखा नहीं दे पाएंगे, लेकिन आप किसी गलती के कारण मूल्य खो सकते हैं।

##### mint

```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 है। यह कोई उच्च लागत नहीं है।

पहली जमा राशि के समय हम दोनों टोकनों का सापेक्ष मूल्य नहीं जानते हैं, इसलिए हम केवल राशियों को गुणा करते हैं और एक वर्गमूल लेते हैं, यह मानते हुए कि जमा राशि हमें दोनों टोकनों में समान मूल्य प्रदान करती है।

हम इस पर भरोसा कर सकते हैं क्योंकि समान मूल्य प्रदान करना जमाकर्ता के हित में है, ताकि मध्यस्थता (arbitrage) में मूल्य खोने से बचा जा सके।
मान लें कि दोनों टोकनों का मूल्य समान है, लेकिन हमारे जमाकर्ता ने **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 |
| मध्यस्थता (arbitrage) के बाद           |  ~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`) को अपडेट करें और उचित घटना उत्सर्जित करें।

##### burn

```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` फ़ंक्शन की दर्पण छवि (mirror image) है।

##### swap

```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} के लिए स्कोप, स्टैक टू डीप त्रुटियों से बचाता है
```

स्थानीय चरों को या तो मेमोरी में संग्रहीत किया जा सकता है या, यदि वे बहुत अधिक नहीं हैं, तो सीधे स्टैक पर।
यदि हम संख्या को सीमित कर सकते हैं ताकि हम स्टैक का उपयोग करें तो हम कम गैस का उपयोग करते हैं। अधिक विवरण के लिए [येलो पेपर, औपचारिक इथेरियम विनिर्देश](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); // आशावादी रूप से टोकन ट्रांसफर करें
```

यह ट्रांसफर आशावादी (optimistic) है, क्योंकि हम यह सुनिश्चित होने से पहले ट्रांसफर करते हैं कि सभी शर्तें पूरी हो गई हैं। इथेरियम में यह ठीक है क्योंकि यदि कॉल में बाद में शर्तें पूरी नहीं होती हैं तो हम इसे रिवर्ट कर देते हैं और इसके द्वारा बनाए गए किसी भी परिवर्तन को रद्द कर देते हैं।

```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 के लिए स्कोप, स्टैक टू डीप त्रुटियों से बचाता है
            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');
```

यह सुनिश्चित करने के लिए एक विवेक जाँच (sanity check) है कि हम स्वैप से नुकसान न उठाएँ। ऐसी कोई परिस्थिति नहीं है जिसमें स्वैप को `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 टोकनों की पहचान उन अनुबंधों के पतों से की जाती है जो उन्हें लागू करते हैं, इसलिए कुंजियाँ (keys) और मान सभी पते हैं। उस पेयर एक्सचेंज का पता प्राप्त करने के लिए जो आपको `tokenA` से `tokenB` में बदलने देता है, आप `getPair[<tokenA address>][<tokenB address>]` का उपयोग करते हैं (या इसके विपरीत)।

दूसरा चर, `allPairs`, एक ऐरे है जिसमें इस फ़ैक्टरी द्वारा बनाए गए पेयर एक्सचेंजों के सभी पते शामिल हैं। इथेरियम में आप किसी मैपिंग की सामग्री पर पुनरावृति (iterate) नहीं कर सकते हैं, या सभी कुंजियों की सूची प्राप्त नहीं कर सकते हैं, इसलिए यह चर यह जानने का एकमात्र तरीका है कि यह फ़ैक्टरी किन एक्सचेंजों का प्रबंधन करती है।

नोट: आप किसी मैपिंग की सभी कुंजियों पर पुनरावृति क्यों नहीं कर सकते, इसका कारण यह है कि अनुबंध डेटा स्टोरेज _महंगा_ है, इसलिए हम इसका जितना कम उपयोग करेंगे उतना अच्छा है, और हम इसे जितनी कम बार बदलेंगे उतना अच्छा है। आप [ऐसी मैपिंग बना सकते हैं जो पुनरावृत्ति का समर्थन करती हैं](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);
```

हम चाहते हैं कि नए एक्सचेंज का पता नियतात्मक (deterministic) हो, ताकि इसकी गणना पहले से ही ऑफचेन की जा सके (यह [लेयर 2 (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) के लिए तीन अदिश (scalar) मानों को मापदंडों के रूप में प्राप्त करता है।

```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 स्वीकृति (approve)](https://eips.ethereum.org/EIPS/eip-20#approve) के रूप में मानें।

## परिधि अनुबंध {#periphery-contracts}

परिधि अनुबंध यूनिस्वैप (Uniswap) के लिए API (एप्लिकेशन प्रोग्राम इंटरफ़ेस) हैं। वे अन्य अनुबंधों या विकेंद्रीकृत एप्लिकेशन (dapps) से बाहरी कॉल के लिए उपलब्ध हैं। आप कोर अनुबंधों को सीधे कॉल कर सकते हैं, लेकिन यह अधिक जटिल है और यदि आप कोई गलती करते हैं तो आप मूल्य खो सकते हैं। कोर अनुबंधों में केवल यह सुनिश्चित करने के लिए परीक्षण होते हैं कि उनके साथ धोखा न हो, किसी और के लिए कोई विवेकपूर्ण जाँच (sanity checks) नहीं होती है। वे परिधि में होते हैं ताकि आवश्यकतानुसार उन्हें अपडेट किया जा सके।

### 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)। सौभाग्य से, परिधि अनुबंध स्थिति-रहित (stateless) होते हैं और कोई संपत्ति नहीं रखते हैं, इसलिए इसे हटाना और लोगों को इसके बजाय प्रतिस्थापन, `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
```

ये जमा करने के लिए न्यूनतम स्वीकार्य राशियाँ हैं। यदि लेन-देन इन राशियों या उससे अधिक के साथ नहीं हो सकता है, तो इसे रिवर्ट कर दें। यदि आप यह सुविधा नहीं चाहते हैं, तो बस शून्य निर्दिष्ट करें।

तरलता प्रदाता आमतौर पर एक न्यूनतम निर्दिष्ट करते हैं, क्योंकि वे लेन-देन को उस विनिमय दर तक सीमित करना चाहते हैं जो वर्तमान दर के करीब हो। यदि विनिमय दर में बहुत अधिक उतार-चढ़ाव होता है, तो इसका मतलब ऐसी खबर हो सकती है जो अंतर्निहित मूल्यों को बदल देती है, और वे मैन्युअल रूप से यह तय करना चाहते हैं कि क्या करना है।

उदाहरण के लिए, एक ऐसे मामले की कल्पना करें जहाँ विनिमय दर एक-से-एक है और तरलता प्रदाता इन मानों को निर्दिष्ट करता है:

| Parameter      | Value |
| -------------- | ----: |
| 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);
        // डस्ट ईथर वापस करें, यदि कोई हो
        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)। इसके बजाय, स्वैप फ़ंक्शन एक पथ (path) की अवधारणा का समर्थन करते हैं। एक व्यापारी A को B के लिए, B को C के लिए, और C को D के लिए विनिमय कर सकता है, इसलिए प्रत्यक्ष A-D पेयर एक्सचेंज की कोई आवश्यकता नहीं है।

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

1. प्रारंभिक स्थिति
2. एक व्यापारी 24.695 A टोकन बेचता है और 25.305 B टोकन प्राप्त करता है।
3. व्यापारी 25.305 C टोकन के लिए 24.695 B टोकन बेचता है, और लगभग 0.61 B टोकन लाभ के रूप में रखता है।
4. फिर व्यापारी 25.305 A टोकन के लिए 24.695 C टोकन बेचता है, और लगभग 0.61 C टोकन लाभ के रूप में रखता है। व्यापारी के पास 0.61 अतिरिक्त A टोकन भी हैं (25.305 जिसके साथ व्यापारी समाप्त होता है, माइनस 24.695 का मूल निवेश)।

| चरण | A-B Exchange                | B-C Exchange                | A-C Exchange                |
| ---- | --------------------------- | --------------------------- | --------------------------- |
| 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 अनुबंधों के पते होते हैं। जैसा कि ऊपर बताया गया है, यह एक ऐरे (array) है क्योंकि आपके पास जो संपत्ति है उससे अपनी मनचाही संपत्ति प्राप्त करने के लिए आपको कई पेयर एक्सचेंजों से गुज़रना पड़ सकता है।

Solidity में एक फ़ंक्शन पैरामीटर को `memory` या `calldata` में संग्रहीत किया जा सकता है। यदि फ़ंक्शन अनुबंध का एक प्रवेश बिंदु (entry point) है, जिसे सीधे उपयोगकर्ता (लेन-देन का उपयोग करके) या किसी भिन्न अनुबंध से कॉल किया जाता है, तो पैरामीटर का मान सीधे कॉल डेटा से लिया जा सकता है। यदि फ़ंक्शन को आंतरिक रूप से कॉल किया जाता है, जैसा कि ऊपर `_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`, एक व्यापारी को इनपुट टोकनों की सटीक संख्या निर्दिष्ट करने की अनुमति देता है जो वह देने को तैयार है और आउटपुट टोकनों की न्यूनतम संख्या जो वह बदले में प्राप्त करने को तैयार है। यह फ़ंक्शन रिवर्स स्वैप करता है, यह एक व्यापारी को उन आउटपुट टोकनों की संख्या निर्दिष्ट करने देता है जो वह चाहता है, और इनपुट टोकनों की अधिकतम संख्या जो वह उनके लिए भुगतान करने को तैयार है।

दोनों ही मामलों में, व्यापारी को पहले इस परिधि अनुबंध को एक व्यय सीमा (allowance) देनी होगी ताकि वह उन्हें ट्रांसफर कर सके।

```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);
        // डस्ट ईथर वापस करें, यदि कोई हो
        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;
            { // स्टैक टू डीप त्रुटियों से बचने के लिए स्कोप
            (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` एक (1) के लिए एन्कोडिंग है।

```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' // इनिट कोड हैश
            ))));
    }
```

यह फ़ंक्शन दो टोकनों के लिए पेयर एक्सचेंज के पते की गणना करता है। यह अनुबंध [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;
    }
```

यह फ़ंक्शन आपको टोकन B की वह मात्रा देता है जो आपको टोकन A के बदले में मिलेगी यदि इसमें कोई शुल्क शामिल नहीं है। यह गणना इस बात को ध्यान में रखती है कि ट्रांसफर विनिमय दर को बदल देता है।

```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/)।