---
title: "ইউনিসোয়াপ-v2 কন্ট্রাক্ট ওয়াক-থ্রু"
description: "ইউনিসোয়াপ-v2 কন্ট্রাক্ট কীভাবে কাজ করে? এটি কেন সেভাবে লেখা হয়েছে?"
author: "ওরি পোমেরান্টজ"
tags:
  - solidity
  - "ড্যাপস"
skill: intermediate
breadcrumb: "ইউনিসোয়াপ v2 ওয়াক-থ্রু"
published: 2021-05-01
lang: bn
---
## ভূমিকা {#introduction}

[ইউনিসোয়াপ v2](https://app.uniswap.org/whitepaper.pdf) যেকোনো দুটি ERC-20 টোকেনের মধ্যে একটি এক্সচেঞ্জ মার্কেট তৈরি করতে পারে। এই নিবন্ধে আমরা এই প্রোটোকল বাস্তবায়নকারী কন্ট্রাক্টগুলোর সোর্স কোড নিয়ে আলোচনা করব এবং দেখব কেন সেগুলো এভাবে লেখা হয়েছে।

### ইউনিসোয়াপ কী করে? {#what-does-uniswap-do}

মূলত, দুই ধরনের ব্যবহারকারী রয়েছে: তারল্য প্রদানকারী এবং ট্রেডার।

_তারল্য প্রদানকারী_ পুলটিতে এমন দুটি টোকেন প্রদান করে যা এক্সচেঞ্জ করা যায় (আমরা সেগুলোকে **Token0** এবং **Token1** বলব)। এর বিনিময়ে, তারা তৃতীয় একটি টোকেন পায় যা পুলের আংশিক মালিকানার প্রতিনিধিত্ব করে, যাকে _তারল্য টোকেন_ বলা হয়।

_ট্রেডাররা_ পুলে এক ধরনের টোকেন পাঠায় এবং অন্যটি গ্রহণ করে (উদাহরণস্বরূপ, **Token0** পাঠায় এবং **Token1** গ্রহণ করে) যা তারল্য প্রদানকারীদের দ্বারা সরবরাহকৃত পুল থেকে আসে। এক্সচেঞ্জ রেট নির্ধারিত হয় পুলে থাকা **Token0** এবং **Token1**-এর আপেক্ষিক সংখ্যার ওপর ভিত্তি করে। এছাড়া, পুলটি তারল্য পুলের জন্য পুরস্কার হিসেবে একটি ছোট শতাংশ কেটে নেয়।

যখন তারল্য প্রদানকারীরা তাদের সম্পদ ফেরত পেতে চায়, তখন তারা পুল টোকেনগুলো পোড়াতে পারে এবং তাদের টোকেনগুলো ফেরত পেতে পারে, যার মধ্যে তাদের পুরস্কারের অংশও অন্তর্ভুক্ত থাকে।

[আরও বিস্তারিত বিবরণের জন্য এখানে ক্লিক করুন](https://docs.uniswap.org/contracts/v2/concepts/core-concepts/swaps/)।

### কেন v2? কেন v3 নয়? {#why-v2}

[ইউনিসোয়াপ v3](https://app.uniswap.org/whitepaper-v3.pdf) হলো এমন একটি আপগ্রেড যা v2-এর চেয়ে অনেক বেশি জটিল। প্রথমে v2 শেখা এবং তারপর v3-তে যাওয়া বেশি সহজ।

### কোর কন্ট্রাক্ট বনাম পেরিফেরি কন্ট্রাক্ট {#contract-types}

ইউনিসোয়াপ v2 দুটি উপাদানে বিভক্ত, একটি কোর এবং একটি পেরিফেরি। এই বিভাজনের ফলে কোর কন্ট্রাক্টগুলো, যা সম্পদ ধারণ করে এবং তাই সেগুলোকে নিরাপদ _হতেই_ হয়, আরও সহজ এবং অডিট করার জন্য সুবিধাজনক হয়। ট্রেডারদের প্রয়োজনীয় সমস্ত অতিরিক্ত কার্যকারিতা এরপর পেরিফেরি কন্ট্রাক্টগুলোর মাধ্যমে প্রদান করা যেতে পারে।

## ডেটা এবং কন্ট্রোল ফ্লো {#flows}

ইউনিসোয়াপ-এর তিনটি প্রধান কাজ করার সময় ডেটা এবং কন্ট্রোলের যে ফ্লো বা প্রবাহ ঘটে তা নিচে দেওয়া হলো:

1. বিভিন্ন টোকেনের মধ্যে সোয়াপ করা
2. মার্কেটে তারল্য যোগ করা এবং পেয়ার এক্সচেঞ্জ ERC-20 তারল্য টোকেন দিয়ে পুরস্কৃত হওয়া
3. ERC-20 তারল্য টোকেন পোড়ানো এবং সেই ERC-20 টোকেনগুলো ফেরত পাওয়া যা পেয়ার এক্সচেঞ্জ ট্রেডারদের এক্সচেঞ্জ করার অনুমতি দেয়

### সোয়াপ {#swap-flow}

এটি সবচেয়ে সাধারণ ফ্লো, যা ট্রেডাররা ব্যবহার করে থাকেন:

#### কলার {#caller}

1. পেরিফেরি অ্যাকাউন্টকে সোয়াপ করার পরিমাণের সমান একটি অ্যালাউন্স প্রদান করুন।
2. পেরিফেরি কন্ট্রাক্টের অনেকগুলো সোয়াপ ফাংশনের মধ্যে যেকোনো একটি কল করুন (কোনটি কল করবেন তা নির্ভর করে ETH জড়িত আছে কি না, ট্রেডার জমা দেওয়ার টোকেনের পরিমাণ বা ফেরত পাওয়ার টোকেনের পরিমাণ নির্দিষ্ট করেছেন কি না ইত্যাদির ওপর)।
   প্রতিটি সোয়াপ ফাংশন একটি `path` গ্রহণ করে, যা হলো অতিক্রম করার জন্য এক্সচেঞ্জগুলোর একটি অ্যারে।

#### পেরিফেরি কন্ট্রাক্টে (UniswapV2Router02.sol) {#in-the-periphery-contract-uniswapv2router02-sol}

3. পাথের প্রতিটি এক্সচেঞ্জে যে পরিমাণ ট্রেড করা প্রয়োজন তা শনাক্ত করুন।
4. পাথের ওপর ইটারেট করে। পথের প্রতিটি এক্সচেঞ্জের জন্য এটি ইনপুট টোকেন পাঠায় এবং তারপর এক্সচেঞ্জের `swap` ফাংশন কল করে।
   বেশিরভাগ ক্ষেত্রে টোকেনগুলোর গন্তব্য ঠিকানা হলো পাথের পরবর্তী পেয়ার এক্সচেঞ্জ। চূড়ান্ত এক্সচেঞ্জের ক্ষেত্রে এটি ট্রেডারের দেওয়া ঠিকানা।

#### কোর কন্ট্রাক্টে (UniswapV2Pair.sol) {#in-the-core-contract-uniswapv2pairsol-2}

5. যাচাই করুন যে কোর কন্ট্রাক্টের সাথে প্রতারণা করা হচ্ছে না এবং সোয়াপের পরে পর্যাপ্ত তারল্য বজায় রাখতে পারে।
6. দেখুন পরিচিত রিজার্ভের পাশাপাশি আমাদের কাছে কতগুলো অতিরিক্ত টোকেন আছে। সেই পরিমাণটি হলো এক্সচেঞ্জ করার জন্য আমাদের প্রাপ্ত ইনপুট টোকেনের সংখ্যা।
7. আউটপুট টোকেনগুলো গন্তব্যে পাঠান।
8. রিজার্ভের পরিমাণ আপডেট করতে `_update` কল করুন

#### পেরিফেরি কন্ট্রাক্টে ফিরে আসা (UniswapV2Router02.sol) {#back-in-the-periphery-contract-uniswapv2router02-sol}

9. প্রয়োজনীয় যেকোনো ক্লিনআপ সম্পাদন করুন (উদাহরণস্বরূপ, ট্রেডারকে পাঠানোর জন্য ETH ফেরত পেতে WETH টোকেন পোড়ানো)

### তারল্য যোগ করা {#add-liquidity-flow}

#### কলার {#caller-2}

1. তারল্য পুলে যোগ করার পরিমাণের সমান একটি অ্যালাউন্স পেরিফেরি অ্যাকাউন্টকে প্রদান করুন।
2. পেরিফেরি কন্ট্রাক্টের `addLiquidity` ফাংশনগুলোর যেকোনো একটি কল করুন।

#### পেরিফেরি কন্ট্রাক্টে (UniswapV2Router02.sol) {#in-the-periphery-contract-uniswapv2router02sol-2}

3. প্রয়োজন হলে একটি নতুন পেয়ার এক্সচেঞ্জ তৈরি করুন
4. যদি কোনো বিদ্যমান পেয়ার এক্সচেঞ্জ থাকে, তবে যোগ করার জন্য টোকেনের পরিমাণ গণনা করুন। এটি উভয় টোকেনের জন্য অভিন্ন মূল্যের হওয়ার কথা, তাই বিদ্যমান টোকেনের সাথে নতুন টোকেনের অনুপাত একই হবে।
5. পরিমাণগুলো গ্রহণযোগ্য কি না তা পরীক্ষা করুন (কলাররা একটি ন্যূনতম পরিমাণ নির্দিষ্ট করতে পারেন যার নিচে তারা তারল্য যোগ করতে চাইবেন না)
6. কোর কন্ট্রাক্ট কল করুন।

#### কোর কন্ট্রাক্টে (UniswapV2Pair.sol) {#in-the-core-contract-uniswapv2pairsol-2-2}

7. তারল্য টোকেন মিন্ট করুন এবং সেগুলো কলারকে পাঠান
8. রিজার্ভের পরিমাণ আপডেট করতে `_update` কল করুন

### তারল্য সরানো {#remove-liquidity-flow}

#### কলার {#caller-3}

1. আন্ডারলাইং টোকেনের বিনিময়ে পোড়ানোর জন্য পেরিফেরি অ্যাকাউন্টকে তারল্য টোকেনের একটি অ্যালাউন্স প্রদান করুন।
2. পেরিফেরি কন্ট্রাক্টের `removeLiquidity` ফাংশনগুলোর যেকোনো একটি কল করুন।

#### পেরিফেরি কন্ট্রাক্টে (UniswapV2Router02.sol) {#in-the-periphery-contract-uniswapv2router02sol-3}

3. পেয়ার এক্সচেঞ্জে তারল্য টোকেনগুলো পাঠান

#### কোর কন্ট্রাক্টে (UniswapV2Pair.sol) {#in-the-core-contract-uniswapv2pairsol-3}

4. পোড়ানো টোকেনের অনুপাতে গন্তব্য ঠিকানায় আন্ডারলাইং টোকেনগুলো পাঠান। উদাহরণস্বরূপ, যদি পুলে 1000 A টোকেন, 500 B টোকেন এবং 90 তারল্য টোকেন থাকে এবং আমরা পোড়ানোর জন্য 9 টোকেন পাই, তবে আমরা 10% তারল্য টোকেন পোড়াচ্ছি, তাই আমরা ব্যবহারকারীকে 100 A টোকেন এবং 50 B টোকেন ফেরত পাঠাব।
5. তারল্য টোকেনগুলো পোড়ান
6. রিজার্ভের পরিমাণ আপডেট করতে `_update` কল করুন

## মূল কন্ট্রাক্টগুলো {#core-contracts}

এগুলো হলো সুরক্ষিত কন্ট্রাক্ট যা তারল্য ধরে রাখে।

### UniswapV2Pair.sol {#uniswapv2pair}

[এই কন্ট্রাক্টটি](https://github.com/Uniswap/uniswap-v2-core/blob/master/contracts/UniswapV2Pair.sol) প্রকৃত পুল বাস্তবায়ন করে যা টোকেন সোয়াপ করে। এটি ইউনিসোয়াপ-এর মূল কার্যকারিতা।

```solidity
pragma solidity =0.5.16;

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

এগুলো হলো সেই সব ইন্টারফেস যা সম্পর্কে কন্ট্রাক্টটির জানা প্রয়োজন, কারণ কন্ট্রাক্টটি হয় এগুলো বাস্তবায়ন করে (`IUniswapV2Pair` এবং `UniswapV2ERC20`) অথবা এটি এমন কন্ট্রাক্টগুলোকে কল করে যারা এগুলো বাস্তবায়ন করে।

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

এই কন্ট্রাক্টটি `UniswapV2ERC20` থেকে ইনহেরিট করে, যা তারল্য টোকেনগুলোর জন্য ERC-20 ফাংশন প্রদান করে।

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

ওভারফ্লো এবং আন্ডারফ্লো এড়াতে [SafeMath লাইব্রেরি](https://docs.openzeppelin.com/contracts/2.x/api/math) ব্যবহার করা হয়। এটি গুরুত্বপূর্ণ কারণ অন্যথায় আমরা এমন একটি পরিস্থিতির সম্মুখীন হতে পারি যেখানে একটি মান `-1` হওয়া উচিত, কিন্তু তার পরিবর্তে `2^256-1` হয়ে যায়।

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

পুল কন্ট্রাক্টের অনেক গণনায় ভগ্নাংশের প্রয়োজন হয়। তবে, EVM ভগ্নাংশ সমর্থন করে না।
ইউনিসোয়াপ যে সমাধানটি খুঁজে পেয়েছে তা হলো 224 বিট মান ব্যবহার করা, যার মধ্যে 112 বিট পূর্ণসংখ্যার জন্য এবং 112 বিট ভগ্নাংশের জন্য। তাই `1.0`-কে `2^112` হিসেবে, `1.5`-কে `2^112 + 2^111` হিসেবে উপস্থাপন করা হয়, ইত্যাদি।

এই লাইব্রেরি সম্পর্কে আরও বিস্তারিত তথ্য [ডকুমেন্টের পরের অংশে](#fixedpoint) পাওয়া যাবে।

#### ভেরিয়েবল {#pair-vars}

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

শূন্য দিয়ে ভাগের ঘটনা এড়াতে, সর্বদা একটি ন্যূনতম সংখ্যক তারল্য টোকেন থাকে (তবে এগুলো শূন্য অ্যাকাউন্টের মালিকানাধীন)। সেই সংখ্যাটি হলো **MINIMUM_LIQUIDITY**, এক হাজার।

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

এটি ERC-20 হস্তান্তর ফাংশনের জন্য ABI সিলেক্টর। এটি দুটি টোকেন অ্যাকাউন্টে ERC-20 টোকেন হস্তান্তর করতে ব্যবহৃত হয়।

```solidity
    address public factory;
```

এটি হলো সেই ফ্যাক্টরি কন্ট্রাক্ট যা এই পুলটি তৈরি করেছে। প্রতিটি পুল হলো দুটি ERC-20 টোকেনের মধ্যে একটি এক্সচেঞ্জ, ফ্যাক্টরি হলো একটি কেন্দ্রীয় পয়েন্ট যা এই সমস্ত পুলকে সংযুক্ত করে।

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

এখানে দুই ধরনের ERC-20 টোকেনের কন্ট্রাক্টগুলোর ঠিকানা রয়েছে যা এই পুলের মাধ্যমে সোয়াপ করা যেতে পারে।

```solidity
    uint112 private reserve0;           // একটি মাত্র স্টোরেজ স্লট ব্যবহার করে, যা getReserves এর মাধ্যমে অ্যাক্সেসযোগ্য
    uint112 private reserve1;           // একটি মাত্র স্টোরেজ স্লট ব্যবহার করে, যা getReserves এর মাধ্যমে অ্যাক্সেসযোগ্য
```

প্রতিটি টোকেন প্রকারের জন্য পুলের যে রিজার্ভ রয়েছে। আমরা ধরে নিই যে দুটি একই পরিমাণ মান উপস্থাপন করে, এবং তাই প্রতিটি token0-এর মান reserve1/reserve0 token1-এর সমান।

```solidity
    uint32  private blockTimestampLast; // একটি মাত্র স্টোরেজ স্লট ব্যবহার করে, যা getReserves এর মাধ্যমে অ্যাক্সেসযোগ্য
```

সর্বশেষ যে ব্লকে একটি এক্সচেঞ্জ ঘটেছে তার টাইমস্ট্যাম্প, যা সময়ের সাথে সাথে এক্সচেঞ্জ রেট ট্র্যাক করতে ব্যবহৃত হয়।

ইথেরিয়াম কন্ট্রাক্টগুলোর সবচেয়ে বড় গ্যাস খরচের একটি হলো স্টোরেজ, যা কন্ট্রাক্টের এক কল থেকে পরবর্তী কল পর্যন্ত বজায় থাকে। প্রতিটি স্টোরেজ সেল 256 বিট দীর্ঘ। তাই তিনটি ভেরিয়েবল, `reserve0`, `reserve1`, এবং `blockTimestampLast` এমনভাবে বরাদ্দ করা হয় যাতে একটি একক স্টোরেজ মান তাদের তিনটিকেই অন্তর্ভুক্ত করতে পারে (112+112+32=256)।

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

এই ভেরিয়েবলগুলো প্রতিটি টোকেনের জন্য ক্রমবর্ধমান খরচ ধারণ করে (প্রতিটি অন্যটির সাপেক্ষে)। এগুলো একটি নির্দিষ্ট সময়ের মধ্যে গড় এক্সচেঞ্জ রেট গণনা করতে ব্যবহার করা যেতে পারে।

```solidity
    uint public kLast; // reserve0 * reserve1, সাম্প্রতিকতম তারল্য ইভেন্ট এর ঠিক পরের অবস্থা অনুযায়ী
```

পেয়ার এক্সচেঞ্জ যেভাবে token0 এবং token1-এর মধ্যে এক্সচেঞ্জ রেট নির্ধারণ করে তা হলো ট্রেডের সময় দুটি রিজার্ভের গুণফল স্থির রাখা। `kLast` হলো এই মান। যখন কোনো তারল্য প্রদানকারী টোকেন জমা দেয় বা উত্তোলন করে তখন এটি পরিবর্তিত হয়, এবং 0.3% মার্কেট ফি-এর কারণে এটি সামান্য বৃদ্ধি পায়।

এখানে একটি সহজ উদাহরণ দেওয়া হলো। মনে রাখবেন যে সরলতার স্বার্থে টেবিলে দশমিক বিন্দুর পরে মাত্র তিনটি সংখ্যা রয়েছে, এবং আমরা 0.3% ট্রেডিং ফি উপেক্ষা করেছি তাই সংখ্যাগুলো পুরোপুরি সঠিক নয়।

| ইভেন্ট | reserve0 | reserve1 | reserve0 \* reserve1 | গড় এক্সচেঞ্জ রেট (token1 / token0) |
| ------------------------------------------- | --------: | --------: | -------------------: | --------------------------------------- |
| প্রাথমিক সেটআপ | 1,000.000 | 1,000.000 | 1,000,000 | |
| ট্রেডার A 47.619 token1-এর বিনিময়ে 50 token0 সোয়াপ করে | 1,050.000 | 952.381 | 1,000,000 | 0.952 |
| ট্রেডার B 8.984 token1-এর বিনিময়ে 10 token0 সোয়াপ করে | 1,060.000 | 943.396 | 1,000,000 | 0.898 |
| ট্রেডার C 34.305 token1-এর বিনিময়ে 40 token0 সোয়াপ করে | 1,100.000 | 909.090 | 1,000,000 | 0.858 |
| ট্রেডার D 109.01 token0-এর বিনিময়ে 100 token1 সোয়াপ করে | 990.990 | 1,009.090 | 1,000,000 | 0.917 |
| ট্রেডার E 10.079 token1-এর বিনিময়ে 10 token0 সোয়াপ করে | 1,000.990 | 999.010 | 1,000,000 | 1.008 |

ট্রেডাররা যখন আরও বেশি token0 প্রদান করে, তখন সরবরাহ এবং চাহিদার উপর ভিত্তি করে token1-এর আপেক্ষিক মান বৃদ্ধি পায়, এবং এর বিপরীতটিও সত্য।

#### লক {#pair-lock}

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

নিরাপত্তা দুর্বলতার একটি শ্রেণি রয়েছে যা [রিএন্ট্রান্সি অপব্যবহারের](https://medium.com/coinmonks/ethernaut-lvl-10-re-entrancy-walkthrough-how-to-abuse-execution-ordering-and-reproduce-the-dao-7ec88b912c14) উপর ভিত্তি করে তৈরি। ইউনিসোয়াপ-এর যেকোনো ERC-20 টোকেন হস্তান্তর করা প্রয়োজন, যার অর্থ হলো এমন ERC-20 কন্ট্রাক্টগুলোকে কল করা যারা তাদের কল করা ইউনিসোয়াপ মার্কেটের অপব্যবহার করার চেষ্টা করতে পারে।
কন্ট্রাক্টের অংশ হিসেবে একটি `unlocked` ভেরিয়েবল রাখার মাধ্যমে, আমরা ফাংশনগুলো চলাকালীন (একই ট্রানজ্যাকশনের মধ্যে) সেগুলোকে কল করা থেকে আটকাতে পারি।

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

এই ফাংশনটি হলো একটি [মডিফায়ার](https://docs.soliditylang.org/en/v0.8.3/contracts.html#function-modifiers), এমন একটি ফাংশন যা একটি সাধারণ ফাংশনকে আবৃত করে কোনোভাবে এর আচরণ পরিবর্তন করে।

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

যদি `unlocked` এক-এর সমান হয়, তবে এটিকে শূন্যতে সেট করুন। যদি এটি আগে থেকেই শূন্য থাকে তবে কলটি রিভার্ট করুন, এটিকে ব্যর্থ করুন।

```solidity
        _;
```

একটি মডিফায়ারে `_;` হলো মূল ফাংশন কল (সমস্ত প্যারামিটার সহ)। এখানে এর অর্থ হলো ফাংশন কলটি কেবল তখনই ঘটে যদি কল করার সময় `unlocked` এক থাকে, এবং এটি চলাকালীন `unlocked`-এর মান শূন্য থাকে।

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

মূল ফাংশনটি রিটার্ন করার পরে, লকটি ছেড়ে দিন।

#### বিবিধ ফাংশন {#pair-misc}

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

এই ফাংশনটি কলারদের এক্সচেঞ্জের বর্তমান স্টেট প্রদান করে। লক্ষ্য করুন যে Solidity ফাংশনগুলো [একাধিক মান রিটার্ন করতে পারে](https://docs.soliditylang.org/en/v0.8.3/contracts.html#returning-multiple-values)।

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

এই অভ্যন্তরীণ ফাংশনটি এক্সচেঞ্জ থেকে অন্য কাউকে একটি নির্দিষ্ট পরিমাণ ERC20 টোকেন হস্তান্তর করে। `SELECTOR` নির্দিষ্ট করে যে আমরা যে ফাংশনটিকে কল করছি তা হলো `transfer(address,uint)` (উপরের সংজ্ঞা দেখুন)।

টোকেন ফাংশনের জন্য একটি ইন্টারফেস ইমপোর্ট করা এড়াতে, আমরা [ABI ফাংশনগুলোর](https://docs.soliditylang.org/en/v0.8.3/units-and-global-variables.html#abi-encoding-and-decoding-functions) একটি ব্যবহার করে "ম্যানুয়ালি" কলটি তৈরি করি।

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

দুটি উপায়ে একটি ERC-20 হস্তান্তর কল ব্যর্থতা রিপোর্ট করতে পারে:

1. রিভার্ট। যদি কোনো বাহ্যিক কন্ট্রাক্টে কল রিভার্ট হয়, তবে বুলিয়ান রিটার্ন মান হয় `false`
2. স্বাভাবিকভাবে শেষ হয় কিন্তু একটি ব্যর্থতা রিপোর্ট করে। সেক্ষেত্রে রিটার্ন মান বাফারের একটি অ-শূন্য দৈর্ঘ্য থাকে, এবং যখন একটি বুলিয়ান মান হিসেবে ডিকোড করা হয় তখন এটি হয় `false`

যদি এই শর্তগুলোর কোনোটি ঘটে, তবে রিভার্ট করুন।

#### ইভেন্ট {#pair-events}

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

যখন কোনো তারল্য প্রদানকারী তারল্য জমা দেয় (`Mint`) বা এটি উত্তোলন করে (`Burn`) তখন এই দুটি ইভেন্ট এমিট হয়। উভয় ক্ষেত্রেই, জমা দেওয়া বা উত্তোলন করা token0 এবং token1-এর পরিমাণ ইভেন্টের অংশ, সেইসাথে যে অ্যাকাউন্টটি আমাদের কল করেছে তার পরিচয়ও (`sender`)। উত্তোলনের ক্ষেত্রে, ইভেন্টটিতে টোকেন গ্রহণকারী লক্ষ্যটিও অন্তর্ভুক্ত থাকে (`to`), যা প্রেরকের মতো নাও হতে পারে।

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

যখন কোনো ট্রেডার একটি টোকেনের বিনিময়ে অন্যটি সোয়াপ করে তখন এই ইভেন্টটি এমিট হয়। আবারও, প্রেরক এবং গন্তব্য একই নাও হতে পারে।
প্রতিটি টোকেন হয় এক্সচেঞ্জে পাঠানো হতে পারে, অথবা এটি থেকে গ্রহণ করা হতে পারে।

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

অবশেষে, কারণ যাই হোক না কেন, প্রতিবার টোকেন যোগ বা উত্তোলন করার সময় সর্বশেষ রিজার্ভ তথ্য (এবং সেই কারণে এক্সচেঞ্জ রেট) প্রদান করতে `Sync` এমিট হয়।

#### সেটআপ ফাংশন {#pair-setup}

নতুন পেয়ার এক্সচেঞ্জ সেট আপ করার সময় এই ফাংশনগুলো একবার কল করার কথা।

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

কনস্ট্রাক্টর নিশ্চিত করে যে আমরা সেই ফ্যাক্টরির ঠিকানা ট্র্যাক করব যা পেয়ারটি তৈরি করেছে। এই তথ্যটি `initialize` এবং ফ্যাক্টরি ফি-এর জন্য (যদি থাকে) প্রয়োজন।

```solidity
    // ডিপ্লয়মেন্টের সময় ফ্যাক্টরি দ্বারা একবার কল করা হয়
    function initialize(address _token0, address _token1) external {
        require(msg.sender == factory, 'UniswapV2: FORBIDDEN'); // পর্যাপ্ত যাচাই
        token0 = _token0;
        token1 = _token1;
    }
```

এই ফাংশনটি ফ্যাক্টরিকে (এবং শুধুমাত্র ফ্যাক্টরিকে) দুটি ERC-20 টোকেন নির্দিষ্ট করার অনুমতি দেয় যা এই পেয়ারটি সোয়াপ করবে।

#### অভ্যন্তরীণ আপডেট ফাংশন {#pair-update-internal}

##### \_update

```solidity
    // রিজার্ভ আপডেট করে এবং, প্রতি ব্লক এ প্রথম কলে, প্রাইস অ্যাকুমুলেটর আপডেট করে
    function _update(uint balance0, uint balance1, uint112 _reserve0, uint112 _reserve1) private {
```

প্রতিবার টোকেন জমা বা উত্তোলন করার সময় এই ফাংশনটি কল করা হয়।

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

যদি balance0 বা balance1 (uint256) uint112(-1) (=2^112-1)-এর চেয়ে বেশি হয় (তাই এটি ওভারফ্লো হয় এবং uint112-এ রূপান্তরিত হওয়ার সময় 0-তে ফিরে আসে) তবে ওভারফ্লো রোধ করতে \_update চালিয়ে যেতে অস্বীকার করুন। একটি সাধারণ টোকেন যা 10^18 ইউনিটে বিভক্ত করা যেতে পারে, এর অর্থ হলো প্রতিটি এক্সচেঞ্জ প্রতিটি টোকেনের প্রায় 5.1\*10^15-এর মধ্যে সীমাবদ্ধ। এখন পর্যন্ত এটি কোনো সমস্যা হয়নি।

```solidity
        uint32 blockTimestamp = uint32(block.timestamp % 2**32);
        uint32 timeElapsed = blockTimestamp - blockTimestampLast; // ওভারফ্লো কাঙ্ক্ষিত
        if (timeElapsed > 0 && _reserve0 != 0 && _reserve1 != 0) {
```

যদি অতিবাহিত সময় শূন্য না হয়, তবে এর অর্থ হলো আমরা এই ব্লকের প্রথম এক্সচেঞ্জ ট্রানজ্যাকশন। সেক্ষেত্রে, আমাদের কস্ট অ্যাকুমুলেটরগুলো আপডেট করতে হবে।

```solidity
            // * কখনও ওভারফ্লো হয় না, এবং + ওভারফ্লো কাঙ্ক্ষিত
            price0CumulativeLast += uint(UQ112x112.encode(_reserve1).uqdiv(_reserve0)) * timeElapsed;
            price1CumulativeLast += uint(UQ112x112.encode(_reserve0).uqdiv(_reserve1)) * timeElapsed;
        }
```

প্রতিটি কস্ট অ্যাকুমুলেটর সর্বশেষ খরচ (অন্য টোকেনের রিজার্ভ/এই টোকেনের রিজার্ভ) গুণ সেকেন্ডে অতিবাহিত সময় দিয়ে আপডেট করা হয়। একটি গড় মূল্য পেতে, আপনি সময়ের দুটি বিন্দুতে ক্রমবর্ধমান মূল্য পড়েন এবং তাদের মধ্যে সময়ের পার্থক্য দিয়ে ভাগ করেন। উদাহরণস্বরূপ, ইভেন্টগুলোর এই ক্রমটি ধরে নিন:

| ইভেন্ট | reserve0 | reserve1 | টাইমস্ট্যাম্প | প্রান্তিক এক্সচেঞ্জ রেট (reserve1 / reserve0) | price0CumulativeLast |
| -------------------------------------------------------- | --------: | --------: | --------- | -------------------------------------------: | -------------------------: |
| প্রাথমিক সেটআপ | 1,000.000 | 1,000.000 | 5,000 | 1.000 | 0 |
| ট্রেডার A 50 token0 জমা দেয় এবং 47.619 token1 ফেরত পায় | 1,050.000 | 952.381 | 5,020 | 0.907 | 20 |
| ট্রেডার B 10 token0 জমা দেয় এবং 8.984 token1 ফেরত পায় | 1,060.000 | 943.396 | 5,030 | 0.890 | 20+10\*0.907 = 29.07 |
| ট্রেডার C 40 token0 জমা দেয় এবং 34.305 token1 ফেরত পায় | 1,100.000 | 909.090 | 5,100 | 0.826 | 29.07+70\*0.890 = 91.37 |
| ট্রেডার D 100 token1 জমা দেয় এবং 109.01 token0 ফেরত পায় | 990.990 | 1,009.090 | 5,110 | 1.018 | 91.37+10\*0.826 = 99.63 |
| ট্রেডার E 10 token0 জমা দেয় এবং 10.079 token1 ফেরত পায় | 1,000.990 | 999.010 | 5,150 | 0.998 | 99.63+40\*1.1018 = 143.702 |

ধরা যাক আমরা 5,030 এবং 5,150 টাইমস্ট্যাম্পের মধ্যে **Token0**-এর গড় মূল্য গণনা করতে চাই। `price0Cumulative`-এর মানের পার্থক্য হলো 143.702-29.07=114.632। এটি দুই মিনিটের (120 সেকেন্ড) গড়। তাই গড় মূল্য হলো 114.632/120 = 0.955।

এই মূল্য গণনার কারণেই আমাদের পুরানো রিজার্ভের আকারগুলো জানতে হবে।

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

অবশেষে, গ্লোবাল ভেরিয়েবলগুলো আপডেট করুন এবং একটি `Sync` ইভেন্ট এমিট করুন।

##### \_mintFee

```solidity
    // যদি ফি চালু থাকে, তবে sqrt(k) এর বৃদ্ধির ১/৬ অংশের সমতুল্য তারল্য মিন্ট করে
    function _mintFee(uint112 _reserve0, uint112 _reserve1) private returns (bool feeOn) {
```

ইউনিসোয়াপ 2.0-তে ট্রেডাররা মার্কেট ব্যবহার করার জন্য 0.30% ফি প্রদান করে। সেই ফি-এর বেশিরভাগ (ট্রেডের 0.25%) সর্বদা তারল্য প্রদানকারীদের কাছে যায়। বাকি 0.05% তারল্য প্রদানকারীদের কাছে অথবা ফ্যাক্টরি দ্বারা প্রোটোকল ফি হিসেবে নির্দিষ্ট করা একটি ঠিকানায় যেতে পারে, যা ইউনিসোয়াপ-কে তাদের উন্নয়ন প্রচেষ্টার জন্য অর্থ প্রদান করে।

গণনা (এবং সেই কারণে গ্যাস খরচ) কমাতে, এই ফি প্রতিটি ট্রানজ্যাকশনের পরিবর্তে কেবল তখনই গণনা করা হয় যখন পুল থেকে তারল্য যোগ করা হয় বা সরানো হয়।

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

ফ্যাক্টরির ফি গন্তব্য পড়ুন। যদি এটি শূন্য হয় তবে কোনো প্রোটোকল ফি নেই এবং সেই ফি গণনা করার কোনো প্রয়োজন নেই।

```solidity
        uint _kLast = kLast; // গ্যাস সাশ্রয়
```

`kLast` স্টেট ভেরিয়েবলটি স্টোরেজে অবস্থিত, তাই কন্ট্রাক্টে বিভিন্ন কলের মধ্যে এর একটি মান থাকবে।
স্টোরেজ অ্যাক্সেস করা ভোলাটাইল মেমরি অ্যাক্সেস করার চেয়ে অনেক বেশি ব্যয়বহুল যা কন্ট্রাক্টে ফাংশন কল শেষ হলে রিলিজ হয়ে যায়, তাই আমরা গ্যাস বাঁচাতে একটি অভ্যন্তরীণ ভেরিয়েবল ব্যবহার করি।

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

তারল্য প্রদানকারীরা কেবল তাদের তারল্য টোকেনগুলোর মূল্যবৃদ্ধির মাধ্যমেই তাদের অংশ পায়। কিন্তু প্রোটোকল ফি-এর জন্য নতুন তারল্য টোকেন মিন্ট করা এবং `feeTo` ঠিকানায় প্রদান করা প্রয়োজন।

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

যদি নতুন তারল্য থাকে যার উপর প্রোটোকল ফি সংগ্রহ করতে হবে। আপনি [এই আর্টিকেলের পরের অংশে](#math) স্কয়ার রুট ফাংশনটি দেখতে পারেন

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

ফি-এর এই জটিল গণনাটি [শ্বেতপত্রের](https://app.uniswap.org/whitepaper.pdf) 5 পৃষ্ঠায় ব্যাখ্যা করা হয়েছে। আমরা জানি যে `kLast` গণনা করার সময় এবং বর্তমান সময়ের মধ্যে কোনো তারল্য যোগ করা বা সরানো হয়নি (কারণ আমরা প্রতিবার তারল্য যোগ বা সরানোর সময় এই গণনাটি চালাই, এটি আসলে পরিবর্তন হওয়ার আগে), তাই `reserve0 * reserve1`-এ যেকোনো পরিবর্তন ট্রানজ্যাকশন ফি থেকে আসতে হবে (এগুলো ছাড়া আমরা `reserve0 * reserve1` স্থির রাখতাম)।

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

প্রকৃতপক্ষে অতিরিক্ত তারল্য টোকেন তৈরি করতে এবং সেগুলোকে `feeTo`-এ বরাদ্দ করতে `UniswapV2ERC20._mint` ফাংশনটি ব্যবহার করুন।

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

যদি কোনো ফি না থাকে তবে `kLast`-কে শূন্যতে সেট করুন (যদি এটি আগে থেকেই তা না হয়)। যখন এই কন্ট্রাক্টটি লেখা হয়েছিল তখন একটি [গ্যাস রিফান্ড বৈশিষ্ট্য](https://eips.ethereum.org/EIPS/eip-3298) ছিল যা কন্ট্রাক্টগুলোকে তাদের অপ্রয়োজনীয় স্টোরেজ শূন্য করে ইথেরিয়াম স্টেটের সামগ্রিক আকার কমাতে উৎসাহিত করত।
এই কোডটি সম্ভব হলে সেই রিফান্ড পায়।

#### বাহ্যিকভাবে অ্যাক্সেসযোগ্য ফাংশন {#pair-external}

মনে রাখবেন যে যদিও যেকোনো ট্রানজ্যাকশন বা কন্ট্রাক্ট এই ফাংশনগুলোকে কল করতে _পারে_, এগুলো পেরিফেরি কন্ট্রাক্ট থেকে কল করার জন্য ডিজাইন করা হয়েছে। আপনি যদি এগুলোকে সরাসরি কল করেন তবে আপনি পেয়ার এক্সচেঞ্জকে প্রতারণা করতে পারবেন না, তবে আপনি ভুলের মাধ্যমে মান হারাতে পারেন।

##### মিন্ট

```solidity
    // এই লো-লেভেল ফাংশনটি এমন একটি কন্ট্রাক্ট থেকে কল করা উচিত যা গুরুত্বপূর্ণ নিরাপত্তা যাচাই করে
    function mint(address to) external lock returns (uint liquidity) {
```

যখন কোনো তারল্য প্রদানকারী পুলে তারল্য যোগ করে তখন এই ফাংশনটি কল করা হয়। এটি পুরস্কার হিসেবে অতিরিক্ত তারল্য টোকেন মিন্ট করে। এটিকে [একটি পেরিফেরি কন্ট্রাক্ট](#uniswapv2router02) থেকে কল করা উচিত যা একই ট্রানজ্যাকশনে তারল্য যোগ করার পরে এটিকে কল করে (যাতে অন্য কেউ বৈধ মালিকের আগে নতুন তারল্য দাবি করে এমন কোনো ট্রানজ্যাকশন জমা দিতে না পারে)।

```solidity
        (uint112 _reserve0, uint112 _reserve1,) = getReserves(); // গ্যাস সাশ্রয়
```

একাধিক মান রিটার্ন করে এমন একটি Solidity ফাংশনের ফলাফল পড়ার এটিই উপায়। আমরা সর্বশেষ রিটার্ন করা মানগুলো, ব্লক টাইমস্ট্যাম্প, বাতিল করি কারণ আমাদের এটির প্রয়োজন নেই।

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

বর্তমান ব্যালেন্সগুলো পান এবং দেখুন প্রতিটি টোকেন প্রকারের কতটা যোগ করা হয়েছে।

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

সংগ্রহ করার জন্য প্রোটোকল ফি গণনা করুন, যদি থাকে, এবং সেই অনুযায়ী তারল্য টোকেন মিন্ট করুন। যেহেতু `_mintFee`-এর প্যারামিটারগুলো হলো পুরানো রিজার্ভ মান, তাই ফি-এর কারণে পুলের পরিবর্তনের উপর ভিত্তি করে ফি সঠিকভাবে গণনা করা হয়।

```solidity
        uint _totalSupply = totalSupply; // গ্যাস সাশ্রয়, অবশ্যই এখানে সংজ্ঞায়িত করতে হবে কারণ totalSupply _mintFee তে আপডেট হতে পারে
        if (_totalSupply == 0) {
            liquidity = Math.sqrt(amount0.mul(amount1)).sub(MINIMUM_LIQUIDITY);
           _mint(address(0), MINIMUM_LIQUIDITY); // প্রথম MINIMUM_LIQUIDITY টোকেন স্থায়ীভাবে লক করে
```

যদি এটি প্রথম জমা হয়, তবে `MINIMUM_LIQUIDITY` টোকেন তৈরি করুন এবং সেগুলোকে লক করতে শূন্য ঠিকানায় পাঠান। এগুলো কখনোই রিডিম করা যাবে না, যার অর্থ হলো পুলটি কখনোই সম্পূর্ণ খালি হবেবিধা (এটি আমাদের কিছু জায়গায় শূন্য দিয়ে ভাগ করা থেকে বাঁচায়)। `MINIMUM_LIQUIDITY`-এর মান হলো এক হাজার, যা বিবেচনা করে যে বেশিরভাগ ERC-20 একটি টোকেনের 10^-18 তম ইউনিটে বিভক্ত, যেমন ETH Wei-তে বিভক্ত, এটি একটি একক টোকেনের মানের 10^-15। খুব বেশি খরচ নয়।

প্রথম জমার সময় আমরা দুটি টোকেনের আপেক্ষিক মান জানি না, তাই আমরা কেবল পরিমাণগুলো গুণ করি এবং একটি স্কয়ার রুট নিই, এই ধরে নিয়ে যে জমাটি আমাদের উভয় টোকেনে সমান মান প্রদান করে।

আমরা এটিকে বিশ্বাস করতে পারি কারণ আরবিট্রেজে মান হারানো এড়াতে সমান মান প্রদান করা আমানতকারীর স্বার্থে।
ধরা যাক দুটি টোকেনের মান অভিন্ন, কিন্তু আমাদের আমানতকারী **Token0**-এর তুলনায় চারগুণ বেশি **Token1** জমা দিয়েছে। একজন ট্রেডার এই সত্যটি ব্যবহার করতে পারে যে পেয়ার এক্সচেঞ্জ মনে করে যে **Token0** আরও মূল্যবান, এটি থেকে মান বের করতে।

| ইভেন্ট | reserve0 | reserve1 | reserve0 \* reserve1 | পুলের মান (reserve0 + reserve1) |
| ------------------------------------------------------------ | -------: | -------: | -------------------: | --------------------------------------: |
| প্রাথমিক সেটআপ | 8 | 32 | 256 | 40 |
| ট্রেডার 8টি **Token0** টোকেন জমা দেয়, 16টি **Token1** ফেরত পায় | 16 | 16 | 256 | 32 |

যেমনটি আপনি দেখতে পাচ্ছেন, ট্রেডার অতিরিক্ত 8টি টোকেন উপার্জন করেছে, যা পুলের মান হ্রাস থেকে আসে, যা এর মালিক আমানতকারীকে ক্ষতিগ্রস্ত করে।

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

পরবর্তী প্রতিটি জমার সাথে আমরা ইতিমধ্যেই দুটি সম্পদের মধ্যে এক্সচেঞ্জ রেট জানি, এবং আমরা আশা করি তারল্য প্রদানকারীরা উভয়টিতে সমান মান প্রদান করবে। যদি তারা তা না করে, তবে আমরা তাদের শাস্তি হিসেবে তাদের প্রদান করা কম মানের উপর ভিত্তি করে তারল্য টোকেন দিই।

এটি প্রাথমিক জমা হোক বা পরবর্তী কোনো জমা, আমরা যে সংখ্যক তারল্য টোকেন প্রদান করি তা `reserve0*reserve1`-এর পরিবর্তনের স্কয়ার রুটের সমান এবং তারল্য টোকেনের মান পরিবর্তিত হয় না (যদি না আমরা এমন একটি জমা পাই যার উভয় প্রকারের সমান মান নেই, সেক্ষেত্রে "জরিমানা" বিতরণ করা হয়)। এখানে একই মান রয়েছে এমন দুটি টোকেনের আরেকটি উদাহরণ দেওয়া হলো, যার মধ্যে তিনটি ভালো জমা এবং একটি খারাপ জমা রয়েছে (শুধুমাত্র এক প্রকারের টোকেন জমা, তাই এটি কোনো তারল্য টোকেন তৈরি করে না)।

| ইভেন্ট | reserve0 | reserve1 | reserve0 \* reserve1 | পুলের মান (reserve0 + reserve1) | এই জমার জন্য মিন্ট করা তারল্য টোকেন | মোট তারল্য টোকেন | প্রতিটি তারল্য টোকেনের মান |
| ------------------------- | -------: | -------: | -------------------: | -------------------------------: | ---------------------------------------: | ---------------------: | ----------------------------: |
| প্রাথমিক সেটআপ | 8.000 | 8.000 | 64 | 16.000 | 8 | 8 | 2.000 |
| প্রতিটি প্রকারের চারটি জমা দিন | 12.000 | 12.000 | 144 | 24.000 | 4 | 12 | 2.000 |
| প্রতিটি প্রকারের দুটি জমা দিন | 14.000 | 14.000 | 196 | 28.000 | 2 | 14 | 2.000 |
| অসমান মানের জমা | 18.000 | 14.000 | 252 | 32.000 | 0 | 14 | ~2.286 |
| আরবিট্রেজের পরে | ~15.874 | ~15.874 | 252 | ~31.748 | 0 | 14 | ~2.267 |

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

প্রকৃতপক্ষে অতিরিক্ত তারল্য টোকেন তৈরি করতে এবং সেগুলোকে সঠিক অ্যাকাউন্টে দিতে `UniswapV2ERC20._mint` ফাংশনটি ব্যবহার করুন।

```solidity

        _update(balance0, balance1, _reserve0, _reserve1);
        if (feeOn) kLast = uint(reserve0).mul(reserve1); // reserve0 এবং reserve1 আপ-টু-ডেট আছে
        emit Mint(msg.sender, amount0, amount1);
    }
```

স্টেট ভেরিয়েবলগুলো (`reserve0`, `reserve1`, এবং প্রয়োজন হলে `kLast`) আপডেট করুন এবং উপযুক্ত ইভেন্ট এমিট করুন।

##### পোড়ানো

```solidity
    // এই লো-লেভেল ফাংশনটি এমন একটি কন্ট্রাক্ট থেকে কল করা উচিত যা গুরুত্বপূর্ণ নিরাপত্তা যাচাই করে
    function burn(address to) external lock returns (uint amount0, uint amount1) {
```

যখন তারল্য উত্তোলন করা হয় এবং উপযুক্ত তারল্য টোকেনগুলো পোড়ানোর প্রয়োজন হয় তখন এই ফাংশনটি কল করা হয়।
এটিও [একটি পেরিফেরি অ্যাকাউন্ট থেকে](#uniswapv2router02) কল করা উচিত।

```solidity
        (uint112 _reserve0, uint112 _reserve1,) = getReserves(); // গ্যাস সাশ্রয়
        address _token0 = token0;                                // গ্যাস সাশ্রয়
        address _token1 = token1;                                // গ্যাস সাশ্রয়
        uint balance0 = IERC20(_token0).balanceOf(address(this));
        uint balance1 = IERC20(_token1).balanceOf(address(this));
        uint liquidity = balanceOf[address(this)];
```

পেরিফেরি কন্ট্রাক্ট কলের আগে পোড়ানোর জন্য তারল্য এই কন্ট্রাক্টে হস্তান্তর করেছে। এইভাবে আমরা জানি কতটা তারল্য পোড়াতে হবে, এবং আমরা নিশ্চিত করতে পারি যে এটি পোড়ানো হয়েছে।

```solidity
        bool feeOn = _mintFee(_reserve0, _reserve1);
        uint _totalSupply = totalSupply; // গ্যাস সাশ্রয়, অবশ্যই এখানে সংজ্ঞায়িত করতে হবে কারণ totalSupply _mintFee তে আপডেট হতে পারে
        amount0 = liquidity.mul(balance0) / _totalSupply; // ব্যালেন্স ব্যবহার করা আনুপাতিক বণ্টন নিশ্চিত করে
        amount1 = liquidity.mul(balance1) / _totalSupply; // ব্যালেন্স ব্যবহার করা আনুপাতিক বণ্টন নিশ্চিত করে
        require(amount0 > 0 && amount1 > 0, 'UniswapV2: INSUFFICIENT_LIQUIDITY_BURNED');
```

তারল্য প্রদানকারী উভয় টোকেনের সমান মান গ্রহণ করে। এইভাবে আমরা এক্সচেঞ্জ রেট পরিবর্তন করি না।

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

        _update(balance0, balance1, _reserve0, _reserve1);
        if (feeOn) kLast = uint(reserve0).mul(reserve1); // reserve0 এবং reserve1 আপ-টু-ডেট আছে
        emit Burn(msg.sender, amount0, amount1, to);
    }

```

বাকি `burn` ফাংশনটি উপরের `mint` ফাংশনের মিরর ইমেজ।

##### সোয়াপ

```solidity
    // এই লো-লেভেল ফাংশনটি এমন একটি কন্ট্রাক্ট থেকে কল করা উচিত যা গুরুত্বপূর্ণ নিরাপত্তা যাচাই করে
    function swap(uint amount0Out, uint amount1Out, address to, bytes calldata data) external lock {
```

এই ফাংশনটিও [একটি পেরিফেরি কন্ট্রাক্ট](#uniswapv2router02) থেকে কল করার কথা।

```solidity
        require(amount0Out > 0 || amount1Out > 0, 'UniswapV2: INSUFFICIENT_OUTPUT_AMOUNT');
        (uint112 _reserve0, uint112 _reserve1,) = getReserves(); // গ্যাস সাশ্রয়
        require(amount0Out < _reserve0 && amount1Out < _reserve1, 'UniswapV2: INSUFFICIENT_LIQUIDITY');

        uint balance0;
        uint balance1;
        { // _token{0,1} এর জন্য স্কোপ, স্ট্যাক টু ডিপ (stack too deep) এরর এড়ায়
```

লোকাল ভেরিয়েবলগুলো মেমরিতে সংরক্ষণ করা যেতে পারে অথবা, যদি সেগুলোর সংখ্যা খুব বেশি না হয়, তবে সরাসরি স্ট্যাকে সংরক্ষণ করা যেতে পারে।
যদি আমরা সংখ্যাটি সীমিত করতে পারি যাতে আমরা স্ট্যাক ব্যবহার করব তবে আমরা কম গ্যাস ব্যবহার করি। আরও বিস্তারিত জানার জন্য [ইয়েলো পেপার, আনুষ্ঠানিক ইথেরিয়াম স্পেসিফিকেশন](https://ethereum.github.io/yellowpaper/paper.pdf), পৃষ্ঠা 26, সমীকরণ 298 দেখুন।

```solidity
            address _token0 = token0;
            address _token1 = token1;
            require(to != _token0 && to != _token1, 'UniswapV2: INVALID_TO');
            if (amount0Out > 0) _safeTransfer(_token0, to, amount0Out); // অপটিমিস্টিক্যালি টোকেন হস্তান্তর করে
            if (amount1Out > 0) _safeTransfer(_token1, to, amount1Out); // অপটিমিস্টিক্যালি টোকেন হস্তান্তর করে
```

এই হস্তান্তরটি আশাবাদী, কারণ সমস্ত শর্ত পূরণ হয়েছে কিনা তা নিশ্চিত হওয়ার আগেই আমরা হস্তান্তর করি। ইথেরিয়াম-এ এটি ঠিক আছে কারণ যদি কলের পরে শর্তগুলো পূরণ না হয় তবে আমরা এটি থেকে রিভার্ট করি এবং এটি যে কোনো পরিবর্তন তৈরি করেছে তা বাতিল করি।

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

অনুরোধ করা হলে রিসিভারকে সোয়াপ সম্পর্কে জানান।

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

বর্তমান ব্যালেন্সগুলো পান। পেরিফেরি কন্ট্রাক্ট সোয়াপের জন্য আমাদের কল করার আগে আমাদের টোকেনগুলো পাঠায়। এটি কন্ট্রাক্টের জন্য এটি পরীক্ষা করা সহজ করে তোলে যে এটি প্রতারিত হচ্ছে না, এমন একটি পরীক্ষা যা মূল কন্ট্রাক্টে _হতেই_ হবে (কারণ আমাদের পেরিফেরি কন্ট্রাক্ট ছাড়া অন্য সত্তাগুলো দ্বারা আমাদের কল করা যেতে পারে)।

```solidity
        uint amount0In = balance0 > _reserve0 - amount0Out ? balance0 - (_reserve0 - amount0Out) : 0;
        uint amount1In = balance1 > _reserve1 - amount1Out ? balance1 - (_reserve1 - amount1Out) : 0;
        require(amount0In > 0 || amount1In > 0, 'UniswapV2: INSUFFICIENT_INPUT_AMOUNT');
        { // reserve{0,1}Adjusted এর জন্য স্কোপ, স্ট্যাক টু ডিপ (stack too deep) এরর এড়ায়
            uint balance0Adjusted = balance0.mul(1000).sub(amount0In.mul(3));
            uint balance1Adjusted = balance1.mul(1000).sub(amount1In.mul(3));
            require(balance0Adjusted.mul(balance1Adjusted) >= uint(_reserve0).mul(_reserve1).mul(1000**2), 'UniswapV2: K');
```

এটি একটি স্যানিটি চেক যা নিশ্চিত করে যে আমরা সোয়াপ থেকে হারাব না। এমন কোনো পরিস্থিতি নেই যেখানে একটি সোয়াপের `reserve0*reserve1` কমানো উচিত। এখানেই আমরা নিশ্চিত করি যে সোয়াপে 0.3% ফি পাঠানো হচ্ছে; K-এর মান স্যানিটি চেক করার আগে, আমরা উভয় ব্যালেন্সকে 1000 দিয়ে গুণ করি এবং 3 দিয়ে গুণ করা পরিমাণগুলো বিয়োগ করি, এর অর্থ হলো বর্তমান রিজার্ভের K মানের সাথে এর K মান তুলনা করার আগে ব্যালেন্স থেকে 0.3% (3/1000 = 0.003 = 0.3%) কাটা হচ্ছে।

```solidity
        }

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

`reserve0` এবং `reserve1` আপডেট করুন, এবং প্রয়োজন হলে প্রাইস অ্যাকুমুলেটর এবং টাইমস্ট্যাম্প আপডেট করুন এবং একটি ইভেন্ট এমিট করুন।

##### সিঙ্কিং বা স্কিম

পেয়ার এক্সচেঞ্জ যে রিজার্ভগুলো আছে বলে মনে করে তার সাথে প্রকৃত ব্যালেন্সগুলোর সিঙ্কিং হারিয়ে যাওয়া সম্ভব।
কন্ট্রাক্টের সম্মতি ছাড়া টোকেন উত্তোলন করার কোনো উপায় নেই, তবে জমা দেওয়া একটি ভিন্ন বিষয়। একটি অ্যাকাউন্ট `mint` বা `swap` কল না করেই এক্সচেঞ্জে টোকেন হস্তান্তর করতে পারে।

সেক্ষেত্রে দুটি সমাধান রয়েছে:

- `sync`, বর্তমান ব্যালেন্সগুলোতে রিজার্ভগুলো আপডেট করুন
- `skim`, অতিরিক্ত পরিমাণ উত্তোলন করুন। মনে রাখবেন যে যেকোনো অ্যাকাউন্টকে `skim` কল করার অনুমতি দেওয়া হয়েছে কারণ আমরা জানি না কে টোকেনগুলো জমা দিয়েছে। এই তথ্যটি একটি ইভেন্টে এমিট করা হয়, তবে ইভেন্টগুলো ব্লকচেইন থেকে অ্যাক্সেসযোগ্য নয়।

```solidity
    // ব্যালেন্সকে রিজার্ভের সাথে মেলাতে বাধ্য করে
    function skim(address to) external lock {
        address _token0 = token0; // গ্যাস সাশ্রয়
        address _token1 = token1; // গ্যাস সাশ্রয়
        _safeTransfer(_token0, to, IERC20(_token0).balanceOf(address(this)).sub(reserve0));
        _safeTransfer(_token1, to, IERC20(_token1).balanceOf(address(this)).sub(reserve1));
    }



    // রিজার্ভকে ব্যালেন্সের সাথে মেলাতে বাধ্য করে
    function sync() external lock {
        _update(IERC20(token0).balanceOf(address(this)), IERC20(token1).balanceOf(address(this)), reserve0, reserve1);
    }
}
```

### UniswapV2Factory.sol {#uniswapv2factory}

[এই কন্ট্রাক্টটি](https://github.com/Uniswap/uniswap-v2-core/blob/master/contracts/UniswapV2Factory.sol) পেয়ার এক্সচেঞ্জগুলো তৈরি করে।

```solidity
pragma solidity =0.5.16;

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

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

প্রোটোকল ফি বাস্তবায়নের জন্য এই স্টেট ভেরিয়েবলগুলো প্রয়োজনীয় ([শ্বেতপত্র](https://app.uniswap.org/whitepaper.pdf), পৃষ্ঠা 5 দেখুন)।
`feeTo` ঠিকানাটি প্রোটোকল ফি-এর জন্য তারল্য টোকেনগুলো জমা করে, এবং `feeToSetter` হলো সেই ঠিকানা যাকে `feeTo`-কে একটি ভিন্ন ঠিকানায় পরিবর্তন করার অনুমতি দেওয়া হয়েছে।

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

এই ভেরিয়েবলগুলো পেয়ারগুলোর, দুটি টোকেন প্রকারের মধ্যে এক্সচেঞ্জগুলোর ট্র্যাক রাখে।

প্রথমটি, `getPair`, হলো একটি ম্যাপিং যা একটি পেয়ার এক্সচেঞ্জ কন্ট্রাক্টকে এটি যে দুটি ERC-20 টোকেন সোয়াপ করে তার উপর ভিত্তি করে শনাক্ত করে। ERC-20 টোকেনগুলো সেই কন্ট্রাক্টগুলোর ঠিকানা দ্বারা শনাক্ত করা হয় যা সেগুলোকে বাস্তবায়ন করে, তাই কী (key) এবং মান (value) সবই হলো ঠিকানা। পেয়ার এক্সচেঞ্জের ঠিকানা পেতে যা আপনাকে `tokenA` থেকে `tokenB`-তে রূপান্তর করতে দেয়, আপনি `getPair[<tokenA address>][<tokenB address>]` ব্যবহার করেন (বা এর উল্টোটা)।

দ্বিতীয় ভেরিয়েবল, `allPairs`, হলো একটি অ্যারে যা এই ফ্যাক্টরি দ্বারা তৈরি পেয়ার এক্সচেঞ্জগুলোর সমস্ত ঠিকানা অন্তর্ভুক্ত করে। ইথেরিয়াম-এ আপনি একটি ম্যাপিংয়ের বিষয়বস্তুর উপর ইটারেট করতে পারবেন না, বা সমস্ত কী-এর একটি তালিকা পেতে পারবেন না, তাই এই ফ্যাক্টরিটি কোন এক্সচেঞ্জগুলো পরিচালনা করে তা জানার একমাত্র উপায় হলো এই ভেরিয়েবলটি।

দ্রষ্টব্য: আপনি একটি ম্যাপিংয়ের সমস্ত কী-এর উপর ইটারেট করতে না পারার কারণ হলো কন্ট্রাক্ট ডেটা স্টোরেজ _ব্যয়বহুল_, তাই আমরা এটি যত কম ব্যবহার করি তত ভালো, এবং আমরা এটি যত কম পরিবর্তন করি তত ভালো। আপনি [ইটারেশন সমর্থন করে এমন ম্যাপিং](https://github.com/ethereum/dapp-bin/blob/master/library/iterable_mapping.sol) তৈরি করতে পারেন, তবে সেগুলোর জন্য কী-এর একটি তালিকার জন্য অতিরিক্ত স্টোরেজ প্রয়োজন। বেশিরভাগ অ্যাপ্লিকেশনে আপনার সেটির প্রয়োজন নেই।

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

যখন একটি নতুন পেয়ার এক্সচেঞ্জ তৈরি করা হয় তখন এই ইভেন্টটি এমিট হয়। এটিতে টোকেনগুলোর ঠিকানা, পেয়ার এক্সচেঞ্জের ঠিকানা এবং ফ্যাক্টরি দ্বারা পরিচালিত এক্সচেঞ্জগুলোর মোট সংখ্যা অন্তর্ভুক্ত থাকে।

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

কনস্ট্রাক্টর একমাত্র যে কাজটি করে তা হলো `feeToSetter` নির্দিষ্ট করা। ফ্যাক্টরিগুলো কোনো ফি ছাড়াই শুরু হয়, এবং শুধুমাত্র `feeSetter` তা পরিবর্তন করতে পারে।

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

এই ফাংশনটি এক্সচেঞ্জ পেয়ারগুলোর সংখ্যা রিটার্ন করে।

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

এটি ফ্যাক্টরির প্রধান ফাংশন, দুটি ERC-20 টোকেনের মধ্যে একটি পেয়ার এক্সচেঞ্জ তৈরি করা। মনে রাখবেন যে যেকেউ এই ফাংশনটি কল করতে পারে। একটি নতুন পেয়ার এক্সচেঞ্জ তৈরি করতে আপনার ইউনিসোয়াপ থেকে অনুমতির প্রয়োজন নেই।

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

আমরা চাই নতুন এক্সচেঞ্জের ঠিকানাটি ডিটারমিনিস্টিক হোক, যাতে এটি অফচেইন-এ আগে থেকেই গণনা করা যায় (এটি [লেয়ার ২ (l2) ট্রানজ্যাকশনের](/developers/docs/scaling/) জন্য কার্যকর হতে পারে)।
এটি করার জন্য আমাদের টোকেন ঠিকানাগুলোর একটি সামঞ্জস্যপূর্ণ ক্রম থাকতে হবে, আমরা যে ক্রমেই সেগুলো পেয়ে থাকি না কেন, তাই আমরা সেগুলোকে এখানে সাজাই।

```solidity
        require(token0 != address(0), 'UniswapV2: ZERO_ADDRESS');
        require(getPair[token0][token1] == address(0), 'UniswapV2: PAIR_EXISTS'); // একটি মাত্র যাচাইই পর্যাপ্ত
```

বড় তারল্য পুলগুলো ছোটগুলোর চেয়ে ভালো, কারণ সেগুলোর মূল্য আরও স্থিতিশীল। আমরা প্রতি জোড়া টোকেনের জন্য একটির বেশি তারল্য পুল রাখতে চাই না। যদি আগে থেকেই একটি এক্সচেঞ্জ থাকে, তবে একই পেয়ারের জন্য আরেকটি তৈরি করার কোনো প্রয়োজন নেই।

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

একটি নতুন কন্ট্রাক্ট তৈরি করতে আমাদের সেই কোডটি প্রয়োজন যা এটি তৈরি করে (উভয় কনস্ট্রাক্টর ফাংশন এবং কোড যা প্রকৃত কন্ট্রাক্টের EVM বাইটকোড মেমরিতে লেখে)। সাধারণত Solidity-তে আমরা কেবল `addr = new <name of contract>(<constructor parameters>)` ব্যবহার করি এবং কম্পাইলার আমাদের জন্য সবকিছুর যত্ন নেয়, তবে একটি ডিটারমিনিস্টিক কন্ট্রাক্ট ঠিকানা পেতে আমাদের [CREATE2 অপকোড](https://eips.ethereum.org/EIPS/eip-1014) ব্যবহার করতে হবে।
যখন এই কোডটি লেখা হয়েছিল তখন সেই অপকোডটি এখনও Solidity দ্বারা সমর্থিত ছিল না, তাই ম্যানুয়ালি কোডটি পাওয়া প্রয়োজনীয় ছিল। এটি আর কোনো সমস্যা নয়, কারণ [Solidity এখন CREATE2 সমর্থন করে](https://docs.soliditylang.org/en/v0.8.3/control-structures.html#salted-contract-creations-create2)।

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

যখন কোনো অপকোড এখনও Solidity দ্বারা সমর্থিত হয় না তখন আমরা [ইনলাইন অ্যাসেম্বলি](https://docs.soliditylang.org/en/v0.8.3/assembly.html) ব্যবহার করে এটিকে কল করতে পারি।

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

নতুন এক্সচেঞ্জকে এটি কোন দুটি টোকেন সোয়াপ করে তা জানাতে `initialize` ফাংশনটি কল করুন।

```solidity
        getPair[token0][token1] = pair;
        getPair[token1][token0] = pair; // বিপরীত দিকে ম্যাপিং পপুলেট করে
        allPairs.push(pair);
        emit PairCreated(token0, token1, pair, allPairs.length);
    }
```

স্টেট ভেরিয়েবলগুলোতে নতুন পেয়ারের তথ্য সংরক্ষণ করুন এবং বিশ্বকে নতুন পেয়ার এক্সচেঞ্জ সম্পর্কে জানাতে একটি ইভেন্ট এমিট করুন।

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

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

এই দুটি ফাংশন `feeSetter`-কে ফি প্রাপক (যদি থাকে) নিয়ন্ত্রণ করতে এবং `feeSetter`-কে একটি নতুন ঠিকানায় পরিবর্তন করার অনুমতি দেয়।

### UniswapV2ERC20.sol {#uniswapv2erc20}

[এই কন্ট্রাক্টটি](https://github.com/Uniswap/uniswap-v2-core/blob/master/contracts/UniswapV2ERC20.sol) ERC-20 তারল্য টোকেন বাস্তবায়ন করে। এটি [ওপেনজেপেলিন ERC-20 কন্ট্রাক্টের](/developers/tutorials/erc20-annotated-code) মতো, তাই আমি কেবল সেই অংশটি ব্যাখ্যা করব যা আলাদা, `permit` কার্যকারিতা।

ইথেরিয়াম-এ ট্রানজ্যাকশনগুলোর জন্য ইথার (ETH) খরচ হয়, যা আসল অর্থের সমতুল্য। আপনার যদি ERC-20 টোকেন থাকে কিন্তু ETH না থাকে, তবে আপনি ট্রানজ্যাকশন পাঠাতে পারবেন না, তাই আপনি সেগুলো দিয়ে কিছুই করতে পারবেন না। এই সমস্যা এড়ানোর একটি সমাধান হলো [মেটা-ট্রানজ্যাকশন](https://docs.uniswap.org/contracts/v2/guides/smart-contract-integration/supporting-meta-transactions)।
টোকেনগুলোর মালিক এমন একটি ট্রানজ্যাকশনে স্বাক্ষর করে যা অন্য কাউকে অফচেইন টোকেন উত্তোলন করার অনুমতি দেয় এবং এটি ইন্টারনেট ব্যবহার করে প্রাপকের কাছে পাঠায়। প্রাপক, যার কাছে ETH আছে, তারপর মালিকের পক্ষে পারমিট জমা দেয়।

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

এই হ্যাশ হলো [ট্রানজ্যাকশন প্রকারের জন্য শনাক্তকারী](https://eips.ethereum.org/EIPS/eip-712#rationale-for-typehash)। আমরা এখানে শুধুমাত্র এই প্যারামিটারগুলোর সাথে `Permit` সমর্থন করি।

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

একজন প্রাপকের পক্ষে একটি ডিজিটাল স্বাক্ষর জাল করা সম্ভব নয়। তবে, একই ট্রানজ্যাকশন দুবার পাঠানো খুবই সহজ (এটি এক ধরনের [রিপ্লে অ্যাটাক](https://wikipedia.org/wiki/Replay_attack))। এটি প্রতিরোধ করতে, আমরা একটি [নন্স](https://wikipedia.org/wiki/Cryptographic_nonce) ব্যবহার করি। যদি একটি নতুন `Permit`-এর নন্স সর্বশেষ ব্যবহৃত নন্সের চেয়ে এক বেশি না হয়, তবে আমরা ধরে নিই যে এটি অবৈধ।

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

এটি [চেইন শনাক্তকারী](https://chainid.network/) পুনরুদ্ধার করার কোড। এটি [Yul](https://docs.soliditylang.org/en/v0.8.4/yul.html) নামক একটি EVM অ্যাসেম্বলি উপভাষা ব্যবহার করে। মনে রাখবেন যে Yul-এর বর্তমান সংস্করণে আপনাকে `chainid()` ব্যবহার করতে হবে, `chainid` নয়।

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

EIP-712-এর জন্য [ডোমেইন সেপারেটর](https://eips.ethereum.org/EIPS/eip-712#rationale-for-domainseparator) গণনা করুন।

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

এটি সেই ফাংশন যা অনুমতিগুলো বাস্তবায়ন করে। এটি প্যারামিটার হিসেবে প্রাসঙ্গিক ফিল্ডগুলো এবং [স্বাক্ষরের](https://yos.io/2018/11/16/ethereum-signatures/) জন্য তিনটি স্কেলার মান (v, r, এবং s) গ্রহণ করে।

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

সময়সীমার পরে ট্রানজ্যাকশনগুলো গ্রহণ করবেন না।

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

`abi.encodePacked(...)` হলো সেই বার্তা যা আমরা পাওয়ার আশা করি। আমরা জানি নন্স কী হওয়া উচিত, তাই আমাদের এটি প্যারামিটার হিসেবে পাওয়ার কোনো প্রয়োজন নেই।

ইথেরিয়াম স্বাক্ষর অ্যালগরিদম স্বাক্ষর করার জন্য 256 বিট পাওয়ার আশা করে, তাই আমরা `keccak256` হ্যাশ ফাংশন ব্যবহার করি।

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

ডাইজেস্ট এবং স্বাক্ষর থেকে আমরা [ecrecover](https://coders-errand.com/ecrecover-signature-verification-ethereum/) ব্যবহার করে সেই ঠিকানাটি পেতে পারি যা এটিতে স্বাক্ষর করেছে।

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

```

যদি সবকিছু ঠিক থাকে, তবে এটিকে [একটি ERC-20 অনুমোদন করা](https://eips.ethereum.org/EIPS/eip-20#approve) হিসেবে বিবেচনা করুন।

## পেরিফেরি কন্ট্রাক্ট {#periphery-contracts}

পেরিফেরি কন্ট্রাক্টগুলো হলো ইউনিসোয়াপ-এর API (অ্যাপ্লিকেশন প্রোগ্রাম ইন্টারফেস)। এগুলো অন্যান্য কন্ট্রাক্ট বা বিকেন্দ্রীকৃত অ্যাপ্লিকেশন (dapp) থেকে এক্সটার্নাল কলের জন্য উপলব্ধ। আপনি সরাসরি কোর কন্ট্রাক্টগুলোতে কল করতে পারেন, তবে এটি আরও জটিল এবং ভুল করলে আপনার ভ্যালু হারাতে পারে। কোর কন্ট্রাক্টগুলোতে কেবল এমন টেস্ট থাকে যা নিশ্চিত করে যে তাদের সাথে প্রতারণা করা হচ্ছে না, অন্য কারও জন্য কোনো স্যানিটি চেক থাকে না। সেগুলো পেরিফেরিতে থাকে যাতে প্রয়োজন অনুযায়ী আপডেট করা যায়।

### UniswapV2Router01.sol {#uniswapv2router01}

[এই কন্ট্রাক্টে](https://github.com/Uniswap/uniswap-v2-periphery/blob/master/contracts/UniswapV2Router01.sol) সমস্যা রয়েছে এবং [এটি আর ব্যবহার করা উচিত নয়](https://docs.uniswap.org/contracts/v2/reference/smart-contracts/router-01)। সৌভাগ্যবশত, পেরিফেরি কন্ট্রাক্টগুলো স্টেটলেস এবং কোনো সম্পদ ধারণ করে না, তাই এটিকে বাতিল করা এবং এর পরিবর্তে মানুষকে `UniswapV2Router02` ব্যবহার করার পরামর্শ দেওয়া সহজ।

### UniswapV2Router02.sol {#uniswapv2router02}

বেশিরভাগ ক্ষেত্রে আপনি [এই কন্ট্রাক্টের](https://github.com/Uniswap/uniswap-v2-periphery/blob/master/contracts/UniswapV2Router02.sol) মাধ্যমে ইউনিসোয়াপ ব্যবহার করবেন।
আপনি [এখানে](https://docs.uniswap.org/contracts/v2/reference/smart-contracts/router-02) দেখতে পারেন কীভাবে এটি ব্যবহার করতে হয়।

```solidity
pragma solidity =0.6.6;

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

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

এগুলোর বেশিরভাগই আমরা আগে দেখেছি, বা বেশ স্পষ্ট। একমাত্র ব্যতিক্রম হলো `IWETH.sol`। ইউনিসোয়াপ v2 যেকোনো জোড়া ERC-20 টোকেনের জন্য এক্সচেঞ্জের অনুমতি দেয়, কিন্তু ইথার (ETH) নিজে কোনো ERC-20 টোকেন নয়। এটি স্ট্যান্ডার্ডের আগের এবং অনন্য মেকানিজমের মাধ্যমে হস্তান্তর করা হয়। ERC-20 টোকেনের ক্ষেত্রে প্রযোজ্য কন্ট্রাক্টগুলোতে ETH-এর ব্যবহার সক্ষম করতে মানুষ [র‍্যাপড ইথার (weth)](https://weth.tkn.eth.limo/) কন্ট্রাক্ট তৈরি করেছে। আপনি এই কন্ট্রাক্টে ETH পাঠান, এবং এটি আপনার জন্য সমপরিমাণ WETH মিন্ট করে। অথবা আপনি WETH পোড়াতে পারেন, এবং ফেরত হিসেবে ETH পেতে পারেন।

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

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

রাউটারকে জানতে হবে কোন ফ্যাক্টরি ব্যবহার করতে হবে, এবং যেসব ট্রানজ্যাকশনে WETH প্রয়োজন সেগুলোর জন্য কোন WETH কন্ট্রাক্ট ব্যবহার করতে হবে। এই মানগুলো [অপরিবর্তনীয়](https://docs.soliditylang.org/en/v0.8.3/contracts.html#constant-and-immutable-state-variables), যার মানে এগুলো কেবল কনস্ট্রাক্টরে সেট করা যেতে পারে। এটি ব্যবহারকারীদের এই আত্মবিশ্বাস দেয় যে কেউ এগুলো পরিবর্তন করে কম সৎ কন্ট্রাক্টের দিকে নির্দেশ করতে পারবে না।

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

এই মডিফায়ার নিশ্চিত করে যে সময়-সীমিত ট্রানজ্যাকশনগুলো ("যদি পারো তবে Y সময়ের আগে X করো") তাদের সময়সীমার পরে ঘটবে না।

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

কনস্ট্রাক্টর কেবল অপরিবর্তনীয় স্টেট ভেরিয়েবলগুলো সেট করে।

```solidity
    receive() external payable {
        assert(msg.sender == WETH); // শুধুমাত্র WETH কন্ট্রাক্ট থেকে ফলব্যাকের মাধ্যমে ETH গ্রহণ করে
    }
```

যখন আমরা WETH কন্ট্রাক্ট থেকে টোকেন রিডিম করে আবার ETH-এ রূপান্তর করি তখন এই ফাংশনটি কল করা হয়। কেবল আমরা যে WETH কন্ট্রাক্ট ব্যবহার করি তারই এটি করার অনুমোদন রয়েছে।

#### তারল্য যোগ করা {#add-liquidity}

এই ফাংশনগুলো পেয়ার এক্সচেঞ্জে টোকেন যোগ করে, যা তারল্য পুল বৃদ্ধি করে।

```solidity

    // **** তারল্য যুক্ত করুন ****
    function _addLiquidity(
```

এই ফাংশনটি পেয়ার এক্সচেঞ্জে জমা করার জন্য A এবং B টোকেনের পরিমাণ গণনা করতে ব্যবহৃত হয়।

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

এগুলো হলো ERC-20 টোকেন কন্ট্রাক্টের ঠিকানা।

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

এগুলো হলো সেই পরিমাণ যা তারল্য প্রদানকারী জমা করতে চান। এগুলো জমা করার জন্য A এবং B-এর সর্বোচ্চ পরিমাণও বটে।

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

এগুলো হলো জমা করার জন্য ন্যূনতম গ্রহণযোগ্য পরিমাণ। যদি এই পরিমাণ বা তার বেশি দিয়ে ট্রানজ্যাকশন সম্পন্ন করা না যায়, তবে এটি রিভার্ট করুন। আপনি যদি এই বৈশিষ্ট্যটি না চান, তবে কেবল শূন্য (0) উল্লেখ করুন।

তারল্য প্রদানকারীরা সাধারণত একটি ন্যূনতম পরিমাণ নির্দিষ্ট করেন, কারণ তারা ট্রানজ্যাকশনটিকে বর্তমান বিনিময় হারের কাছাকাছি একটি হারে সীমাবদ্ধ করতে চান। যদি বিনিময় হার খুব বেশি ওঠানামা করে তবে এর অর্থ হতে পারে এমন কোনো খবর যা অন্তর্নিহিত মান পরিবর্তন করে, এবং তারা ম্যানুয়ালি সিদ্ধান্ত নিতে চান কী করতে হবে।

উদাহরণস্বরূপ, এমন একটি পরিস্থিতি কল্পনা করুন যেখানে বিনিময় হার এক-এর বিপরীতে এক এবং তারল্য প্রদানকারী এই মানগুলো নির্দিষ্ট করেন:

| 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);
        // ডাস্ট eth থাকলে তা ফেরত দেয়
        if (msg.value > amountETH) TransferHelper.safeTransferETH(msg.sender, msg.value - amountETH);
    }
```

ব্যবহারকারী ইতিমধ্যেই আমাদের ETH পাঠিয়েছেন, তাই যদি কোনো অতিরিক্ত অবশিষ্ট থাকে (কারণ অন্য টোকেনটি ব্যবহারকারীর ধারণার চেয়ে কম মূল্যবান), তবে আমাদের একটি রিফান্ড ইস্যু করতে হবে।

#### তারল্য অপসারণ {#remove-liquidity}

এই ফাংশনগুলো তারল্য অপসারণ করবে এবং তারল্য প্রদানকারীকে অর্থ ফেরত দেবে।

```solidity
    // **** তারল্য অপসারণ করুন ****
    function removeLiquidity(
        address tokenA,
        address tokenB,
        uint liquidity,
        uint amountAMin,
        uint amountBMin,
        address to,
        uint deadline
    ) public virtual override ensure(deadline) returns (uint amountA, uint amountB) {
```

তারল্য অপসারণের সবচেয়ে সহজ ক্ষেত্র। প্রতিটি টোকেনের একটি ন্যূনতম পরিমাণ রয়েছে যা তারল্য প্রদানকারী গ্রহণ করতে সম্মত হন, এবং এটি অবশ্যই সময়সীমার আগে ঘটতে হবে।

```solidity
        address pair = UniswapV2Library.pairFor(factory, tokenA, tokenB);
        IUniswapV2Pair(pair).transferFrom(msg.sender, pair, liquidity); // পেয়ারে তারল্য পাঠায়
        (uint amount0, uint amount1) = IUniswapV2Pair(pair).burn(to);
```

কোর কন্ট্রাক্টের `burn` ফাংশন ব্যবহারকারীকে টোকেন ফেরত দেওয়ার বিষয়টি পরিচালনা করে।

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

যখন একটি ফাংশন একাধিক মান রিটার্ন করে, কিন্তু আমরা কেবল সেগুলোর কয়েকটিতে আগ্রহী থাকি, তখন আমরা এভাবেই কেবল সেই মানগুলো পাই। একটি মান পড়া এবং তা কখনো ব্যবহার না করার চেয়ে গ্যাসের দিক থেকে এটি কিছুটা সস্তা।

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

কোর কন্ট্রাক্ট যেভাবে পরিমাণগুলো রিটার্ন করে (প্রথমে ছোট ঠিকানার টোকেন) তা থেকে ব্যবহারকারী যেভাবে সেগুলো আশা করে (যা `tokenA` এবং `tokenB` এর সাথে মিলে যায়) সেভাবে রূপান্তর করুন।

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

প্রথমে হস্তান্তর করা এবং তারপর এটি বৈধ কিনা তা যাচাই করা ঠিক আছে, কারণ যদি তা না হয় তবে আমরা সমস্ত স্টেট পরিবর্তন থেকে রিভার্ট করব।

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

ETH-এর জন্য তারল্য অপসারণ প্রায় একই রকম, শুধু পার্থক্য হলো আমরা WETH টোকেনগুলো গ্রহণ করি এবং তারপর তারল্য প্রদানকারীকে ফেরত দেওয়ার জন্য সেগুলোকে ETH-এ রিডিম করি।

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


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

এই ফাংশনগুলো মেটা-ট্রানজ্যাকশন রিলে করে যাতে ইথার ছাড়া ব্যবহারকারীরা [পারমিট মেকানিজম](#uniswapv2erc20) ব্যবহার করে পুল থেকে উত্তোলন করতে পারে।

```solidity

    // **** তারল্য অপসারণ করুন (ফি-অন-ট্রান্সফার টোকেন সমর্থন করে) ****
    function removeLiquidityETHSupportingFeeOnTransferTokens(
        address token,
        uint liquidity,
        uint amountTokenMin,
        uint amountETHMin,
        address to,
        uint deadline
    ) public virtual override ensure(deadline) returns (uint amountETH) {
        (, amountETH) = removeLiquidity(
            token,
            WETH,
            liquidity,
            amountTokenMin,
            amountETHMin,
            address(this),
            deadline
        );
        TransferHelper.safeTransfer(token, to, IERC20(token).balanceOf(address(this)));
        IWETH(WETH).withdraw(amountETH);
        TransferHelper.safeTransferETH(to, amountETH);
    }

```

এই ফাংশনটি এমন টোকেনগুলোর জন্য ব্যবহার করা যেতে পারে যেগুলোর হস্তান্তর বা স্টোরেজ ফি রয়েছে। যখন কোনো টোকেনের এমন ফি থাকে তখন আমরা কতটা টোকেন ফেরত পাব তা জানার জন্য `removeLiquidity` ফাংশনের উপর নির্ভর করতে পারি না, তাই আমাদের প্রথমে উত্তোলন করতে হবে এবং তারপর ব্যালেন্স পেতে হবে।

```solidity


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

চূড়ান্ত ফাংশনটি মেটা-ট্রানজ্যাকশনের সাথে স্টোরেজ ফি যুক্ত করে।

#### ট্রেড {#trade}

```solidity
    // **** সোয়াপ ****
    // প্রাথমিক পরিমাণটি ইতিমধ্যে প্রথম পেয়ারে পাঠানো হয়েছে এমনটা প্রয়োজন
    function _swap(uint[] memory amounts, address[] memory path, address _to) internal virtual {
```

এই ফাংশনটি অভ্যন্তরীণ প্রসেসিং সম্পাদন করে যা ট্রেডারদের জন্য উন্মুক্ত ফাংশনগুলোর জন্য প্রয়োজনীয়।

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

আমি যখন এটি লিখছি তখন [388,160টি ERC-20 টোকেন](https://eth.blockscout.com/tokens) রয়েছে। যদি প্রতিটি টোকেন জোড়ার জন্য একটি পেয়ার এক্সচেঞ্জ থাকত, তবে এটি 150 বিলিয়নেরও বেশি পেয়ার এক্সচেঞ্জ হতো। এই মুহূর্তে পুরো চেইনে [অ্যাকাউন্টের সংখ্যা এর মাত্র 0.1%](https://eth.blockscout.com/stats/accountsGrowth)। এর পরিবর্তে, সোয়াপ ফাংশনগুলো একটি পাথের ধারণা সমর্থন করে। একজন ট্রেডার A-কে B-এর জন্য, B-কে C-এর জন্য এবং C-কে D-এর জন্য এক্সচেঞ্জ করতে পারেন, তাই সরাসরি A-D পেয়ার এক্সচেঞ্জের কোনো প্রয়োজন নেই।

এই মার্কেটগুলোতে দামগুলো সিঙ্ক্রোনাইজড থাকে, কারণ যখন সেগুলো সিঙ্কের বাইরে থাকে তখন এটি আর্বিট্রেজের সুযোগ তৈরি করে। উদাহরণস্বরূপ, তিনটি টোকেন 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 এক্সচেঞ্জ                | B-C এক্সচেঞ্জ                | A-C এক্সচেঞ্জ                |
| ---- | --------------------------- | --------------------------- | --------------------------- |
| 1    | A:1000 B:1050 A/B=1.05      | B:1000 C:1050 B/C=1.05      | A:1050 C:1000 C/A=1.05      |
| 2    | A:1024.695 B:1024.695 A/B=1 | B:1000 C:1050 B/C=1.05      | A:1050 C:1000 C/A=1.05      |
| 3    | A:1024.695 B:1024.695 A/B=1 | B:1024.695 C:1024.695 B/C=1 | A:1050 C:1000 C/A=1.05      |
| 4    | A:1024.695 B:1024.695 A/B=1 | B:1024.695 C:1024.695 B/C=1 | A:1024.695 C:1024.695 C/A=1 |

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

আমরা বর্তমানে যে পেয়ারটি পরিচালনা করছি তা পান, এটি সাজান (পেয়ারের সাথে ব্যবহারের জন্য) এবং প্রত্যাশিত আউটপুট পরিমাণ পান।

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

প্রত্যাশিত আউট পরিমাণগুলো পান, পেয়ার এক্সচেঞ্জ যেভাবে আশা করে সেভাবে সাজানো।

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

এটি কি শেষ এক্সচেঞ্জ? যদি তাই হয়, তবে ট্রেডের জন্য প্রাপ্ত টোকেনগুলো গন্তব্যে পাঠান। যদি না হয়, তবে এটি পরবর্তী পেয়ার এক্সচেঞ্জে পাঠান।

```solidity

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

টোকেনগুলো সোয়াপ করার জন্য প্রকৃতপক্ষে পেয়ার এক্সচেঞ্জকে কল করুন। এক্সচেঞ্জ সম্পর্কে জানানোর জন্য আমাদের কোনো কলব্যাকের প্রয়োজন নেই, তাই আমরা সেই ফিল্ডে কোনো বাইট পাঠাই না।

```solidity
    function swapExactTokensForTokens(
```

এই ফাংশনটি সরাসরি ট্রেডারদের দ্বারা একটি টোকেন অন্যটির সাথে সোয়াপ করতে ব্যবহৃত হয়।

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

এই প্যারামিটারে ERC-20 কন্ট্রাক্টগুলোর ঠিকানা থাকে। উপরে যেমন ব্যাখ্যা করা হয়েছে, এটি একটি অ্যারে কারণ আপনার কাছে থাকা সম্পদ থেকে আপনার কাঙ্ক্ষিত সম্পদে পৌঁছানোর জন্য আপনাকে বেশ কয়েকটি পেয়ার এক্সচেঞ্জের মধ্য দিয়ে যেতে হতে পারে।

Solidity-তে একটি ফাংশন প্যারামিটার `memory` বা `calldata`-এ সংরক্ষণ করা যেতে পারে। যদি ফাংশনটি কন্ট্রাক্টের একটি এন্ট্রি পয়েন্ট হয়, যা সরাসরি কোনো ব্যবহারকারী (একটি ট্রানজ্যাকশন ব্যবহার করে) বা অন্য কোনো কন্ট্রাক্ট থেকে কল করা হয়, তবে প্যারামিটারের মান সরাসরি কল ডেটা থেকে নেওয়া যেতে পারে। যদি ফাংশনটি অভ্যন্তরীণভাবে কল করা হয়, যেমন উপরের `_swap`, তবে প্যারামিটারগুলো `memory`-এ সংরক্ষণ করতে হবে। কল করা কন্ট্রাক্টের দৃষ্টিকোণ থেকে `calldata` হলো রিড অনলি।

`uint` বা `address` এর মতো স্কেলার টাইপের ক্ষেত্রে কম্পাইলার আমাদের জন্য স্টোরেজ নির্বাচনের বিষয়টি পরিচালনা করে, কিন্তু অ্যারেগুলোর ক্ষেত্রে, যা দীর্ঘতর এবং বেশি ব্যয়বহুল, আমরা কোন ধরনের স্টোরেজ ব্যবহার করা হবে তা নির্দিষ্ট করে দিই।

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

রিটার্ন মানগুলো সর্বদা মেমোরিতে রিটার্ন করা হয়।

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

প্রতিটি সোয়াপে ক্রয় করার পরিমাণ গণনা করুন। যদি ফলাফলটি ট্রেডারের গ্রহণ করতে ইচ্ছুক ন্যূনতম পরিমাণের চেয়ে কম হয়, তবে ট্রানজ্যাকশন থেকে রিভার্ট করুন।

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

অবশেষে, প্রথম পেয়ার এক্সচেঞ্জের জন্য অ্যাকাউন্টে প্রাথমিক ERC-20 টোকেন হস্তান্তর করুন এবং `_swap` কল করুন। এটি সবই একই ট্রানজ্যাকশনে ঘটছে, তাই পেয়ার এক্সচেঞ্জ জানে যে কোনো অপ্রত্যাশিত টোকেন এই হস্তান্তরের অংশ।

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

আগের ফাংশন, `swapTokensForTokens`, একজন ট্রেডারকে তিনি ঠিক কতগুলো ইনপুট টোকেন দিতে ইচ্ছুক এবং বিনিময়ে তিনি ন্যূনতম কতগুলো আউটপুট টোকেন পেতে ইচ্ছুক তা নির্দিষ্ট করার অনুমতি দেয়। এই ফাংশনটি বিপরীত সোয়াপ করে, এটি একজন ট্রেডারকে তিনি কতগুলো আউটপুট টোকেন চান এবং সেগুলোর জন্য তিনি সর্বোচ্চ কতগুলো ইনপুট টোকেন দিতে ইচ্ছুক তা নির্দিষ্ট করতে দেয়।

উভয় ক্ষেত্রেই, ট্রেডারকে প্রথমে এই পেরিফেরি কন্ট্রাক্টকে একটি অ্যালাউন্স দিতে হবে যাতে এটি সেগুলোকে হস্তান্তর করতে পারে।

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


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



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


    function swapETHForExactTokens(uint amountOut, address[] calldata path, address to, uint deadline)
        external
        virtual
        override
        payable
        ensure(deadline)
        returns (uint[] memory amounts)
    {
        require(path[0] == WETH, 'UniswapV2Router: INVALID_PATH');
        amounts = UniswapV2Library.getAmountsIn(factory, amountOut, path);
        require(amounts[0] <= msg.value, 'UniswapV2Router: EXCESSIVE_INPUT_AMOUNT');
        IWETH(WETH).deposit{value: amounts[0]}();
        assert(IWETH(WETH).transfer(UniswapV2Library.pairFor(factory, path[0], path[1]), amounts[0]));
        _swap(amounts, path, to);
        // ডাস্ট eth থাকলে তা ফেরত দেয়
        if (msg.value > amounts[0]) TransferHelper.safeTransferETH(msg.sender, msg.value - amounts[0]);
    }
```

এই চারটি ভেরিয়েন্টই ETH এবং টোকেনের মধ্যে ট্রেডিংয়ের সাথে জড়িত। একমাত্র পার্থক্য হলো আমরা হয় ট্রেডারের কাছ থেকে ETH গ্রহণ করি এবং এটি WETH মিন্ট করতে ব্যবহার করি, অথবা আমরা পাথের শেষ এক্সচেঞ্জ থেকে WETH গ্রহণ করি এবং এটি পুড়িয়ে ফেলি, যার ফলে প্রাপ্ত ETH ট্রেডারকে ফেরত পাঠাই।

```solidity
    // **** সোয়াপ (ফি-অন-ট্রান্সফার টোকেন সমর্থন করে) ****
    // প্রাথমিক পরিমাণটি ইতিমধ্যে প্রথম পেয়ারে পাঠানো হয়েছে এমনটা প্রয়োজন
    function _swapSupportingFeeOnTransferTokens(address[] memory path, address _to) internal virtual {
```

এটি হলো হস্তান্তর বা স্টোরেজ ফি থাকা টোকেনগুলো সোয়াপ করার অভ্যন্তরীণ ফাংশন যা ([এই সমস্যাটি](https://github.com/Uniswap/uniswap-interface/issues/835)) সমাধান করে।

```solidity
        for (uint i; i < path.length - 1; i++) {
            (address input, address output) = (path[i], path[i + 1]);
            (address token0,) = UniswapV2Library.sortTokens(input, output);
            IUniswapV2Pair pair = IUniswapV2Pair(UniswapV2Library.pairFor(factory, input, output));
            uint amountInput;
            uint amountOutput;
            { // স্ট্যাক টু ডিপ (stack too deep) এরর এড়াতে স্কোপ
            (uint reserve0, uint reserve1,) = pair.getReserves();
            (uint reserveInput, uint reserveOutput) = input == token0 ? (reserve0, reserve1) : (reserve1, reserve0);
            amountInput = IERC20(input).balanceOf(address(pair)).sub(reserveInput);
            amountOutput = UniswapV2Library.getAmountOut(amountInput, reserveInput, reserveOutput);
```

হস্তান্তর ফির কারণে আমরা প্রতিটি হস্তান্তর থেকে কতটা পাব তা জানার জন্য `getAmountsOut` ফাংশনের উপর নির্ভর করতে পারি না (যেমনটা আমরা আসল `_swap` কল করার আগে করি)। এর পরিবর্তে আমাদের প্রথমে হস্তান্তর করতে হবে এবং তারপর দেখতে হবে আমরা কতগুলো টোকেন ফেরত পেয়েছি।

দ্রষ্টব্য: তাত্ত্বিকভাবে আমরা `_swap` এর পরিবর্তে কেবল এই ফাংশনটি ব্যবহার করতে পারতাম, কিন্তু নির্দিষ্ট কিছু ক্ষেত্রে (উদাহরণস্বরূপ, যদি হস্তান্তরটি শেষ পর্যন্ত রিভার্ট হয়ে যায় কারণ প্রয়োজনীয় ন্যূনতম পরিমাণ পূরণের জন্য শেষে পর্যাপ্ত পরিমাণ নেই) এতে বেশি গ্যাস খরচ হবে। ট্রান্সফার ফি টোকেনগুলো বেশ বিরল, তাই যদিও আমাদের সেগুলোকে সামঞ্জস্য করতে হবে, তবে সমস্ত সোয়াপের ক্ষেত্রে ধরে নেওয়ার কোনো প্রয়োজন নেই যে সেগুলো অন্তত একটির মধ্য দিয়ে যাবে।

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


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


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


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

এগুলো সাধারণ টোকেনের জন্য ব্যবহৃত একই ভেরিয়েন্ট, তবে এগুলো এর পরিবর্তে `_swapSupportingFeeOnTransferTokens` কল করে।

```solidity
    // **** লাইব্রেরি ফাংশনসমূহ ****
    function quote(uint amountA, uint reserveA, uint reserveB) public pure virtual override returns (uint amountB) {
        return UniswapV2Library.quote(amountA, reserveA, reserveB);
    }

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

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

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

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

এই ফাংশনগুলো কেবল প্রক্সি যা [UniswapV2Library ফাংশনগুলোকে](#uniswapv2library) কল করে।

### UniswapV2Migrator.sol {#uniswapv2migrator}

এই কন্ট্রাক্টটি পুরানো v1 থেকে v2-তে এক্সচেঞ্জগুলো মাইগ্রেট করতে ব্যবহৃত হয়েছিল। যেহেতু সেগুলো মাইগ্রেট করা হয়েছে, তাই এটি আর প্রাসঙ্গিক নয়।

## লাইব্রেরি {#libraries}

[SafeMath লাইব্রেরি](https://docs.openzeppelin.com/contracts/2.x/api/math) ভালোভাবে ডকুমেন্টেড, তাই এখানে এটি ডকুমেন্ট করার কোনো প্রয়োজন নেই।

### Math {#math}

এই লাইব্রেরিতে কিছু গাণিতিক ফাংশন রয়েছে যা সাধারণত Solidity কোডে প্রয়োজন হয় না, তাই এগুলো এই ভাষার অংশ নয়।

```solidity
pragma solidity =0.5.16;

// বিভিন্ন গাণিতিক কাজ করার জন্য একটি লাইব্রেরি

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

    // ব্যাবিলনীয় পদ্ধতি (https://wikipedia.org/wiki/Methods_of_computing_square_roots#Babylonian_method)
    function sqrt(uint y) internal pure returns (uint z) {
        if (y > 3) {
            z = y;
            uint x = y / 2 + 1;
```

x-কে এমন একটি আনুমানিক মান হিসেবে ধরে শুরু করুন যা বর্গমূলের চেয়ে বেশি (এ কারণেই আমাদের 1-3 কে বিশেষ ক্ষেত্র হিসেবে বিবেচনা করতে হবে)।

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

আরও কাছাকাছি একটি আনুমানিক মান বের করুন, যা হলো পূর্ববর্তী আনুমানিক মান এবং যে সংখ্যার বর্গমূল আমরা বের করার চেষ্টা করছি তাকে পূর্ববর্তী আনুমানিক মান দিয়ে ভাগ করে পাওয়া ফলের গড়। নতুন আনুমানিক মানটি বিদ্যমান মানের চেয়ে কম না হওয়া পর্যন্ত পুনরাবৃত্তি করুন। আরও বিস্তারিত জানতে, [এখানে দেখুন](https://wikipedia.org/wiki/Methods_of_computing_square_roots#Babylonian_method)।

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

আমাদের কখনোই শূন্যের বর্গমূলের প্রয়োজন হবে না। এক, দুই এবং তিনের বর্গমূল প্রায় এক (আমরা পূর্ণসংখ্যা ব্যবহার করি, তাই ভগ্নাংশকে উপেক্ষা করি)।

```solidity
        }
    }
}
```

### ফিক্সড পয়েন্ট ফ্র্যাকশন (UQ112x112) {#fixedpoint}

এই লাইব্রেরিটি ভগ্নাংশ পরিচালনা করে, যা সাধারণত ইথেরিয়াম পাটিগণিতের অংশ নয়। এটি _x_ সংখ্যাটিকে _x\*2^112_ হিসেবে এনকোড করে এই কাজটি করে। এটি আমাদের কোনো পরিবর্তন ছাড়াই মূল যোগ এবং বিয়োগ অপকোডগুলো ব্যবহার করতে দেয়।

```solidity
pragma solidity =0.5.16;

// বাইনারি ফিক্সড পয়েন্ট নম্বর হ্যান্ডেল করার জন্য একটি লাইব্রেরি (https://wikipedia.org/wiki/Q_(number_format))

// রেঞ্জ: [0, 2**112 - 1]
// রেজোলিউশন: 1 / 2**112

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

`Q112` হলো একের জন্য এনকোডিং।

```solidity
    // একটি uint112 কে UQ112x112 হিসেবে এনকোড করে
    function encode(uint112 y) internal pure returns (uint224 z) {
        z = uint224(y) * Q112; // কখনও ওভারফ্লো হয় না
    }
```

যেহেতু y হলো `uint112`, তাই এর সর্বোচ্চ মান হতে পারে 2^112-1। সেই সংখ্যাটিকে এখনও `UQ112x112` হিসেবে এনকোড করা যেতে পারে।

```solidity
    // একটি UQ112x112 কে uint112 দিয়ে ভাগ করে, একটি UQ112x112 রিটার্ন করে
    function uqdiv(uint224 x, uint112 y) internal pure returns (uint224 z) {
        z = x / uint224(y);
    }
}
```

যদি আমরা দুটি `UQ112x112` মানকে ভাগ করি, তবে ফলাফলটি আর 2^112 দ্বারা গুণিত থাকে না। তাই এর পরিবর্তে আমরা হরের জন্য একটি পূর্ণসংখ্যা নিই। গুণের ক্ষেত্রেও আমাদের একই ধরনের কৌশল ব্যবহার করার প্রয়োজন হতো, কিন্তু আমাদের `UQ112x112` মানগুলোর গুণ করার প্রয়োজন নেই।

### UniswapV2Library {#uniswapv2library}

এই লাইব্রেরিটি শুধুমাত্র পেরিফেরি কন্ট্রাক্টগুলো ব্যবহার করে

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

যদি কোনো ফি জড়িত না থাকে, তবে টোকেন A এর বিনিময়ে আপনি কী পরিমাণ টোকেন B পাবেন তা এই ফাংশনটি আপনাকে দেয়। এই গণনাটি বিবেচনায় নেয় যে হস্তান্তরটি বিনিময় হার পরিবর্তন করে।

```solidity
    // একটি অ্যাসেটের ইনপুট পরিমাণ এবং পেয়ার রিজার্ভ দেওয়া হলে, অন্য অ্যাসেটের সর্বোচ্চ আউটপুট পরিমাণ রিটার্ন করে
    function getAmountOut(uint amountIn, uint reserveIn, uint reserveOut) internal pure returns (uint amountOut) {
```

উপরের `quote` ফাংশনটি দারুণ কাজ করে যদি পেয়ার এক্সচেঞ্জ ব্যবহার করার জন্য কোনো ফি না থাকে। তবে, যদি 0.3% এক্সচেঞ্জ ফি থাকে তবে আপনি আসলে যে পরিমাণ পাবেন তা কম হবে। এই ফাংশনটি এক্সচেঞ্জ ফির পরের পরিমাণ গণনা করে।

```solidity

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

Solidity স্থানীয়ভাবে ভগ্নাংশ পরিচালনা করে না, তাই আমরা কেবল পরিমাণটিকে 0.997 দিয়ে গুণ করতে পারি না। এর পরিবর্তে, আমরা লবকে 997 দিয়ে এবং হরকে 1000 দিয়ে গুণ করি, যা একই ফলাফল দেয়।

```solidity
    // একটি অ্যাসেটের আউটপুট পরিমাণ এবং পেয়ার রিজার্ভ দেওয়া হলে, অন্য অ্যাসেটের প্রয়োজনীয় ইনপুট পরিমাণ রিটার্ন করে
    function getAmountIn(uint amountOut, uint reserveIn, uint reserveOut) internal pure returns (uint amountIn) {
        require(amountOut > 0, 'UniswapV2Library: INSUFFICIENT_OUTPUT_AMOUNT');
        require(reserveIn > 0 && reserveOut > 0, 'UniswapV2Library: INSUFFICIENT_LIQUIDITY');
        uint numerator = reserveIn.mul(amountOut).mul(1000);
        uint denominator = reserveOut.sub(amountOut).mul(997);
        amountIn = (numerator / denominator).add(1);
    }
```

এই ফাংশনটি প্রায় একই কাজ করে, তবে এটি আউটপুটের পরিমাণ পায় এবং ইনপুট প্রদান করে।

```solidity

    // যেকোনো সংখ্যক পেয়ারে চেইনড getAmountOut গণনা সম্পাদন করে
    function getAmountsOut(address factory, uint amountIn, address[] memory path) internal view returns (uint[] memory amounts) {
        require(path.length >= 2, 'UniswapV2Library: INVALID_PATH');
        amounts = new uint[](path.length);
        amounts[0] = amountIn;
        for (uint i; i < path.length - 1; i++) {
            (uint reserveIn, uint reserveOut) = getReserves(factory, path[i], path[i + 1]);
            amounts[i + 1] = getAmountOut(amounts[i], reserveIn, reserveOut);
        }
    }

    // যেকোনো সংখ্যক পেয়ারে চেইনড getAmountIn গণনা সম্পাদন করে
    function getAmountsIn(address factory, uint amountOut, address[] memory path) internal view returns (uint[] memory amounts) {
        require(path.length >= 2, 'UniswapV2Library: INVALID_PATH');
        amounts = new uint[](path.length);
        amounts[amounts.length - 1] = amountOut;
        for (uint i = path.length - 1; i > 0; i--) {
            (uint reserveIn, uint reserveOut) = getReserves(factory, path[i - 1], path[i]);
            amounts[i - 1] = getAmountIn(amounts[i], reserveIn, reserveOut);
        }
    }
}
```

যখন একাধিক পেয়ার এক্সচেঞ্জের মধ্য দিয়ে যাওয়ার প্রয়োজন হয়, তখন এই দুটি ফাংশন মানগুলো শনাক্ত করার কাজ পরিচালনা করে।

### ট্রান্সফার হেল্পার {#transfer-helper}

[এই লাইব্রেরিটি](https://github.com/Uniswap/uniswap-lib/blob/master/contracts/libraries/TransferHelper.sol) ERC-20 এবং ইথেরিয়াম হস্তান্তরের চারপাশে সফলতার চেক যোগ করে যাতে একটি রিভার্ট এবং একটি `false` মান ফেরত আসাকে একইভাবে বিবেচনা করা যায়।

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

pragma solidity >=0.6.0;

// ERC-20 টোকেন এর সাথে ইন্টারঅ্যাক্ট করার এবং ETH পাঠানোর জন্য হেল্পার মেথড যা ধারাবাহিকভাবে true/false রিটার্ন করে না
library TransferHelper {
    function safeApprove(
        address token,
        address to,
        uint256 value
    ) internal {
        // bytes4(keccak256(bytes('approve(address,uint256)')));
        (bool success, bytes memory data) = token.call(abi.encodeWithSelector(0x095ea7b3, to, value));

```

আমরা দুটি উপায়ের যেকোনো একটিতে ভিন্ন একটি কন্ট্রাক্ট কল করতে পারি:

- একটি ফাংশন কল তৈরি করতে একটি ইন্টারফেস সংজ্ঞা ব্যবহার করে
- কলটি তৈরি করতে "ম্যানুয়ালি" [অ্যাপ্লিকেশন বাইনারি ইন্টারফেস (ABI)](https://docs.soliditylang.org/en/v0.8.3/abi-spec.html) ব্যবহার করে। কোডের লেখক এটিই করার সিদ্ধান্ত নিয়েছেন।

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

ERC-20 স্ট্যান্ডার্ডের আগে তৈরি হওয়া টোকেনগুলোর সাথে ব্যাকওয়ার্ড সামঞ্জস্যের স্বার্থে, একটি ERC-20 কল রিভার্ট করার মাধ্যমে (যে ক্ষেত্রে `success` হলো `false`) অথবা সফল হয়ে একটি `false` মান ফেরত দেওয়ার মাধ্যমে (যে ক্ষেত্রে আউটপুট ডেটা থাকে এবং আপনি যদি এটিকে বুলিয়ান হিসেবে ডিকোড করেন তবে আপনি `false` পাবেন) ব্যর্থ হতে পারে।

```solidity


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

এই ফাংশনটি [ERC-20 এর হস্তান্তর কার্যকারিতা](https://eips.ethereum.org/EIPS/eip-20#transfer) বাস্তবায়ন করে, যা একটি অ্যাকাউন্টকে অন্য একটি অ্যাকাউন্টের দেওয়া অ্যালাউন্স খরচ করার অনুমতি দেয়।

```solidity

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

এই ফাংশনটি [ERC-20 এর transferFrom কার্যকারিতা](https://eips.ethereum.org/EIPS/eip-20#transferfrom) বাস্তবায়ন করে, যা একটি অ্যাকাউন্টকে অন্য একটি অ্যাকাউন্টের দেওয়া অ্যালাউন্স খরচ করার অনুমতি দেয়।

```solidity

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

এই ফাংশনটি একটি অ্যাকাউন্টে ইথার হস্তান্তর করে। ভিন্ন কোনো কন্ট্রাক্টে যেকোনো কল ইথার পাঠানোর চেষ্টা করতে পারে। যেহেতু আমাদের আসলে কোনো ফাংশন কল করার প্রয়োজন নেই, তাই আমরা কলের সাথে কোনো ডেটা পাঠাই না।

## উপসংহার {#conclusion}

এটি প্রায় 50 পৃষ্ঠার একটি দীর্ঘ নিবন্ধ। আপনি যদি এতদূর পড়ে থাকেন, তবে আপনাকে অভিনন্দন! আশা করি এতক্ষণে আপনি বুঝতে পেরেছেন যে একটি বাস্তব-জীবনের অ্যাপ্লিকেশন (ছোট নমুনা প্রোগ্রামের বিপরীতে) লেখার ক্ষেত্রে কী কী বিষয় বিবেচনা করতে হয় এবং আপনার নিজের ব্যবহারের জন্য কন্ট্রাক্ট লিখতে আপনি এখন আরও বেশি সক্ষম।

এখন যান, দরকারী কিছু লিখুন এবং আমাদের চমকে দিন।

[আমার আরও কাজের জন্য এখানে দেখুন](https://cryptodocguy.pro/)।