ప్రధాన కంటెంట్‌కు దాటవేయి

యూనిస్వాప్-వి2 ఒప్పంద విశ్లేషణ

దృఢత్వం
మధ్యస్థ
Ori Pomerantz
1 మే, 2021
51 నిమిషం పఠనం

పరిచయం

Uniswap v2 (opens in a new tab) ఏవైనా రెండు ERC-20 టోకెన్ల మధ్య మార్పిడి మార్కెట్ ను సృష్టించగలదు. ఈ వ్యాసంలో మేము ఈ ప్రోటోకాల్‌ను అమలు చేసే ఒప్పందాల కోసం సోర్స్ కోడ్‌ను పరిశీలిస్తాము మరియు అవి ఈ విధంగా ఎందుకు వ్రాయబడ్డాయో చూస్తాము.

యూనిస్వాప్ ఏమి చేస్తుంది?

సాధారణంగా, రెండు రకాల వినియోగదారులు ఉంటారు: ద్రవ్యత్వ ప్రదాతలు మరియు వ్యాపారులు.

ద్రవ్యత్వ ప్రదాతలు మార్పిడి చేయగల రెండు టోకెన్‌లతో పూల్‌ను అందిస్తారు (మేము వాటిని టోకెన్0 మరియు టోకెన్1 అని పిలుస్తాము). ప్రతిఫలంగా, వారు ద్రవ్యత్వ టోకెన్ అని పిలువబడే పూల్ యొక్క పాక్షిక యాజమాన్యాన్ని సూచించే మూడవ టోకెన్‌ను స్వీకరిస్తారు.

వ్యాపారులు ఒక రకమైన టోకెన్‌ను పూల్‌కు పంపి, ద్రవ్యత్వ ప్రదాతలచే అందించబడిన పూల్ నుండి మరొకటి (ఉదాహరణకు, టోకెన్0 ను పంపి, టోకెన్1 ను అందుకుంటారు) అందుకుంటారు. పూల్ కలిగి ఉన్న టోకెన్0ల మరియు టోకెన్1ల సాపేక్ష సంఖ్య ద్వారా మార్పిడి రేటు నిర్ణయించబడుతుంది. అదనంగా, ద్రవ్యత్వ పూల్ కోసం పూల్ ఒక చిన్న శాతాన్ని రివార్డ్‌గా తీసుకుంటుంది.

ద్రవ్యత్వ ప్రదాతలు తమ ఆస్తులను తిరిగి కోరుకున్నప్పుడు వారు పూల్ టోకెన్లను బర్న్ చేయవచ్చు మరియు వారి రివార్డ్ ల వాటాతో సహా వారి టోకెన్లను తిరిగి పొందవచ్చు.

పూర్తి వివరణ కోసం ఇక్కడ క్లిక్ చేయండి (opens in a new tab).

v2 ఎందుకు? v3 ఎందుకు కాదు?

Uniswap v3 (opens in a new tab) అనేది v2 కంటే చాలా క్లిష్టమైన అప్గ్రేడ్. మొదట v2 నేర్చుకుని తర్వాత v3 కి వెళ్లడం సులభం.

కోర్ ఒప్పందాలు vs పెరిఫెరీ ఒప్పందాలు

యూనిస్వాప్ v2 రెండు భాగాలుగా విభజించబడింది, ఒక కోర్ మరియు పెరిఫెరీ. ఈ విభజన ఆస్తులను కలిగి ఉన్న కోర్ ఒప్పందాలను, అందువల్ల సురక్షితంగా ఉండవలసి ఉంటుంది, సరళంగా మరియు ఆడిట్ చేయడానికి సులభంగా ఉండటానికి అనుమతిస్తుంది. వ్యాపారులకు అవసరమైన అన్ని అదనపు ఫంక్షనాలిటీని పెరిఫెరీ ఒప్పందాల ద్వారా అందించవచ్చు.

డేటా మరియు నియంత్రణ ప్రవాహాలు

మీరు యూనిస్వాప్ యొక్క మూడు ప్రధాన చర్యలను చేసినప్పుడు జరిగే డేటా మరియు నియంత్రణ ప్రవాహం ఇది:

  1. విభిన్న టోకెన్ల మధ్య స్వాప్ చేయండి
  2. మార్కెట్ కు ద్రవ్యత్వం జోడించి, జత మార్పిడి ERC-20 ద్రవ్యత్వ టోకెన్లతో రివార్డ్ పొందండి
  3. ERC-20 ద్రవ్యత్వ టోకెన్లను బర్న్ చేసి, వ్యాపారులకు మార్పిడి చేయడానికి జత మార్పిడి అనుమతించే ERC-20 టోకెన్లను తిరిగి పొందండి

స్వాప్

ఇది వ్యాపారులు ఉపయోగించే అత్యంత సాధారణ ప్రవాహం:

కాలర్

  1. స్వాప్ చేయవలసిన మొత్తంలో పెరిఫెరీ ఖాతాకు ఒక భత్యం అందించండి.
  2. పెరిఫెరీ ఒప్పందం యొక్క అనేక స్వాప్ ఫంక్షన్లలో ఒకదానిని కాల్ చేయండి (ETH ప్రమేయం ఉందో లేదో, వ్యాపారి డిపాజిట్ చేయడానికి టోకెన్ల మొత్తాన్ని లేదా తిరిగి పొందడానికి టోకెన్ల మొత్తాన్ని పేర్కొంటాడో లేదో ఆధారపడి ఉంటుంది). ప్రతి స్వాప్ ఫంక్షన్ పాత్ను అంగీకరిస్తుంది, వెళ్లాల్సిన మార్పిడుల యొక్క శ్రేణి.

పెరిఫెరీ ఒప్పందంలో (UniswapV2Router02.sol)

  1. మార్గం వెంట ప్రతి మార్పిడిలో వర్తకం చేయవలసిన మొత్తాలను గుర్తించండి.
  2. మార్గంపై మళ్ళింపులు. మార్గం వెంట ప్రతి మార్పిడికి ఇది ఇన్‌పుట్ టోకెన్‌ను పంపుతుంది, ఆపై మార్పిడి యొక్క swap ఫంక్షన్‌ను పిలుస్తుంది. చాలా సందర్భాల్లో టోకెన్ల కోసం గమ్యస్థానం చిరునామా మార్గంలో తదుపరి జత మార్పిడి. తుది మార్పిడిలో అది వ్యాపారి అందించిన చిరునామా.

కోర్ ఒప్పందంలో (UniswapV2Pair.sol) {#in-the-core-contract-uniswapv2pairsol-2}5. కోర్ ఒప్పందం మోసం చేయబడలేదని మరియు స్వాప్ తర్వాత తగినంత ద్రవ్యతను కొనసాగించగలదని ధృవీకరించండి.

  1. తెలిసిన నిల్వలకు అదనంగా మనకు ఎన్ని అదనపు టోకెన్లు ఉన్నాయో చూడండి. ఆ మొత్తం మార్పిడి కోసం మేము స్వీకరించిన ఇన్‌పుట్ టోకెన్ల సంఖ్య.
  2. అవుట్పుట్ టోకెన్లను గమ్యస్థానానికి పంపండి.
  3. నిల్వ మొత్తాలను అప్డేట్ చేయడానికి _update ని కాల్ చేయండి

పెరిఫెరీ ఒప్పందానికి తిరిగి (UniswapV2Router02.sol)

  1. ఏవైనా అవసరమైన శుభ్రతను నిర్వహించండి (ఉదాహరణకు, వ్యాపారికి పంపడానికి ETH ను తిరిగి పొందడానికి WETH టోకెన్లను బర్న్ చేయండి)

ద్రవ్యతను జోడించండి

కాలర్

  1. ద్రవ్యత్వ పూల్ కు జోడించవలసిన మొత్తాలలో పెరిఫెరీ ఖాతాకు ఒక భత్యం అందించండి.
  2. పెరిఫెరీ ఒప్పందం యొక్క addLiquidity ఫంక్షన్లలో ఒకదానిని కాల్ చేయండి.

పెరిఫెరీ ఒప్పందంలో (UniswapV2Router02.sol)

  1. అవసరమైతే కొత్త జత మార్పిడిని సృష్టించండి
  2. ఒకవేళ ప్రస్తుతం ఒక జత మార్పిడి ఉంటే, జోడించడానికి టోకెన్ల మొత్తాన్ని లెక్కించండి. ఇది రెండు టోకెన్లకు ఒకేలాంటి విలువగా ఉండాలి, కాబట్టి కొత్త టోకెన్లకు మరియు ప్రస్తుతం ఉన్న టోకెన్లకు ఒకే నిష్పత్తి.
  3. మొత్తాలు ఆమోదయోగ్యమైనవా అని తనిఖీ చేయండి (కాలర్లు ఒక కనీస మొత్తాన్ని పేర్కొనవచ్చు, దాని కంటే తక్కువ ద్రవ్యతను జోడించడానికి ఇష్టపడరు)
  4. కోర్ ఒప్పందాన్ని కాల్ చేయండి.

కోర్ ఒప్పందంలో (UniswapV2Pair.sol)

  1. ద్రవ్యత్వ టోకెన్లను మింట్ చేసి, వాటిని కాలర్ కు పంపండి
  2. నిల్వ మొత్తాలను అప్డేట్ చేయడానికి _update ని కాల్ చేయండి

ద్రవ్యతను తొలగించండి

కాలర్

  1. అండర్లయింగ్ టోకెన్ల మార్పిడి కోసం బర్న్ చేయవలసిన ద్రవ్యత్వ టోకెన్ల భత్యంతో పెరిఫెరీ ఖాతాను అందించండి.
  2. పెరిఫెరీ ఒప్పందం యొక్క removeLiquidity ఫంక్షన్లలో ఒకదానిని కాల్ చేయండి.

పెరిఫెరీ ఒప్పందంలో (UniswapV2Router02.sol)

  1. ద్రవ్యత్వ టోకెన్లను జత మార్పిడికి పంపండి

కోర్ ఒప్పందంలో (UniswapV2Pair.sol)

  1. గమ్యస్థాన చిరునామాకు బర్న్ చేయబడిన టోకెన్ల నిష్పత్తిలో అండర్లయింగ్ టోకెన్లను పంపండి. ఉదాహరణకు, పూల్ లో 1000 ఎ టోకెన్లు, 500 బి టోకెన్లు మరియు 90 ద్రవ్యత్వ టోకెన్లు ఉంటే, మరియు మనం బర్న్ చేయడానికి 9 టోకెన్లను స్వీకరిస్తే, మనం ద్రవ్యత్వ టోకెన్లలో 10% బర్న్ చేస్తున్నాము, కాబట్టి మనం వినియోగదారునికి 100 ఎ టోకెన్లు మరియు 50 బి టోకెన్లు తిరిగి పంపుతాము.
  2. ద్రవ్యత్వ టోకెన్లను బర్న్ చేయండి
  3. నిల్వ మొత్తాలను అప్డేట్ చేయడానికి _update ని కాల్ చేయండి

కోర్ ఒప్పందాలు

ఇవి ద్రవ్యతను కలిగి ఉన్న సురక్షిత ఒప్పందాలు.

UniswapV2Pair.sol

ఈ ఒప్పందం (opens in a new tab) టోకెన్లను మార్పిడి చేసే అసలు పూల్ ను అమలు చేస్తుంది. ఇది కోర్ యూనిస్వాప్ ఫంక్షనాలిటీ.

ఒప్పందం అమలు చేసే కారణంగా (IUniswapV2Pair మరియు UniswapV2ERC20) లేదా అవి అమలు చేసే ఒప్పందాలను పిలిచే కారణంగా, ఒప్పందానికి తెలిసి ఉండవలసిన అన్ని ఇంటర్ఫేస్లు ఇవి.

contract UniswapV2Pair is IUniswapV2Pair, UniswapV2ERC20 {

ఈ ఒప్పందం UniswapV2ERC20 నుండి వారసత్వం పొందుతుంది, ఇది ద్రవ్యత్వ టోకెన్ల కోసం ERC-20 ఫంక్షన్లను అందిస్తుంది.

    using SafeMath  for uint;

ఓవర్ఫ్లో మరియు అండర్ఫ్లోలను నివారించడానికి సేఫ్ మాథ్ లైబ్రరీ (opens in a new tab) ఉపయోగించబడుతుంది. ఇది ముఖ్యం ఎందుకంటే లేకపోతే మనం -1 విలువ ఉండవలసిన పరిస్థితిలో ముగుస్తాము, కానీ బదులుగా 2^256-1 ఉంటుంది.

    using UQ112x112 for uint224;

పూల్ ఒప్పందంలోని చాలా గణనలకు భిన్నాలు అవసరం. అయినప్పటికీ, భిన్నాలు EVM ద్వారా మద్దతు ఇవ్వబడవు. యూనిస్వాప్ కనుగొన్న పరిష్కారం 224 బిట్ విలువలను ఉపయోగించడం, పూర్ణాంక భాగం కోసం 112 బిట్స్ మరియు భిన్నం కోసం 112 బిట్స్. కాబట్టి 1.0 ను 2^112 గా, 1.5 ను 2^112 + 2^111 గా మొదలైనవిగా సూచిస్తారు.

ఈ లైబ్రరీ గురించి మరిన్ని వివరాలు పత్రంలో తరువాత అందుబాటులో ఉన్నాయి.

చరరాశులు

    uint public constant MINIMUM_LIQUIDITY = 10**3;

సున్నాతో విభజన సందర్భాలను నివారించడానికి, ఎల్లప్పుడూ ఉండే కనీస సంఖ్యలో ద్రవ్యత్వ టోకెన్లు ఉన్నాయి (కానీ ఖాతా సున్నా యాజమాన్యంలో ఉంటాయి). ఆ సంఖ్య MINIMUM_LIQUIDITY, వెయ్యి.

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

ఇది ERC-20 బదిలీ ఫంక్షన్ కోసం ABI సెలెక్టర్. ఇది రెండు టోకెన్ ఖాతాలలో ERC-20 టోకెన్లను బదిలీ చేయడానికి ఉపయోగించబడుతుంది.

    address public factory;

ఈ పూల్ ను సృష్టించిన ఫ్యాక్టరీ ఒప్పందం ఇది. ప్రతి పూల్ రెండు ERC-20 టోకెన్ల మధ్య మార్పిడి, ఫ్యాక్టరీ ఈ పూల్స్ అన్నింటినీ కలిపే ఒక కేంద్ర బిందువు.

    address public token0;
    address public token1;

ఈ పూల్ ద్వారా మార్పిడి చేయగల రెండు రకాల ERC-20 టోకెన్ల కోసం ఒప్పందాల చిరునామాలు ఇవి.

    uint112 private reserve0;           // ఒకే నిల్వ స్థానాన్ని ఉపయోగిస్తుంది, getReserves ద్వారా యాక్సెస్ చేయవచ్చు
    uint112 private reserve1;           // ఒకే నిల్వ స్థానాన్ని ఉపయోగిస్తుంది, getReserves ద్వారా యాక్సెస్ చేయవచ్చు

ప్రతి టోకెన్ రకం కోసం పూల్ కలిగి ఉన్న నిల్వలు. మేము రెండూ ఒకే విలువను సూచిస్తాయని ఊహిస్తాము, కాబట్టి ప్రతి టోకెన్0 విలువ reserve1/reserve0 టోకెన్1 లు.

    uint32  private blockTimestampLast; // ఒకే నిల్వ స్థానాన్ని ఉపయోగిస్తుంది, getReserves ద్వారా యాక్సెస్ చేయవచ్చు

ఒక మార్పిడి జరిగిన చివరి బ్లాక్ కోసం టైమ్స్టాంప్, కాలక్రమేణా మార్పిడి రేట్లను ట్రాక్ చేయడానికి ఉపయోగించబడుతుంది.

Ethereum ఒప్పందాల యొక్క అతిపెద్ద గ్యాస్ ఖర్చులలో ఒకటి నిల్వ, ఇది ఒప్పందం యొక్క ఒక కాల్ నుండి మరొక దానికి నిలుస్తుంది. ప్రతి నిల్వ కణం 256 బిట్స్ పొడవు ఉంటుంది. కాబట్టి మూడు వేరియబుల్స్, reserve0, reserve1, మరియు blockTimestampLast, ఒకే నిల్వ విలువ ఈ మూడింటిని చేర్చగల విధంగా కేటాయించబడ్డాయి (112+112+32=256).

    uint public price0CumulativeLast;
    uint public price1CumulativeLast;

ఈ వేరియబుల్స్ ప్రతి టోకెన్ కోసం సంచిత ఖర్చులను కలిగి ఉంటాయి (ప్రతి ఒక్కటి మరొకదాని పరంగా). కాలక్రమేణా సగటు మార్పిడి రేటును లెక్కించడానికి వాటిని ఉపయోగించవచ్చు.

    uint public kLast; // reserve0 * reserve1, అత్యంత ఇటీవలి ద్రవ్యత్వ సంఘటన తర్వాత వెంటనే

టోకెన్0 మరియు టోకెన్1 మధ్య మార్పిడి రేటుపై జత మార్పిడి నిర్ణయించే విధానం ట్రేడ్ల సమయంలో రెండు నిల్వల యొక్క గుణకారాన్ని స్థిరంగా ఉంచడం. kLast ఈ విలువ. ఒక ద్రవ్యత్వ ప్రదాత టోకెన్లను డిపాజిట్ చేసినప్పుడు లేదా ఉపసంహరించుకున్నప్పుడు ఇది మారుతుంది, మరియు 0.3% మార్కెట్ రుసుము కారణంగా ఇది కొద్దిగా పెరుగుతుంది.

ఇక్కడ ఒక సాధారణ ఉదాహరణ. గమనించండి, సరళత కోసం పట్టికలో దశాంశ బిందువు తర్వాత మూడు అంకెలు మాత్రమే ఉన్నాయి, మరియు మేము 0.3% ట్రేడింగ్ రుసుమును విస్మరిస్తాము కాబట్టి సంఖ్యలు ఖచ్చితమైనవి కావు.

సంఘటననిల్వ0నిల్వ1నిల్వ0 * నిల్వ1సగటు మార్పిడి రేటు (టోకెన్1 / టోకెన్0)
ప్రారంభ సెటప్1,000.0001,000.0001,000,000
వ్యాపారి A 50 టోకెన్0 ను 47.619 టోకెన్1 కోసం స్వాప్ చేస్తాడు1,050.000952.3811,000,0000.952
వ్యాపారి B 10 టోకెన్0 ను 8.984 టోకెన్1 కోసం స్వాప్ చేస్తాడు1,060.000943.3961,000,0000.898
వ్యాపారి C 40 టోకెన్0 ను 34.305 టోకెన్1 కోసం స్వాప్ చేస్తాడు1,100.000909.0901,000,0000.858
వ్యాపారి D 100 టోకెన్1 ను 109.01 టోకెన్0 కోసం స్వాప్ చేస్తాడు990.9901,009.0901,000,0000.917
వ్యాపారి E 10 టోకెన్0 ను 10.079 టోకెన్1 కోసం స్వాప్ చేస్తాడు1,000.990999.0101,000,0001.008

వ్యాపారులు టోకెన్0 ను ఎక్కువగా అందించే కొద్దీ, టోకెన్1 యొక్క సాపేక్ష విలువ పెరుగుతుంది మరియు సరఫరా మరియు డిమాండ్ ఆధారంగా దీనికి విరుద్ధంగా ఉంటుంది.

లాక్

    uint private unlocked = 1;

పునఃప్రవేశ దుర్వినియోగం (opens in a new tab) ఆధారంగా ఉండే భద్రతా దుర్బలత్వాల యొక్క ఒక తరగతి ఉంది. యూనిస్వాప్ అనధికారిక ERC-20 టోకెన్లను బదిలీ చేయవలసి ఉంటుంది, అంటే వాటిని పిలిచే యూనిస్వాప్ మార్కెట్ ను దుర్వినియోగం చేయడానికి ప్రయత్నించే ERC-20 ఒప్పందాలను పిలవడం. ఒప్పందంలో భాగంగా unlocked వేరియబుల్ ను కలిగి ఉండటం ద్వారా, ఫంక్షన్లు నడుస్తున్నప్పుడు (ఒకే లావాదేవీలో) పిలవకుండా నిరోధించవచ్చు.

    modifier lock() {

ఈ ఫంక్షన్ ఒక మాడిఫైయర్ (opens in a new tab), దాని ప్రవర్తనను మార్చడానికి ఒక సాధారణ ఫంక్షన్ చుట్టూ చుట్టుకునే ఫంక్షన్.

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

unlocked ఒకటితో సమానంగా ఉంటే, దానిని సున్నాకు సెట్ చేయండి. ఇది ఇప్పటికే సున్నా అయితే కాల్ ను రద్దు చేయండి, దానిని విఫలం చేయండి.

        _;

ఒక మాడిఫైయర్ లో _; అనేది అసలు ఫంక్షన్ కాల్ (అన్ని పారామీటర్లతో). ఇక్కడ ఫంక్షన్ కాల్ unlocked పిలిచినప్పుడు ఒకటిగా ఉంటేనే జరుగుతుందని అర్థం, మరియు అది నడుస్తున్నప్పుడు unlocked విలువ సున్నాగా ఉంటుంది.

        unlocked = 1;
    }

ప్రధాన ఫంక్షన్ తిరిగి వచ్చిన తర్వాత, లాక్ ను విడుదల చేయండి.

ఇతరములు ఫంక్షన్లు

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

ఈ ఫంక్షన్ కాలర్లకు మార్పిడి యొక్క ప్రస్తుత స్థితిని అందిస్తుంది. Solidity ఫంక్షన్లు బహుళ విలువలను తిరిగి ఇవ్వగలవు (opens in a new tab) గమనించండి.

    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 ఫంక్షన్లలో (opens in a new tab) ఒకదానిని ఉపయోగించి కాల్ ను "మాన్యువల్ గా" సృష్టిస్తాము.

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

ఒక ERC-20 బదిలీ కాల్ వైఫల్యాన్ని నివేదించగల రెండు మార్గాలు ఉన్నాయి:

  1. తిరిగి వెళ్లండి. ఒక బాహ్య ఒప్పందానికి కాల్ తిరిగి వెళ్ళినట్లయితే, బూలియన్ రిటర్న్ విలువ false అవుతుంది
  2. సాధారణంగా ముగించండి కానీ వైఫల్యాన్ని నివేదించండి. ఆ సందర్భంలో రిటర్న్ విలువ బఫర్ సున్నా కాని పొడవును కలిగి ఉంటుంది, మరియు బూలియన్ విలువగా డీకోడ్ చేసినప్పుడు అది false అవుతుంది

ఈ పరిస్థితులలో ఏది జరిగినా, తిరిగి వెళ్లండి.

సంఘటనలు

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

ద్రవ్యత్వ ప్రదాత ద్రవ్యతను డిపాజిట్ చేసినప్పుడు (Mint) లేదా ఉపసంహరించుకున్నప్పుడు (Burn) ఈ రెండు సంఘటనలు విడుదల చేయబడతాయి. ఏ సందర్భంలోనైనా, డిపాజిట్ చేయబడిన లేదా ఉపసంహరించుకున్న టోకెన్0 మరియు టోకెన్1 మొత్తాలు సంఘటనలో భాగంగా ఉంటాయి, అలాగే మనల్ని పిలిచిన ఖాతా యొక్క గుర్తింపు (sender) కూడా ఉంటుంది. ఉపసంహరణ సందర్భంలో, సంఘటన టోకెన్లను స్వీకరించిన లక్ష్యాన్ని (to) కూడా కలిగి ఉంటుంది, ఇది పంపినవారికి సమానంగా ఉండకపోవచ్చు.

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

ఒక వ్యాపారి ఒక టోకెన్ ను మరొక దాని కోసం స్వాప్ చేసినప్పుడు ఈ సంఘటన విడుదల చేయబడుతుంది. మళ్ళీ, పంపినవారు మరియు గమ్యం ఒకేలా ఉండకపోవచ్చు. ప్రతి టోకెన్ మార్పిడికి పంపబడవచ్చు లేదా దాని నుండి స్వీకరించబడవచ్చు.

    event Sync(uint112 reserve0, uint112 reserve1);

చివరగా, కారణంతో సంబంధం లేకుండా, తాజా నిల్వ సమాచారం (మరియు అందువల్ల మార్పిడి రేటు) అందించడానికి టోకెన్లు జోడించబడినప్పుడు లేదా ఉపసంహరించుకున్నప్పుడు ప్రతిసారీ Sync విడుదల చేయబడుతుంది.

సెటప్ ఫంక్షన్లు

కొత్త జత మార్పిడి ఏర్పాటు చేసినప్పుడు ఈ ఫంక్షన్లను ఒకసారి పిలవాలి.

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

నిర్మాణకర్త జతను సృష్టించిన ఫ్యాక్టరీ యొక్క చిరునామాను మేము ట్రాక్ చేస్తామని నిర్ధారిస్తుంది. initialize మరియు ఫ్యాక్టరీ రుసుము (ఒకవేళ ఉంటే) కోసం ఈ సమాచారం అవసరం

    // విస్తరణ సమయంలో ఫ్యాక్టరీ ద్వారా ఒకసారి పిలవబడుతుంది
    function initialize(address _token0, address _token1) external {
        require(msg.sender == factory, 'UniswapV2: FORBIDDEN'); // తగినంత తనిఖీ
        token0 = _token0;
        token1 = _token1;
    }

ఈ ఫంక్షన్ ఫ్యాక్టరీకి (మరియు ఫ్యాక్టరీకి మాత్రమే) ఈ జత మార్పిడి చేసే రెండు ERC-20 టోకెన్లను పేర్కొనడానికి అనుమతిస్తుంది.

అంతర్గత నవీకరణ ఫంక్షన్లు

_update
    // నిల్వలను నవీకరించండి మరియు, ప్రతి బ్లాక్ కు మొదటి కాల్ లో, ధర సంచితాలను
    function _update(uint balance0, uint balance1, uint112 _reserve0, uint112 _reserve1) private {

టోకెన్లు డిపాజిట్ చేయబడినప్పుడు లేదా ఉపసంహరించుకున్నప్పుడు ప్రతిసారీ ఈ ఫంక్షన్ పిలవబడుతుంది.

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

ఒకవేళ బ్యాలెన్స్0 లేదా బ్యాలెన్స్1 (uint256) uint112(-1) (=2^112-1) కంటే ఎక్కువగా ఉంటే (కాబట్టి అది ఓవర్ఫ్లో అవుతుంది & uint112 కు మార్చబడినప్పుడు తిరిగి 0 కు చుట్టబడుతుంది) ఓవర్ఫ్లోలను నివారించడానికి _అప్డేట్ కొనసాగించడాన్ని తిరస్కరించండి. 10^18 యూనిట్లుగా విభజించగల ఒక సాధారణ టోకెన్ తో, ప్రతి మార్పిడి ప్రతి టోకెన్లకు సుమారు 5.1*10^15 వరకు పరిమితం చేయబడిందని అర్థం. ఇప్పటివరకు అది సమస్య కాదు.

        uint32 blockTimestamp = uint32(block.timestamp % 2**32);
        uint32 timeElapsed = blockTimestamp - blockTimestampLast; // ఓవర్ఫ్లో కోరబడుతుంది
        if (timeElapsed > 0 && _reserve0 != 0 && _reserve1 != 0) {

గడచిన సమయం సున్నా కాకపోతే, ఈ బ్లాక్ లో మేము మొదటి మార్పిడి లావాదేవీ అని అర్థం. ఆ సందర్భంలో, మేము ధర సంచితాలను నవీకరించాలి.

            // * ఎప్పుడూ ఓవర్ఫ్లో కాదు, మరియు + ఓవర్ఫ్లో కోరబడుతుంది
            price0CumulativeLast += uint(UQ112x112.encode(_reserve1).uqdiv(_reserve0)) * timeElapsed;
            price1CumulativeLast += uint(UQ112x112.encode(_reserve0).uqdiv(_reserve1)) * timeElapsed;
        }

ప్రతి ధర సంచితం తాజా ధరతో (ఇతర టోకెన్ నిల్వ/ఈ టోకెన్ నిల్వ) సెకన్లలో గడచిన సమయంతో నవీకరించబడుతుంది. సగటు ధరను పొందడానికి, మీరు కాలంలో రెండు బిందువులలో సంచిత ధరను చదివి, వాటి మధ్య సమయ వ్యత్యాసంతో విభజిస్తారు. ఉదాహరణకు, ఈ సంఘటనల క్రమాన్ని ఊహించండి:

సంఘటననిల్వ0నిల్వ1టైమ్ స్టాంప్ఉపాంత మార్పిడి రేటు (నిల్వ1 / నిల్వ0)ధర0సంచితచివరి
ప్రారంభ సెటప్1,000.0001,000.0005,0001.0000
వ్యాపారి A 50 టోకెన్0 ను డిపాజిట్ చేసి 47.619 టోకెన్1 ను తిరిగి పొందుతాడు1,050.000952.3815,0200.90720
వ్యాపారి B 10 టోకెన్0 ను డిపాజిట్ చేసి 8.984 టోకెన్1 ను తిరిగి పొందుతాడు1,060.000943.3965,0300.89020+10*0.907 = 29.07
వ్యాపారి C 40 టోకెన్0 ను డిపాజిట్ చేసి 34.305 టోకెన్1 ను తిరిగి పొందుతాడు1,100.000909.0905,1000.82629.07+70*0.890 = 91.37
వ్యాపారి D 100 టోకెన్1 ను డిపాజిట్ చేసి 109.01 టోకెన్0 ను తిరిగి పొందుతాడు990.9901,009.0905,1101.01891.37+10*0.826 = 99.63
వ్యాపారి E 10 టోకెన్0 ను డిపాజిట్ చేసి 10.079 టోకెన్1 ను తిరిగి పొందుతాడు1,000.990999.0105,1500.99899.63+40*1.1018 = 143.702

5,030 మరియు 5,150 టైమ్ స్టాంప్ ల మధ్య టోకెన్0 సగటు ధరను లెక్కించాలనుకుందాం. price0Cumulative విలువలో వ్యత్యాసం 143.702-29.07=114.632. ఇది రెండు నిమిషాల (120 సెకన్లు) సగటు. కాబట్టి సగటు ధర 114.632/120 = 0.955.

ఈ ధర గణన పాత నిల్వ పరిమాణాలను తెలుసుకోవాల్సిన కారణం.

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

చివరగా, గ్లోబల్ వేరియబుల్స్ నవీకరించి Sync సంఘటనను విడుదల చేయండి.

_mintFee
    // రుసుము ఆన్లో ఉంటే, sqrt(k) లోని పెరుగుదలలో 1/6 వ వంతుకు సమానమైన ద్రవ్యతను మింట్ చేయండి
    function _mintFee(uint112 _reserve0, uint112 _reserve1) private returns (bool feeOn) {

యూనిస్వాప్ 2.0లో వ్యాపారులు మార్కెట్ ను ఉపయోగించడానికి 0.30% రుసుము చెల్లిస్తారు. ఆ రుసుములో ఎక్కువ భాగం (ట్రేడ్ లో 0.25%) ఎల్లప్పుడూ ద్రవ్యత్వ ప్రదాతలకు వెళ్తుంది. మిగిలిన 0.05% ద్రవ్యత్వ ప్రదాతలకు లేదా ఫ్యాక్టరీ ద్వారా ప్రోటోకాల్ రుసుముగా పేర్కొన్న చిరునామాకు వెళ్ళవచ్చు, ఇది యూనిస్వాప్ కు వారి అభివృద్ధి ప్రయత్నానికి చెల్లిస్తుంది.

గణనలను (మరియు అందువల్ల గ్యాస్ ఖర్చులను) తగ్గించడానికి, ప్రతి లావాదేవీ వద్ద కాకుండా, పూల్ నుండి ద్రవ్యత జోడించబడినప్పుడు లేదా తీసివేయబడినప్పుడు మాత్రమే ఈ రుసుము లెక్కించబడుతుంది.

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

ఫ్యాక్టరీ యొక్క రుసుము గమ్యాన్ని చదవండి. అది సున్నా అయితే ప్రోటోకాల్ రుసుము లేదు మరియు ఆ రుసుమును లెక్కించాల్సిన అవసరం లేదు.

        uint _kLast = kLast; // గ్యాస్ పొదుపు

kLast స్థితి వేరియబుల్ నిల్వలో ఉంది, కాబట్టి ఒప్పందానికి విభిన్న కాల్ ల మధ్య దానికి ఒక విలువ ఉంటుంది. నిల్వకు ప్రాప్యత ఒప్పందానికి ఫంక్షన్ కాల్ ముగిసినప్పుడు విడుదల చేయబడే అస్థిర జ్ఞాపకశక్తికి ప్రాప్యత కంటే చాలా ఖరీదైనది, కాబట్టి మేము గ్యాస్ మీద ఆదా చేయడానికి ఒక అంతర్గత వేరియబుల్ ను ఉపయోగిస్తాము.

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

ద్రవ్యత్వ ప్రదాతలు వారి ద్రవ్యత్వ టోకెన్ల ప్రశంసల ద్వారా తమ వాటాను పొందుతారు. కానీ ప్రోటోకాల్ రుసుము కొత్త ద్రవ్యత్వ టోకెన్లను మింట్ చేసి feeTo చిరునామాకు అందించాలి.

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

ఒక ప్రోటోకాల్ రుసుమును వసూలు చేయడానికి కొత్త ద్రవ్యత ఉంటే. మీరు ఈ వ్యాసంలో తరువాత స్క్వేర్ రూట్ ఫంక్షన్ ను చూడవచ్చు (#Math)

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

రుసుముల యొక్క ఈ సంక్లిష్ట గణన వైట్‌పేపర్ (opens in a new tab) లో 5వ పేజీలో వివరించబడింది. kLast లెక్కించబడిన సమయం మరియు ప్రస్తుతం మధ్య ద్రవ్యత జోడించబడలేదు లేదా తీసివేయబడలేదు అని మనకు తెలుసు (ఎందుకంటే ద్రవ్యత జోడించబడినప్పుడు లేదా తీసివేయబడినప్పుడు ప్రతిసారీ, అది వాస్తవంగా మారే ముందు ఈ గణనను మేము అమలు చేస్తాము), కాబట్టి reserve0 * reserve1 లో ఏదైనా మార్పు లావాదేవీ రుసుముల నుండి రావాలి (అవి లేకుండా మేము reserve0 * reserve1 ను స్థిరంగా ఉంచుతాము).

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

అదనపు ద్రవ్యత్వ టోకెన్లను వాస్తవంగా సృష్టించి, వాటిని feeTo కు కేటాయించడానికి UniswapV2ERC20._mint ఫంక్షన్ ను ఉపయోగించండి.

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

kLast కు రుసుము సెట్ చేయకపోతే దానిని సున్నాకు సెట్ చేయండి (ఒకవేళ అది ఇప్పటికే అలా లేకపోతే). ఈ ఒప్పందం వ్రాయబడినప్పుడు ఒక గ్యాస్ రీఫండ్ ఫీచర్ (opens in a new tab) ఉండేది, ఇది ఒప్పందాలను వారు అవసరం లేని నిల్వను సున్నా చేయడం ద్వారా ఎథేరియం స్థితి యొక్క మొత్తం పరిమాణాన్ని తగ్గించడానికి ప్రోత్సహించింది. ఈ కోడ్ సాధ్యమైనప్పుడు ఆ రీఫండ్ ను పొందుతుంది.

బాహ్యంగా యాక్సెస్ చేయగల ఫంక్షన్లు

ఏదైనా లావాదేవీ లేదా ఒప్పందం ఈ ఫంక్షన్లను కాల్ చేయగలదు గమనించండి, అవి పెరిఫెరీ ఒప్పందం నుండి పిలవబడటానికి రూపొందించబడ్డాయి. మీరు వాటిని నేరుగా పిలిస్తే మీరు జత మార్పిడిని మోసం చేయలేరు, కానీ మీరు పొరపాటు ద్వారా విలువను కోల్పోవచ్చు.

మింట్
    // ఈ తక్కువ-స్థాయి ఫంక్షన్ ముఖ్యమైన భద్రతా తనిఖీలను చేసే ఒప్పందం నుండి పిలవబడాలి
    function mint(address to) external lock returns (uint liquidity) {

ఒక ద్రవ్యత్వ ప్రదాత పూల్ కు ద్రవ్యతను జోడించినప్పుడు ఈ ఫంక్షన్ పిలవబడుతుంది. ఇది రివార్డుగా అదనపు ద్రవ్యత్వ టోకెన్లను మింట్ చేస్తుంది. దీనిని పెరిఫెరీ ఒప్పందం నుండి పిలవాలి, ఇది అదే లావాదేవీలో ద్రవ్యతను జోడించిన తర్వాత దానిని పిలుస్తుంది (కాబట్టి చట్టబద్ధమైన యజమానికి ముందు కొత్త ద్రవ్యతను క్లెయిమ్ చేసే లావాదేవీని ఎవరూ సమర్పించలేరు).

        (uint112 _reserve0, uint112 _reserve1,) = getReserves(); // గ్యాస్ పొదుపు

బహుళ విలువలను తిరిగి ఇచ్చే 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);

ప్రస్తుత బ్యాలెన్స్ లను పొంది, ప్రతి టోకెన్ రకానికి ఎంత జోడించబడిందో చూడండి.

        bool feeOn = _mintFee(_reserve0, _reserve1);

సేకరించవలసిన ప్రోటోకాల్ రుసుములను లెక్కించండి, ఒకవేళ ఉంటే, మరియు దానికి అనుగుణంగా ద్రవ్యత్వ టోకెన్లను మింట్ చేయండి. _mintFee పారామీటర్లు పాత నిల్వ విలువలు కాబట్టి, రుసుము కారణంగా పూల్ మార్పుల ఆధారంగా మాత్రమే రుసుము ఖచ్చితంగా లెక్కించబడుతుంది.

        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. అధిక ఖర్చు కాదు.

మొదటి డిపాజిట్ సమయంలో రెండు టోకెన్ల సాపేక్ష విలువ మనకు తెలియదు, కాబట్టి మేము మొత్తాలను గుణించి, డిపాజిట్ రెండు టోకెన్లలో సమాన విలువను అందిస్తుందని ఊహించి స్క్వేర్ రూట్ తీసుకుంటాము.

ఆర్బిట్రేజ్ వల్ల విలువను కోల్పోకుండా ఉండటానికి సమాన విలువను అందించడం డిపాజిటర్ ప్రయోజనం కాబట్టి మనం దీనిని నమ్మవచ్చు. రెండు టోకెన్ల విలువ ఒకేలా ఉందని అనుకుందాం, కానీ మన డిపాజిటర్ టోకెన్0 కంటే నాలుగు రెట్లు ఎక్కువ టోకెన్1 ని డిపాజిట్ చేసారు. ఒక వ్యాపారి టోకెన్0 విలువైనదని జత మార్పిడి భావించే వాస్తవాన్ని ఉపయోగించి దాని నుండి విలువను తీయవచ్చు.

సంఘటననిల్వ0నిల్వ1నిల్వ0 * నిల్వ1పూల్ విలువ (నిల్వ0 + నిల్వ1)
ప్రారంభ సెటప్83225640
వ్యాపారి 8 టోకెన్0 టోకెన్లను డిపాజిట్ చేస్తాడు, తిరిగి 16 టోకెన్1 ను పొందుతాడు161625632

మీరు చూడగలరు, వ్యాపారి అదనంగా 8 టోకెన్లను సంపాదించాడు, ఇది పూల్ విలువలో తగ్గుదల నుండి వస్తుంది, దాని యజమాని అయిన డిపాజిటర్ కు నష్టం కలిగిస్తుంది.

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

ప్రతి తదుపరి డిపాజిట్ తో రెండు ఆస్తుల మధ్య మార్పిడి రేటు మనకు ఇప్పటికే తెలుసు, మరియు ద్రవ్యత్వ ప్రదాతలు రెండింటిలోనూ సమాన విలువను అందిస్తారని మేము ఆశిస్తున్నాము. వారు అలా చేయకపోతే, శిక్షగా వారు అందించిన తక్కువ విలువ ఆధారంగా మేము వారికి ద్రవ్యత్వ టోకెన్లను ఇస్తాము.

ఇది ప్రారంభ డిపాజిట్ అయినా లేదా తదుపరిది అయినా, మేము అందించే ద్రవ్యత్వ టోకెన్ల సంఖ్య reserve0*reserve1 లోని మార్పు యొక్క స్క్వేర్ రూట్ కు సమానం మరియు ద్రవ్యత్వ టోకెన్ విలువ మారదు (రెండు రకాల సమాన విలువలు లేని డిపాజిట్ ను మేము పొందితే తప్ప, ఆ సందర్భంలో "జరిమానా" పంపిణీ చేయబడుతుంది). ఇక్కడ అదే విలువ కలిగిన రెండు టోకెన్లతో మరొక ఉదాహరణ ఉంది, మూడు మంచి డిపాజిట్లు మరియు ఒకటి చెడ్డది (ఒకే టోకెన్ రకం డిపాజిట్, కాబట్టి ఇది ఏ ద్రవ్యత్వ టోకెన్లను ఉత్పత్తి చేయదు).

సంఘటననిల్వ0నిల్వ1నిల్వ0 * నిల్వ1పూల్ విలువ (నిల్వ0 + నిల్వ1)ఈ డిపాజిట్ కోసం ముద్రించిన ద్రవ్యత్వ టోకెన్లుమొత్తం ద్రవ్యత్వ టోకెన్లుప్రతి ద్రవ్యత్వ టోకెన్ విలువ
ప్రారంభ సెటప్8.0008.0006416.000882.000
ప్రతి రకం నాలుగు డిపాజిట్ చేయండి12.00012.00014424.0004122.000
ప్రతి రకం రెండు డిపాజిట్ చేయండి14.00014.00019628.0002142.000
అసమాన విలువ డిపాజిట్18.00014.00025232.000014~2.286
ఆర్బిట్రేజ్ తరువాత~15.874~15.874252~31.748014~2.267
        }
        require(liquidity > 0, 'UniswapV2: INSUFFICIENT_LIQUIDITY_MINTED');
        _mint(to, liquidity);

అదనపు ద్రవ్యత్వ టోకెన్లను వాస్తవంగా సృష్టించి, సరైన ఖాతాకు ఇవ్వడానికి UniswapV2ERC20._mint ఫంక్షన్ ను ఉపయోగించండి.


        _update(balance0, balance1, _reserve0, _reserve1);
        if (feeOn) kLast = uint(reserve0).mul(reserve1); // reserve0 మరియు reserve1 నవీకరించబడినవి
        emit Mint(msg.sender, amount0, amount1);
    }

స్థితి వేరియబుల్స్ (reserve0, reserve1, మరియు అవసరమైతే kLast) నవీకరించి, తగిన సంఘటనను విడుదల చేయండి.

బర్న్
    // ఈ తక్కువ-స్థాయి ఫంక్షన్ ముఖ్యమైన భద్రతా తనిఖీలను చేసే ఒప్పందం నుండి పిలవబడాలి
    function burn(address to) external lock returns (uint amount0, uint amount1) {

ద్రవ్యత ఉపసంహరించబడినప్పుడు మరియు తగిన ద్రవ్యత్వ టోకెన్లను బర్న్ చేయవలసినప్పుడు ఈ ఫంక్షన్ పిలవబడుతుంది. ఇది పెరిఫెరీ ఖాతా నుండి కూడా పిలవబడాలి.

        (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)];

పెరిఫెరీ ఒప్పందం కాల్ కు ముందు బర్న్ చేయవలసిన ద్రవ్యతను ఈ ఒప్పందానికి బదిలీ చేసింది. ఆ విధంగా ఎంత ద్రవ్యతను బర్న్ చేయాలో మనకు తెలుసు, మరియు అది బర్న్ అవుతుందని మనం నిర్ధారించుకోవచ్చు.

        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');

ద్రవ్యత్వ ప్రదాత రెండు టోకెన్ల సమాన విలువను పొందుతాడు. ఈ విధంగా మనం మార్పిడి రేటును మార్చము.

burn ఫంక్షన్ యొక్క మిగిలిన భాగం పైన ఉన్న mint ఫంక్షన్ యొక్క ప్రతిబింబం.

స్వాప్
    // ఈ తక్కువ-స్థాయి ఫంక్షన్ ముఖ్యమైన భద్రతా తనిఖీలను చేసే ఒప్పందం నుండి పిలవబడాలి
    function swap(uint amount0Out, uint amount1Out, address to, bytes calldata data) external lock {

ఈ ఫంక్షన్ కూడా పెరిఫెరీ ఒప్పందం నుండి పిలవబడాలి.

        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} కోసం స్కోప్, స్టాక్ చాలా లోతైన లోపాలను నివారిస్తుంది

స్థానిక వేరియబుల్స్ మెమరీలో లేదా, వాటి సంఖ్య చాలా ఎక్కువగా లేకపోతే, నేరుగా స్టాక్ మీద నిల్వ చేయబడతాయి. మనం స్టాక్ ను ఉపయోగించడానికి సంఖ్యను పరిమితం చేయగలిగితే మనం తక్కువ గ్యాస్ ను ఉపయోగిస్తాము. మరిన్ని వివరాల కోసం ఎల్లో పేపర్, అధికారిక Ethereum స్పెసిఫికేషన్లు (opens in a new tab), p. 26, సమీకరణం 298 చూడండి.

            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); // ఆశాజనకంగా టోకెన్లను బదిలీ చేయండి

ఈ బదిలీ ఆశాజనకమైనది, ఎందుకంటే అన్ని షరతులు నెరవేరాయని మేము ఖచ్చితంగా తెలుసుకోకముందే మేము బదిలీ చేస్తాము. ఇది Ethereum లో సరే, ఎందుకంటే కాల్ లో తరువాత షరతులు నెరవేరకపోతే మేము దాని నుండి మరియు అది సృష్టించిన ఏవైనా మార్పుల నుండి తిరిగి వస్తాము.

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

అభ్యర్థించినట్లయితే స్వాప్ గురించి స్వీకర్తకు తెలియజేయండి.

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

ప్రస్తుత బ్యాలెన్స్ లను పొందండి. పెరిఫెరీ ఒప్పందం స్వాప్ కోసం మమ్మల్ని పిలిచే ముందు టోకెన్లను మాకు పంపుతుంది. ఇది ఒప్పందానికి అది మోసం చేయబడలేదని తనిఖీ చేయడానికి సులభం చేస్తుంది, ఇది కోర్ ఒప్పందంలో జరగవలసిన తనిఖీ (ఎందుకంటే మా పెరిఫెరీ ఒప్పందం కంటే ఇతర సంస్థల నుండి మమ్మల్ని పిలవవచ్చు).

        uint amount0In = balance0 > _reserve0 - amount0Out ? balance0 - (_reserve0 - amount0Out) : 0;
        uint amount1In = balance1 > _reserve1 - amount1Out ? balance1 - (_reserve1 - amount1Out) : 0;
        require(amount0In > 0 || amount1In > 0, 'UniswapV2: INSUFFICIENT_INPUT_AMOUNT');
        { // reserve{0,1}Adjusted కోసం స్కోప్, స్టాక్ చాలా లోతైన లోపాలను నివారిస్తుంది
            uint balance0Adjusted = balance0.mul(1000).sub(amount0In.mul(3));
            uint balance1Adjusted = balance1.mul(1000).sub(amount1In.mul(3));
            require(balance0Adjusted.mul(balance1Adjusted) >= uint(_reserve0).mul(_reserve1).mul(1000**2), 'UniswapV2: K');

ఇది స్వాప్ నుండి మనం నష్టపోకుండా చూసుకోవడానికి ఒక సానిటీ తనిఖీ. ఒక స్వాప్ reserve0*reserve1 ను తగ్గించే పరిస్థితి ఏదీ లేదు. ఇది కూడా స్వాప్ పై 0.3% రుసుము పంపబడుతుందని మేము నిర్ధారించుకునే చోట; K విలువను సానిటీ తనిఖీ చేసే ముందు, మేము రెండు బ్యాలెన్స్ లను 1000 తో గుణించి, మొత్తాలను 3 తో గుణించి తీసివేస్తాము, అంటే 0.3% (3/1000 = 0.003 = 0.3%) బ్యాలెన్స్ నుండి తీసివేయబడుతుంది, దాని K విలువను ప్రస్తుత నిల్వల K విలువతో పోల్చడానికి ముందు.

        }

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

reserve0 మరియు reserve1 ను నవీకరించండి, మరియు అవసరమైతే ధర సంచితాలు మరియు టైమ్ స్టాంప్ ను నవీకరించి, ఒక సంఘటనను విడుదల చేయండి.

సింక్ లేదా స్కిమ్

నిజమైన బ్యాలెన్స్ లు జత మార్పిడి భావించే నిల్వలతో సమకాలీకరణ కోల్పోవడం సాధ్యమే. ఒప్పందం యొక్క అనుమతి లేకుండా టోకెన్లను ఉపసంహరించుకోవడానికి మార్గం లేదు, కానీ డిపాజిట్లు వేరే విషయం. ఒక ఖాతా mint లేదా swap ను పిలవకుండానే మార్పిడికి టోకెన్లను బదిలీ చేయగలదు.

ఆ సందర్భంలో రెండు పరిష్కారాలు ఉన్నాయి:

  • sync, నిల్వలను ప్రస్తుత బ్యాలెన్స్ లకు నవీకరించండి
  • skim, అదనపు మొత్తాన్ని ఉపసంహరించుకోండి. టోకెన్లను ఎవరు డిపాజిట్ చేశారో మాకు తెలియదు కాబట్టి ఏ ఖాతానైనా skim ను పిలవడానికి అనుమతించబడుతుంది గమనించండి. ఈ సమాచారం ఒక సంఘటనలో విడుదల చేయబడుతుంది, కానీ సంఘటనలు బ్లాక్‌చెయిన్ నుండి అందుబాటులో ఉండవు.

UniswapV2Factory.sol

ఈ ఒప్పందం (opens in a new tab) జత మార్పిడులను సృష్టిస్తుంది.

pragma solidity =0.5.16;

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

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

ఈ స్థితి వేరియబుల్స్ ప్రోటోకాల్ రుసుమును అమలు చేయడానికి అవసరం (వైట్‌పేపర్ (opens in a new tab), p. 5 చూడండి). feeTo చిరునామా ప్రోటోకాల్ రుసుము కోసం ద్రవ్యత్వ టోకెన్లను సేకరిస్తుంది, మరియు feeToSetter feeTo ను వేరే చిరునామాకు మార్చడానికి అనుమతించబడిన చిరునామా.

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

ఈ వేరియబుల్స్ జతలను, రెండు టోకెన్ రకాల మధ్య మార్పిడులను ట్రాక్ చేస్తాయి.

మొదటిది, getPair, ఇది రెండు ERC-20 టోకెన్లు మార్పిడి చేసే జత మార్పిడి ఒప్పందాన్ని గుర్తించే ఒక మ్యాపింగ్. ERC-20 టోకెన్లు వాటిని అమలు చేసే ఒప్పందాల చిరునామాల ద్వారా గుర్తించబడతాయి, కాబట్టి కీలు మరియు విలువ అన్నీ చిరునామాలే. tokenA నుండి tokenB కు మార్చడానికి అనుమతించే జత మార్పిడి చిరునామాను పొందడానికి, మీరు getPair[<tokenA address>][<tokenB address>] (లేదా ఇంకో విధంగా) ఉపయోగిస్తారు.

రెండవ వేరియబుల్, allPairs, ఈ ఫ్యాక్టరీ సృష్టించిన జత మార్పిడుల అన్ని చిరునామాలను కలిగి ఉన్న ఒక శ్రేణి. Ethereum లో మీరు ఒక మ్యాపింగ్ యొక్క కంటెంట్ పై మళ్ళింపు చేయలేరు, లేదా అన్ని కీ ల జాబితాను పొందలేరు, కాబట్టి ఈ ఫ్యాక్టరీ ఏ మార్పిడులను నిర్వహిస్తుందో తెలుసుకోవడానికి ఈ వేరియబుల్ మాత్రమే మార్గం.

గమనిక: మీరు ఒక మ్యాపింగ్ యొక్క అన్ని కీ లపై మళ్ళింపు చేయలేకపోవడానికి కారణం ఒప్పంద డేటా నిల్వ ఖరీదైనది, కాబట్టి దానిని తక్కువగా ఉపయోగించడం మరియు దానిని తక్కువగా మార్చడం మంచిది. మీరు మళ్ళింపుకు మద్దతు ఇచ్చే మ్యాపింగ్స్ (opens in a new tab) ను సృష్టించవచ్చు, కానీ వాటికి కీ ల జాబితా కోసం అదనపు నిల్వ అవసరం. చాలా అప్లికేషన్లలో మీకు అది అవసరం లేదు.

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

ఒక కొత్త జత మార్పిడి సృష్టించబడినప్పుడు ఈ సంఘటన విడుదల చేయబడుతుంది. ఇది టోకెన్ల చిరునామాలు, జత మార్పిడి చిరునామా, మరియు ఫ్యాక్టరీ ద్వారా నిర్వహించబడే మొత్తం మార్పిడుల సంఖ్యను కలిగి ఉంటుంది.

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

నిర్మాణకర్త చేసే ఏకైక పని feeToSetter ను పేర్కొనడం. ఫ్యాక్టరీలు రుసుము లేకుండా ప్రారంభమవుతాయి, మరియు feeSetter మాత్రమే దానిని మార్చగలదు.

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

ఈ ఫంక్షన్ మార్పిడి జతల సంఖ్యను తిరిగి ఇస్తుంది.

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

ఇది ఫ్యాక్టరీ యొక్క ప్రధాన ఫంక్షన్, రెండు ERC-20 టోకెన్ల మధ్య జత మార్పిడిని సృష్టించడం. ఎవరైనా ఈ ఫంక్షన్ ను పిలవవచ్చు గమనించండి. కొత్త జత మార్పిడిని సృష్టించడానికి మీకు యూనిస్వాప్ నుండి అనుమతి అవసరం లేదు.

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

మేము కొత్త మార్పిడి చిరునామాను డిటర్మినిస్టిక్ గా ఉండాలని కోరుకుంటున్నాము, కాబట్టి దానిని ముందుగానే ఆఫ్ చైన్ లో లెక్కించవచ్చు (ఇది /developers/docs/scaling/ లావాదేవీలకు ఉపయోగపడవచ్చు). దీనిని చేయడానికి, మనం వాటిని స్వీకరించిన క్రమంతో సంబంధం లేకుండా టోకెన్ చిరునామాల యొక్క స్థిరమైన క్రమాన్ని కలిగి ఉండాలి, కాబట్టి మనం వాటిని ఇక్కడ క్రమబద్ధీకరిస్తాము.

        require(token0 != address(0), 'UniswapV2: ZERO_ADDRESS');
        require(getPair[token0][token1] == address(0), 'UniswapV2: PAIR_EXISTS'); // ఒకే తనిఖీ సరిపోతుంది

చిన్న వాటి కంటే పెద్ద ద్రవ్యత్వ పూల్స్ మంచివి, ఎందుకంటే వాటికి మరింత స్థిరమైన ధరలు ఉంటాయి. మేము ప్రతి జత టోకెన్లకు ఒకే ద్రవ్యత్వ పూల్ కంటే ఎక్కువ ఉండకూడదని కోరుకుంటున్నాము. ఒకవేళ ఇప్పటికే ఒక మార్పిడి ఉంటే, అదే జత కోసం మరొకటి సృష్టించవలసిన అవసరం లేదు.

        bytes memory bytecode = type(UniswapV2Pair).creationCode;

ఒక కొత్త ఒప్పందాన్ని సృష్టించడానికి మనకు దానిని సృష్టించే కోడ్ అవసరం (నిర్మాణకర్త ఫంక్షన్ మరియు అసలు ఒప్పందం యొక్క EVM బైట్కోడ్ ను మెమరీకి వ్రాసే కోడ్ రెండూ). సాధారణంగా Solidity లో మేము addr = new <name of contract>(<constructor parameters>) ను ఉపయోగిస్తాము మరియు కంపైలర్ మా కోసం అన్నింటినీ చూసుకుంటుంది, కానీ ఒక డిటర్మినిస్టిక్ ఒప్పంద చిరునామాను కలిగి ఉండటానికి మేము CREATE2 ఆప్కోడ్ (opens in a new tab) ను ఉపయోగించాలి. ఈ కోడ్ వ్రాయబడినప్పుడు ఆ ఆప్కోడ్ ఇంకా Solidity ద్వారా మద్దతు ఇవ్వబడలేదు, కాబట్టి కోడ్ ను మాన్యువల్ గా పొందడం అవసరం. ఇది ఇప్పుడు సమస్య కాదు, ఎందుకంటే Solidity ఇప్పుడు CREATE2 కు మద్దతు ఇస్తుంది (opens in a new tab).

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

ఒక ఆప్కోడ్ ఇంకా Solidity ద్వారా మద్దతు ఇవ్వబడనప్పుడు మనం దానిని ఇన్లైన్ అసెంబ్లీ (opens in a new tab) ఉపయోగించి పిలవవచ్చు.

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

కొత్త మార్పిడి ఏ రెండు టోకెన్లను మార్పిడి చేస్తుందో చెప్పడానికి initialize ఫంక్షన్ ను పిలవండి.

        getPair[token0][token1] = pair;
        getPair[token1][token0] = pair; // రివర్స్ దిశలో మ్యాపింగ్ నింపండి
        allPairs.push(pair);
        emit PairCreated(token0, token1, pair, allPairs.length);

కొత్త జత సమాచారాన్ని స్థితి వేరియబుల్స్ లో సేవ్ చేసి, కొత్త జత మార్పిడి గురించి ప్రపంచానికి తెలియజేయడానికి ఒక సంఘటనను విడుదల చేయండి.

ఈ రెండు ఫంక్షన్లు feeSetter రుసుము స్వీకర్తను (ఏదైనా ఉంటే) నియంత్రించడానికి మరియు feeSetter ను కొత్త చిరునామాకు మార్చడానికి అనుమతిస్తాయి.

UniswapV2ERC20.sol

ఈ ఒప్పందం (opens in a new tab) ERC-20 ద్రవ్యత్వ టోకెన్ ను అమలు చేస్తుంది. ఇది ఓపెన్జెప్పెలిన్ ERC-20 ఒప్పందం లాంటిది, కాబట్టి నేను భిన్నమైన భాగాన్ని, permit ఫంక్షనాలిటీని మాత్రమే వివరిస్తాను.

Ethereum పై లావాదేవీలకు ether (ETH) ఖర్చవుతుంది, ఇది నిజమైన డబ్బుతో సమానం. మీ దగ్గర ERC-20 టోకెన్లు ఉండి ETH లేకపోతే, మీరు లావాదేవీలను పంపలేరు, కాబట్టి మీరు వాటితో ఏమీ చేయలేరు. ఈ సమస్యను నివారించడానికి ఒక పరిష్కారం మెటా-లావాదేవీలు (opens in a new tab). టోకెన్ల యజమాని టోకెన్లను ఆఫ్చైన్ ఉపసంహరించుకోవడానికి వేరొకరికి అనుమతించే లావాదేవీపై సంతకం చేసి, దానిని ఇంటర్నెట్ ద్వారా స్వీకర్తకు పంపుతాడు. ETH ఉన్న స్వీకర్త, యజమాని తరఫున అనుమతిని సమర్పిస్తాడు.

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

ఈ హాష్ లావాదేవీ రకం యొక్క ఐడెంటిఫైయర్ (opens in a new tab). మేము ఇక్కడ మద్దతు ఇచ్చే ఏకైక రకం Permit ఈ పారామీటర్లతో.

    mapping(address => uint) public nonces;

ఒక స్వీకర్త డిజిటల్ సంతకాన్ని నకిలీ చేయడం అసాధ్యం. అయినప్పటికీ, అదే లావాదేవీని రెండుసార్లు పంపడం చాలా సులభం (ఇది రీప్లే అటాక్ (opens in a new tab) యొక్క ఒక రూపం). దీనిని నివారించడానికి, మేము ఒక నోన్స్ (opens in a new tab) ను ఉపయోగిస్తాము. ఒక కొత్త Permit యొక్క నోన్స్ చివరిగా ఉపయోగించిన దానికంటే ఒకటి ఎక్కువగా లేకపోతే, మేము దానిని చెల్లనిదిగా భావిస్తాము.

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

ఇది చైన్ ఐడెంటిఫైయర్ (opens in a new tab) ను తిరిగి పొందడానికి కోడ్. ఇది యూల్ (opens in a new tab) అని పిలువబడే EVM అసెంబ్లీ మాండలికాన్ని ఉపయోగిస్తుంది. యూల్ యొక్క ప్రస్తుత వెర్షన్ లో మీరు chainid() ఉపయోగించాలని గమనించండి, chainid కాదు.

EIP-712 కోసం డొమైన్ సెపరేటర్ (opens in a new tab) ను లెక్కించండి.

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

అనుమతులను అమలు చేసే ఫంక్షన్ ఇది. ఇది పారామీటర్లుగా సంబంధిత ఫీల్డ్స్, మరియు సంతకం (opens in a new tab) (v, r, మరియు s) కోసం మూడు స్కేలార్ విలువలను స్వీకరిస్తుంది.

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

గడువు తేదీ తర్వాత లావాదేవీలను అంగీకరించవద్దు.

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

abi.encodePacked(...) అనేది మేము పొందాలని ఆశించే సందేశం. నోన్స్ ఎలా ఉండాలో మనకు తెలుసు, కాబట్టి దానిని పారామీటర్ గా పొందాల్సిన అవసరం లేదు.

Ethereum సంతకం అల్గోరిథం సంతకం చేయడానికి 256 బిట్స్ పొందాలని ఆశిస్తుంది, కాబట్టి మేము keccak256 హాష్ ఫంక్షన్ ను ఉపయోగిస్తాము.

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

డైజెస్ట్ మరియు సంతకం నుండి మనం దానిపై సంతకం చేసిన చిరునామాను ecrecover (opens in a new tab) ఉపయోగించి పొందవచ్చు.

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

అన్నీ సరే అయితే, దీనిని ఒక ERC-20 ఆమోదం (opens in a new tab) గా పరిగణించండి.

పెరిఫెరీ ఒప్పందాలు

పెరిఫెరీ ఒప్పందాలు యూనిస్వాప్ కోసం API (అప్లికేషన్ ప్రోగ్రామ్ ఇంటర్ఫేస్). అవి ఇతర ఒప్పందాల నుండి లేదా వికేంద్రీకృత అప్లికేషన్ల నుండి బాహ్య కాల్స్ కోసం అందుబాటులో ఉన్నాయి. మీరు కోర్ ఒప్పందాలను నేరుగా పిలవవచ్చు, కానీ అది మరింత సంక్లిష్టంగా ఉంటుంది మరియు మీరు పొరపాటు చేస్తే విలువను కోల్పోవచ్చు. కోర్ ఒప్పందాలలో అవి మోసగించబడలేదని నిర్ధారించుకోవడానికి మాత్రమే పరీక్షలు ఉంటాయి, ఇతరుల కోసం సానిటీ తనిఖీలు ఉండవు. అవి పెరిఫెరీలో ఉన్నాయి కాబట్టి వాటిని అవసరమైనప్పుడు నవీకరించవచ్చు.

UniswapV2Router01.sol

ఈ ఒప్పందానికి (opens in a new tab) సమస్యలు ఉన్నాయి, మరియు ఇకపై ఉపయోగించకూడదు (opens in a new tab). అదృష్టవశాత్తు, పెరిఫెరీ ఒప్పందాలు స్థితిరహితంగా ఉంటాయి మరియు ఏ ఆస్తులనూ కలిగి ఉండవు, కాబట్టి దానిని తొలగించి, బదులుగా UniswapV2Router02 ను ఉపయోగించమని ప్రజలకు సూచించడం సులభం.

UniswapV2Router02.sol

చాలా సందర్భాల్లో మీరు ఈ ఒప్పందం (opens in a new tab) ద్వారా యూనిస్వాప్ ను ఉపయోగిస్తారు. మీరు దానిని ఎలా ఉపయోగించాలో ఇక్కడ (opens in a new tab) చూడవచ్చు.

వీటిలో చాలా వరకు మేము ఇంతకుముందు ఎదుర్కొన్నాము, లేదా చాలా స్పష్టంగా ఉన్నాయి. ఒక మినహాయింపు IWETH.sol. యూనిస్వాప్ v2 ఏ జత ERC-20 టోకెన్లకైనా మార్పిడులను అనుమతిస్తుంది, కానీ ether (ETH) స్వయంగా ERC-20 టోకెన్ కాదు. ఇది ప్రమాణానికి ముందుది మరియు ప్రత్యేక యంత్రాంగాల ద్వారా బదిలీ చేయబడుతుంది. ERC-20 టోకెన్లకు వర్తించే ఒప్పందాలలో ETH వాడకాన్ని ప్రారంభించడానికి ప్రజలు చుట్టబడిన ఈథర్ (WETH) (opens in a new tab) ఒప్పందంతో వచ్చారు. మీరు ఈ ఒప్పందానికి ETH ను పంపుతారు, మరియు అది మీకు సమానమైన WETH ను మింట్ చేస్తుంది. లేదా మీరు WETH ను బర్న్ చేసి, ETH ను తిరిగి పొందవచ్చు.

contract UniswapV2Router02 is IUniswapV2Router02 {
    using SafeMath for uint;

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

రౌటర్ ఏ ఫ్యాక్టరీని ఉపయోగించాలో తెలుసుకోవాలి, మరియు WETH అవసరమయ్యే లావాదేవీల కోసం ఏ WETH ఒప్పందాన్ని ఉపయోగించాలో తెలుసుకోవాలి. ఈ విలువలు మార్పులేనివి (opens in a new tab), అంటే అవి నిర్మాణకర్తలో మాత్రమే సెట్ చేయబడతాయి. ఇది వినియోగదారులకు వాటిని తక్కువ నిజాయితీ గల ఒప్పందాలకు మార్చడానికి ఎవరూ మార్చలేరనే విశ్వాసాన్ని ఇస్తుంది.

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

ఈ మాడిఫైయర్ కాలపరిమితి గల లావాదేవీలు ("సమయం Y కంటే ముందు X చేయండి, ఒకవేళ వీలైతే") వాటి కాలపరిమితి తర్వాత జరగకుండా చూస్తుంది.

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

నిర్మాణకర్త మార్పులేని స్థితి వేరియబుల్స్ ను సెట్ చేస్తుంది.

    receive() external payable {
        assert(msg.sender == WETH); // WETH ఒప్పందం నుండి ఫాల్‌బ్యాక్ ద్వారా మాత్రమే ETH ని అంగీకరించండి
    }

WETH ఒప్పందం నుండి టోకెన్లను తిరిగి ETH గా విమోచించుకున్నప్పుడు ఈ ఫంక్షన్ పిలవబడుతుంది. మేము ఉపయోగించే WETH ఒప్పందం మాత్రమే అలా చేయడానికి అధికారం కలిగి ఉంది.

ద్రవ్యతను జోడించండి

ఈ ఫంక్షన్లు జత మార్పిడికి టోకెన్లను జోడిస్తాయి, ఇది ద్రవ్యత్వ పూల్ ను పెంచుతుంది.


    // **** ద్రవ్యతను జోడించండి ****
    function _addLiquidity(

ఈ ఫంక్షన్ జత మార్పిడిలోకి డిపాజిట్ చేయవలసిన A మరియు B టోకెన్ల మొత్తాన్ని లెక్కించడానికి ఉపయోగించబడుతుంది.

        address tokenA,
        address tokenB,

ఇవి ERC-20 టోకెన్ ఒప్పందాల చిరునామాలు.

        uint amountADesired,
        uint amountBDesired,

ఇవి ద్రవ్యత్వ ప్రదాత డిపాజిట్ చేయాలనుకుంటున్న మొత్తాలు. ఇవి డిపాజిట్ చేయవలసిన గరిష్ట మొత్తాలు A మరియు B.

        uint amountAMin,
        uint amountBMin

ఇవి డిపాజిట్ చేయడానికి కనీస ఆమోదయోగ్యమైన మొత్తాలు. లావాదేవీ ఈ మొత్తాలతో లేదా అంతకంటే ఎక్కువతో జరగలేకపోతే, దాని నుండి తిరిగి రండి. మీకు ఈ ఫీచర్ వద్దనుకుంటే, కేవలం సున్నాను పేర్కొనండి.

ద్రవ్యత్వ ప్రదాతలు సాధారణంగా ఒక కనీస మొత్తాన్ని పేర్కొంటారు, ఎందుకంటే వారు లావాదేవీని ప్రస్తుత రేటుకు దగ్గరగా ఉండే మార్పిడి రేటుకు పరిమితం చేయాలనుకుంటారు. మార్పిడి రేటు చాలా ఎక్కువ హెచ్చుతగ్గులకు గురైతే, అది అంతర్లీన విలువలను మార్చే వార్తలను సూచిస్తుంది, మరియు వారు ఏమి చేయాలో మాన్యువల్ గా నిర్ణయించుకోవాలనుకుంటారు.

ఉదాహరణకు, మార్పిడి రేటు ఒకటికి ఒకటిగా ఉన్న ఒక సందర్భాన్ని ఊహించండి మరియు ద్రవ్యత్వ ప్రదాత ఈ విలువలను పేర్కొంటారు:

పరామితివిలువ
కోరిన మొత్తంA1000
కోరిన మొత్తంB1000
కనీస మొత్తంA900
కనీస మొత్తంB800

మార్పిడి రేటు 0.9 మరియు 1.25 మధ్య ఉన్నంత కాలం, లావాదేవీ జరుగుతుంది. మార్పిడి రేటు ఆ పరిధి నుండి బయటకు వెళితే, లావాదేవీ రద్దు చేయబడుతుంది.

ఈ జాగ్రత్తకు కారణం లావాదేవీలు తక్షణమే జరగవు, మీరు వాటిని సమర్పిస్తారు మరియు చివరికి ఒక ధృవీకరణకర్త వాటిని ఒక బ్లాక్‌లో చేర్చబోతున్నాడు (మీ గ్యాస్ ధర చాలా తక్కువగా ఉంటే తప్ప, ఆ సందర్భంలో మీరు దానిని ఓవర్‌రైట్ చేయడానికి అదే నోన్స్ మరియు అధిక గ్యాస్ ధరతో మరొక లావాదేవీని సమర్పించవలసి ఉంటుంది). సమర్పణ మరియు చేర్పు మధ్య వ్యవధిలో ఏమి జరుగుతుందో మీరు నియంత్రించలేరు.

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

ద్రవ్యత్వ ప్రదాత నిల్వల మధ్య ప్రస్తుత నిష్పత్తికి సమానమైన నిష్పత్తిని కలిగి ఉండటానికి డిపాజిట్ చేయవలసిన మొత్తాలను ఫంక్షన్ తిరిగి ఇస్తుంది.

        // ఇంకా జత లేకపోతే దాన్ని సృష్టించండి
        if (IUniswapV2Factory(factory).getPair(tokenA, tokenB) == address(0)) {
            IUniswapV2Factory(factory).createPair(tokenA, tokenB);
        }

ఈ టోకెన్ జత కోసం ఇంకా మార్పిడి లేకపోతే, దానిని సృష్టించండి.

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

జతలో ప్రస్తుత నిల్వలను పొందండి.

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

ప్రస్తుత నిల్వలు ఖాళీగా ఉంటే ఇది కొత్త జత మార్పిడి. డిపాజిట్ చేయవలసిన మొత్తాలు ద్రవ్యత్వ ప్రదాత అందించాలనుకుంటున్న వాటికి సరిగ్గా సమానంగా ఉండాలి.

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

మొత్తాలు ఎలా ఉంటాయో చూడాలనుకుంటే, మేము ఈ ఫంక్షన్ (opens in a new tab) ఉపయోగించి ఉత్తమమైన మొత్తాన్ని పొందుతాము. మేము ప్రస్తుత నిల్వల మాదిరిగానే నిష్పత్తిని కోరుకుంటున్నాము.

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

amountBOptimal ద్రవ్యత్వ ప్రదాత డిపాజిట్ చేయాలనుకుంటున్న మొత్తం కంటే తక్కువగా ఉంటే, టోకెన్ B ప్రస్తుతం ద్రవ్యత్వ డిపాజిటర్ భావించే దానికంటే ఎక్కువ విలువైనదని అర్థం, కాబట్టి తక్కువ మొత్తం అవసరం.

            } 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 టోకెన్లు.

గ్రాఫ్

మీరు ద్రవ్యతను నేరుగా కోర్ ఒప్పందంలోకి డిపాజిట్ చేయవచ్చు ( UniswapV2Pair::mint (opens in a new tab) ఉపయోగించి), కానీ కోర్ ఒప్పందం అది మోసగించబడలేదని మాత్రమే తనిఖీ చేస్తుంది, కాబట్టి మీరు మీ లావాదేవీని సమర్పించిన సమయం మరియు అది అమలు చేయబడిన సమయం మధ్య మార్పిడి రేటు మారితే మీరు విలువను కోల్పోయే ప్రమాదం ఉంది. మీరు పెరిఫెరీ ఒప్పందాన్ని ఉపయోగిస్తే, అది మీరు డిపాజిట్ చేయవలసిన మొత్తాన్ని లెక్కిస్తుంది మరియు దానిని వెంటనే డిపాజిట్ చేస్తుంది, కాబట్టి మార్పిడి రేటు మారదు మరియు మీరు ఏమీ కోల్పోరు.

ఈ ఫంక్షన్ ద్రవ్యతను డిపాజిట్ చేయడానికి ఒక లావాదేవీ ద్వారా పిలవబడవచ్చు. చాలా పారామీటర్లు పైన ఉన్న _addLiquidity లోని వాటితో సమానంగా ఉంటాయి, రెండు మినహాయింపులతో:

. to అనేది ద్రవ్యత్వ ప్రదాత యొక్క పూల్ లోని వాటాను చూపించడానికి ముద్రించబడిన కొత్త ద్రవ్యత్వ టోకెన్లను పొందే చిరునామా . deadline అనేది లావాదేవీపై ఒక కాలపరిమితి

    ) 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 (లైబ్రరీలలో క్రింద చూడండి) ను ఉపయోగిస్తాము.

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

వినియోగదారు నుండి సరైన మొత్తంలో టోకెన్లను జత మార్పిడిలోకి బదిలీ చేయండి.

        liquidity = IUniswapV2Pair(pair).mint(to);

ప్రతిఫలంగా to చిరునామాకు పూల్ లో పాక్షిక యాజమాన్యం కోసం ద్రవ్యత్వ టోకెన్లను ఇవ్వండి. కోర్ ఒప్పందం యొక్క mint ఫంక్షన్ దాని వద్ద ఎన్ని అదనపు టోకెన్లు ఉన్నాయో చూస్తుంది (ద్రవ్యత చివరిసారి మారినప్పుడు దాని వద్ద ఉన్న దానితో పోలిస్తే) మరియు దానికి అనుగుణంగా ద్రవ్యతను మింట్ చేస్తుంది.

    function addLiquidityETH(
        address token,
        uint amountTokenDesired,

ఒక ద్రవ్యత్వ ప్రదాత టోకెన్/ETH జత మార్పిడికి ద్రవ్యతను అందించాలనుకున్నప్పుడు, కొన్ని తేడాలు ఉన్నాయి. ద్రవ్యత్వ ప్రదాత కోసం ETH ను చుట్టే పనిని ఒప్పందం నిర్వహిస్తుంది. వినియోగదారు ఎన్ని ETH డిపాజిట్ చేయాలనుకుంటున్నారో పేర్కొనవలసిన అవసరం లేదు, ఎందుకంటే వినియోగదారు వాటిని లావాదేవీతో పంపుతారు (మొత్తం msg.value లో అందుబాటులో ఉంటుంది).

ETH ను డిపాజిట్ చేయడానికి ఒప్పందం మొదట దానిని WETH గా చుట్టి, ఆపై WETH ను జతలోకి బదిలీ చేస్తుంది. బదిలీ assert లో చుట్టబడిందని గమనించండి. దీని అర్థం బదిలీ విఫలమైతే ఈ ఒప్పంద కాల్ కూడా విఫలమవుతుంది, అందువల్ల చుట్టేది వాస్తవంగా జరగదు.

        liquidity = IUniswapV2Pair(pair).mint(to);
        // దుమ్ము ఈథర్ ను తిరిగి చెల్లించండి, ఏదైనా ఉంటే
        if (msg.value > amountETH) TransferHelper.safeTransferETH(msg.sender, msg.value - amountETH);

వినియోగదారు మాకు ఇప్పటికే ETH ను పంపారు, కాబట్టి ఏదైనా అదనంగా మిగిలి ఉంటే (ఎందుకంటే ఇతర టోకెన్ వినియోగదారు భావించిన దానికంటే తక్కువ విలువైనది), మేము వాపసు జారీ చేయాలి.

ద్రవ్యతను తొలగించండి

ఈ ఫంక్షన్లు ద్రవ్యతను తొలగించి, ద్రవ్యత్వ ప్రదాతకు తిరిగి చెల్లిస్తాయి.

ద్రవ్యతను తొలగించే అత్యంత సులభమైన సందర్భం. ద్రవ్యత్వ ప్రదాత అంగీకరించడానికి ప్రతి టోకెన్ యొక్క కనీస మొత్తం ఉంది, మరియు అది గడువు తేదీకి ముందు జరగాలి.

        address pair = UniswapV2Library.pairFor(factory, tokenA, tokenB);
        IUniswapV2Pair(pair).transferFrom(msg.sender, pair, liquidity); // ద్రవ్యతను జతకు పంపండి
        (uint amount0, uint amount1) = IUniswapV2Pair(pair).burn(to);

కోర్ ఒప్పందం యొక్క burn ఫంక్షన్ వినియోగదారుకు టోకెన్లను తిరిగి చెల్లించే పనిని నిర్వహిస్తుంది.

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

ఒక ఫంక్షన్ బహుళ విలువలను తిరిగి ఇచ్చినప్పుడు, కానీ మనం వాటిలో కొన్నింటిపై మాత్రమే ఆసక్తి కలిగి ఉన్నప్పుడు, మనం ఆ విలువలను మాత్రమే పొందే విధానం ఇది. ఒక విలువను చదివి ఎప్పుడూ ఉపయోగించకపోవడం కంటే గ్యాస్ పరంగా ఇది కొంత చౌకైనది.

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

కోర్ ఒప్పందం వాటిని తిరిగి ఇచ్చే విధానం (తక్కువ చిరునామా టోకెన్ మొదట) నుండి వినియోగదారు వాటిని ఆశించే విధానానికి (tokenA మరియు tokenB కు అనుగుణంగా) మొత్తాలను అనువదించండి.

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

మొదట బదిలీ చేసి, ఆపై అది చట్టబద్ధమైనదేనా అని ధృవీకరించడం సరే, ఎందుకంటే అది కాకపోతే మనం అన్ని స్థితి మార్పుల నుండి తిరిగి వస్తాము.

ETH కోసం ద్రవ్యతను తొలగించడం దాదాపు అదే, కానీ మేము WETH టోకెన్లను స్వీకరించి, ఆపై వాటిని ETH కోసం విమోచించి, ద్రవ్యత్వ ప్రదాతకు తిరిగి ఇస్తాము.

ఈ ఫంక్షన్లు ఈథర్ లేని వినియోగదారులను పూల్ నుండి ఉపసంహరించుకోవడానికి మెటా-లావాదేవీలను రిలే చేస్తాయి, అనుమతి యంత్రాంగం ను ఉపయోగిస్తాయి.

బదిలీ లేదా నిల్వ రుసుములు ఉన్న టోకెన్ల కోసం ఈ ఫంక్షన్ ను ఉపయోగించవచ్చు. ఒక టోకెన్ కు అలాంటి రుసుములు ఉన్నప్పుడు, మేము ఎంత టోకెన్ తిరిగి పొందాలో చెప్పడానికి removeLiquidity ఫంక్షన్ పై ఆధారపడలేము, కాబట్టి మేము మొదట ఉపసంహరించుకుని, ఆపై బ్యాలెన్స్ ను పొందాలి.

తుది ఫంక్షన్ మెటా-లావాదేవీలతో నిల్వ రుసుములను కలుపుతుంది.

వర్తకం

    // **** స్వాప్ ****
    // ప్రారంభ మొత్తాన్ని ఇప్పటికే మొదటి జతకు పంపవలసి ఉంటుంది
    function _swap(uint[] memory amounts, address[] memory path, address _to) internal virtual {

ఈ ఫంక్షన్ వర్తకులకు అందుబాటులో ఉండే ఫంక్షన్‌ల కోసం అవసరమైన అంతర్గత ప్రాసెసింగ్‌ను నిర్వహిస్తుంది.

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

నేను ఇది వ్రాస్తున్నప్పుడు 388,160 ERC-20 టోకెన్లు (opens in a new tab) ఉన్నాయి. ప్రతి టోకెన్ జతకు ఒక జత ఎక్స్ఛేంజ్ ఉంటే, అది 150 బిలియన్లకు పైగా జత ఎక్స్ఛేంజ్‌లు అవుతుంది. ప్రస్తుతానికి, మొత్తం గొలుసులో, ఖాతాల సంఖ్యలో కేవలం 0.1% మాత్రమే ఉంది (opens in a new tab). దానికి బదులుగా, స్వాప్ ఫంక్షన్‌లు పాత్ భావనకు మద్దతు ఇస్తాయి. ఒక వర్తకుడు A ని B కి, B ని C కి, మరియు C ని D కి మార్చుకోవచ్చు, కాబట్టి ప్రత్యక్ష A-D జత మార్పిడి అవసరం లేదు.

ఈ మార్కెట్లలోని ధరలు సమకాలీకరించబడతాయి, ఎందుకంటే అవి సమకాలీకరణలో లేనప్పుడు మధ్యవర్తిత్వానికి అవకాశం ఏర్పడుతుంది. ఉదాహరణకు, మూడు టోకెన్లు A, B, మరియు C అని ఊహించుకోండి. ప్రతి జతకు ఒకటి చొప్పున మూడు జతల మార్పిడిలు ఉన్నాయి.

  1. ప్రారంభ పరిస్థితి
  2. ఒక వర్తకుడు 24.695 A టోకెన్లను అమ్మి, 25.305 B టోకెన్లను పొందుతాడు.
  3. వర్తకుడు 24.695 B టోకెన్లను 25.305 C టోకెన్లకు అమ్మి, లాభంగా సుమారు 0.61 B టోకెన్లను ఉంచుకుంటాడు.
  4. తర్వాత వర్తకుడు 24.695 C టోకెన్లను 25.305 A టోకెన్లకు అమ్మి, సుమారు 0.61 C టోకెన్లను లాభంగా ఉంచుకుంటాడు. వర్తకుడికి 0.61 అదనపు A టోకెన్లు కూడా ఉన్నాయి (వర్తకుడు చివరికి పొందిన 25.305, అసలు పెట్టుబడి అయిన 24.695 మినహా).
దశA-B ఎక్స్ఛేంజ్B-C ఎక్స్ఛేంజ్A-C ఎక్స్ఛేంజ్
1A:1000 B:1050 A/B=1.05B:1000 C:1050 B/C=1.05A:1050 C:1000 C/A=1.05
2A:1024.695 B:1024.695 A/B=1B:1000 C:1050 B/C=1.05A:1050 C:1000 C/A=1.05
3A:1024.695 B:1024.695 A/B=1B:1024.695 C:1024.695 B/C=1A:1050 C:1000 C/A=1.05
4A:1024.695 B:1024.695 A/B=1B:1024.695 C:1024.695 B/C=1A:1024.695 C:1024.695 C/A=1
            (address input, address output) = (path[i], path[i + 1]);
            (address token0,) = UniswapV2Library.sortTokens(input, output);
            uint amountOut = amounts[i + 1];

మనం ప్రస్తుతం నిర్వహిస్తున్న జతను పొందండి, దానిని క్రమబద్ధీకరించండి (జతతో ఉపయోగం కోసం) మరియు ఊహించిన అవుట్‌పుట్ మొత్తాన్ని పొందండి.

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

ఊహించిన అవుట్ మొత్తాలను పొందండి, జత మార్పిడి వాటిని ఆశించే విధంగా క్రమబద్ధీకరించబడింది.

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

ఇది చివరి మార్పిడియా? అవును అయితే, వర్తకం కోసం అందుకున్న టోకెన్లను గమ్యస్థానానికి పంపండి. కాకపోతే, దాన్ని తదుపరి జత మార్పిడికి పంపండి.


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

టోకెన్లను మార్చడానికి జత మార్పిడిని వాస్తవానికి కాల్ చేయండి. మార్పిడి గురించి మాకు తెలియజేయడానికి మాకు కాల్‌బ్యాక్ అవసరం లేదు, కాబట్టి మేము ఆ ఫీల్డ్‌లో ఏ బైట్‌లను పంపము.

    function swapExactTokensForTokens(

ఈ ఫంక్షన్‌ను వర్తకులు ఒక టోకెన్‌ను మరొకదానికి మార్చడానికి నేరుగా ఉపయోగిస్తారు.

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

ఈ పరామితి ERC-20 కాంట్రాక్టుల చిరునామాలను కలిగి ఉంటుంది. పైన వివరించినట్లుగా, ఇది ఒక శ్రేణి ఎందుకంటే మీరు మీ దగ్గర ఉన్న ఆస్తి నుండి మీకు కావలసిన ఆస్తిని పొందడానికి అనేక జత మార్పిడిల ద్వారా వెళ్ళవలసి రావచ్చు.

Solidityలోని ఒక ఫంక్షన్ పరామితిని memory లేదా calldataలో నిల్వ చేయవచ్చు. ఒకవేళ ఫంక్షన్ కాంట్రాక్ట్‌కు ఎంట్రీ పాయింట్ అయితే, వినియోగదారు (ఒక లావాదేవీని ఉపయోగించి) నుండి లేదా వేరే కాంట్రాక్ట్ నుండి నేరుగా కాల్ చేయబడితే, అప్పుడు పరామితి విలువను కాల్ డేటా నుండి నేరుగా తీసుకోవచ్చు. పైన _swap వలె, ఫంక్షన్‌ను అంతర్గతంగా కాల్ చేస్తే, అప్పుడు పరామితులను memoryలో నిల్వ చేయాలి. కాల్ చేయబడిన కాంట్రాక్ట్ దృక్కోణం నుండి calldata కేవలం చదవడానికి మాత్రమే.

uint లేదా address వంటి స్కేలార్ రకాలతో, కంపైలర్ మా కోసం నిల్వ ఎంపికను నిర్వహిస్తుంది, కానీ శ్రేణులతో, అవి పొడవుగా మరియు ఖరీదైనవిగా ఉంటాయి, మేము ఉపయోగించాల్సిన నిల్వ రకాన్ని నిర్దేశిస్తాము.

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

తిరిగి వచ్చే విలువలు ఎల్లప్పుడూ మెమరీలో తిరిగి వస్తాయి.

        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, to);
    }

చివరగా, ప్రారంభ ERC-20 టోకెన్‌ను మొదటి జత మార్పిడి ఖాతాకు బదిలీ చేసి, _swapను కాల్ చేయండి. ఇదంతా ఒకే లావాదేవీలో జరుగుతోంది, కాబట్టి ఊహించని టోకెన్లు ఈ బదిలీలో భాగంగా ఉన్నాయని జత మార్పిడికి తెలుసు.

మునుపటి ఫంక్షన్, swapTokensForTokens, వర్తకుడికి అతను ఇవ్వడానికి ఇష్టపడే ఖచ్చితమైన ఇన్‌పుట్ టోకెన్‌ల సంఖ్యను మరియు దానికి బదులుగా అతను స్వీకరించడానికి ఇష్టపడే కనీస అవుట్‌పుట్ టోకెన్‌ల సంఖ్యను పేర్కొనడానికి అనుమతిస్తుంది. ఈ ఫంక్షన్ రివర్స్ స్వాప్‌ను చేస్తుంది, ఇది వర్తకుడికి అతను కోరుకునే అవుట్‌పుట్ టోకెన్‌ల సంఖ్యను మరియు వాటి కోసం అతను చెల్లించడానికి ఇష్టపడే గరిష్ట ఇన్‌పుట్ టోకెన్‌ల సంఖ్యను పేర్కొనడానికి అనుమతిస్తుంది.

రెండు సందర్భాల్లో, వర్తకుడు మొదట ఈ పెరిఫెరీ కాంట్రాక్ట్‌కు వాటిని బదిలీ చేయడానికి అనుమతించే భత్యాన్ని ఇవ్వాలి.

ఈ నాలుగు వేరియంట్‌లు అన్నీ ETH మరియు టోకెన్ల మధ్య వర్తకంతో ముడిపడి ఉన్నాయి. వర్తకుని నుండి మనం ETHని స్వీకరించి, దానిని WETHని ముద్రించడానికి ఉపయోగించడం లేదా మార్గంలో చివరి మార్పిడి నుండి WETHని స్వీకరించి, దానిని బర్న్ చేసి, ఫలితంగా వచ్చే ETHని వర్తకునికి తిరిగి పంపడం మాత్రమే తేడా.

    // **** స్వాప్ (ఫీ-ఆన్-ట్రాన్స్‌ఫర్ టోకెన్‌లకు మద్దతు) ****
    // ప్రారంభ మొత్తాన్ని ఇప్పటికే మొదటి జతకు పంపవలసి ఉంటుంది
    function _swapSupportingFeeOnTransferTokens(address[] memory path, address _to) internal virtual {

బదిలీ లేదా నిల్వ రుసుములు ఉన్న టోకెన్‌లను మార్చుకోవడానికి ఇది అంతర్గత ఫంక్షన్ (ఈ సమస్యను (opens in a new tab) పరిష్కరించడానికి).

బదిలీ రుసుముల కారణంగా, ప్రతి బదిలీ నుండి మనకు ఎంత లభిస్తుందో చెప్పడానికి getAmountsOut ఫంక్షన్‌పై మేము ఆధారపడలేము (అసలు _swapను కాల్ చేయడానికి ముందు మేము చేసే విధంగా). దానికి బదులుగా మనం మొదట బదిలీ చేసి, తర్వాత మనకు ఎన్ని టోకెన్లు తిరిగి వచ్చాయో చూడాలి.

గమనిక: సిద్ధాంతపరంగా మనం _swapకు బదులుగా ఈ ఫంక్షన్‌ను ఉపయోగించవచ్చు, కానీ కొన్ని సందర్భాల్లో (ఉదాహరణకు, అవసరమైన కనీస మొత్తాన్ని చేరుకోవడానికి చివరిలో తగినంతగా లేనందున బదిలీ తిరిగి మార్చబడితే) దానికి ఎక్కువ గ్యాస్ ఖర్చవుతుంది. ట్రాన్స్‌ఫర్ ఫీ టోకెన్లు చాలా అరుదుగా ఉంటాయి, కాబట్టి వాటికి అనుగుణంగా ఉండాల్సిన అవసరం ఉన్నప్పటికీ, అన్ని స్వాప్‌లు వాటిలో కనీసం ఒకదాని ద్వారా వెళ్తాయని ఊహించుకోవాల్సిన అవసరం లేదు.

ఇవి సాధారణ టోకెన్ల కోసం ఉపయోగించే అదే వేరియంట్‌లు, కానీ అవి బదులుగా _swapSupportingFeeOnTransferTokensను కాల్ చేస్తాయి.

ఈ ఫంక్షన్‌లు UniswapV2Library ఫంక్షన్‌లను కాల్ చేసే ప్రాక్సీలు మాత్రమే.

UniswapV2Migrator.sol

ఈ కాంట్రాక్ట్ పాత v1 నుండి v2 కి ఎక్స్‌ఛేంజ్‌లను మైగ్రేట్ చేయడానికి ఉపయోగించబడింది. ఇప్పుడు అవి మైగ్రేట్ చేయబడ్డాయి, ఇది ఇకపై సంబంధితం కాదు.

లైబ్రరీలు

SafeMath లైబ్రరీ (opens in a new tab) బాగా డాక్యుమెంట్ చేయబడింది, కాబట్టి ఇక్కడ దానిని డాక్యుమెంట్ చేయవలసిన అవసరం లేదు.

గణితం

ఈ లైబ్రరీలో Solidity కోడ్‌లో సాధారణంగా అవసరం లేని కొన్ని గణిత ఫంక్షన్‌లు ఉన్నాయి, కాబట్టి అవి భాషలో భాగం కావు.

xను వర్గమూలానికి కంటే ఎక్కువగా ఉన్న అంచనాతో ప్రారంభించండి (అందుకే మనం 1-3ను ప్రత్యేక సందర్భాలుగా పరిగణించవలసి ఉంటుంది).

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

మరింత దగ్గరి అంచనాను పొందండి, మునుపటి అంచనా యొక్క సగటు మరియు మనం వర్గమూలాన్ని కనుగొనడానికి ప్రయత్నిస్తున్న సంఖ్యను మునుపటి అంచనాతో భాగించడం. కొత్త అంచనా ప్రస్తుత దాని కంటే తక్కువగా లేనంత వరకు పునరావృతం చేయండి. మరిన్ని వివరాల కోసం, ఇక్కడ చూడండి (opens in a new tab).

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

మనకు సున్నా వర్గమూలం ఎప్పుడూ అవసరం లేదు. ఒకటి, రెండు మరియు మూడు వర్గమూలాలు సుమారు ఒకటి (మనం పూర్ణాంకాలను ఉపయోగిస్తాము, కాబట్టి మనం భిన్నాన్ని విస్మరిస్తాము).

        }
    }
}

స్థిర పాయింట్ భిన్నాలు (UQ112x112)

ఈ లైబ్రరీ భిన్నాలను నిర్వహిస్తుంది, ఇవి సాధారణంగా ఇతీరియము అంకగణితంలో భాగంగా ఉండవు. ఇది x సంఖ్యను x*2^112గా ఎన్‌కోడ్ చేయడం ద్వారా చేస్తుంది. ఇది అసలు కూడిక మరియు తీసివేత ఆప్కోడ్లను మార్పు లేకుండా ఉపయోగించడానికి అనుమతిస్తుంది.

Q112 అనేది ఒకటి కోసం ఎన్‌కోడింగ్.

    // ఒక uint112ను UQ112x112గా ఎన్‌కోడ్ చేయండి
    function encode(uint112 y) internal pure returns (uint224 z) {
        z = uint224(y) * Q112; // ఎప్పటికీ ఓవర్‌ఫ్లో కాదు
    }

y అనేది uint112 కాబట్టి, అది గరిష్టంగా 2^112-1 కావచ్చు. ఆ సంఖ్యను ఇప్పటికీ UQ112x112గా ఎన్‌కోడ్ చేయవచ్చు.

    // ఒక UQ112x112ను ఒక uint112తో భాగించి, UQ112x112ను తిరిగి ఇవ్వండి
    function uqdiv(uint224 x, uint112 y) internal pure returns (uint224 z) {
        z = x / uint224(y);
    }
}

మనం రెండు UQ112x112 విలువలను భాగిస్తే, ఫలితం ఇకపై 2^112తో గుణించబడదు. కాబట్టి బదులుగా మనం హారం కోసం ఒక పూర్ణాంకాన్ని తీసుకుంటాము. గుణకారం చేయడానికి కూడా మనం ఇలాంటి ట్రిక్‌ను ఉపయోగించాల్సి ఉండేది, కానీ UQ112x112 విలువల గుణకారం మనం చేయవలసిన అవసరం లేదు.

UniswapV2Library

ఈ లైబ్రరీ కేవలం పెరిఫెరీ కాంట్రాక్టుల ద్వారా మాత్రమే ఉపయోగించబడుతుంది.

రెండు టోకెన్‌లను చిరునామా ద్వారా క్రమబద్ధీకరించండి, తద్వారా వాటి కోసం జత మార్పిడి చిరునామాను పొందగలుగుతాము. ఇది అవసరం ఎందుకంటే లేకపోతే మనకు రెండు అవకాశాలు ఉంటాయి, ఒకటి పరామితులు A,B కోసం మరియు మరొకటి పరామితులు B,A కోసం, ఇది ఒకదాని బదులుగా రెండు మార్పిడిలకు దారి తీస్తుంది.

ఈ ఫంక్షన్ రెండు టోకెన్ల కోసం జత మార్పిడి చిరునామాను లెక్కిస్తుంది. ఈ కాంట్రాక్ట్ CREATE2 ఆప్కోడ్ (opens in a new tab) ఉపయోగించి సృష్టించబడింది, కాబట్టి అది ఉపయోగించే పరామితులను మనం తెలిస్తే, అదే అల్గారిథమ్ ఉపయోగించి చిరునామాను లెక్కించవచ్చు. ఇది ఫ్యాక్టరీని అడగడం కంటే చాలా చౌకైనది, మరియు

    // ఒక జత కోసం రిజర్వ్‌లను పొందుతుంది మరియు క్రమబద్ధీకరిస్తుంది
    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);
    }

ఈ ఫంక్షన్ జత మార్పిడి వద్ద ఉన్న రెండు టోకెన్ల రిజర్వ్‌లను తిరిగి ఇస్తుంది. ఇది టోకెన్‌లను ఏ క్రమంలోనైనా స్వీకరించగలదని మరియు అంతర్గత ఉపయోగం కోసం వాటిని క్రమబద్ధీకరిస్తుందని గమనించండి.

    // ఒక ఆస్తి యొక్క కొంత మొత్తం మరియు జత రిజర్వ్‌లు ఇవ్వబడినప్పుడు, ఇతర ఆస్తి యొక్క సమానమైన మొత్తాన్ని తిరిగి ఇస్తుంది
    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 పొందుతారో తెలియజేస్తుంది. ఈ లెక్కింపు బదిలీ మార్పిడి రేటును మారుస్తుందని పరిగణనలోకి తీసుకుంటుంది.

    // ఒక ఆస్తి యొక్క ఇన్‌పుట్ మొత్తం మరియు జత నిల్వలు ఇచ్చినప్పుడు, ఇతర ఆస్తి యొక్క గరిష్ట అవుట్‌పుట్ మొత్తాన్ని తిరిగి ఇస్తుంది
    function getAmountOut(uint amountIn, uint reserveIn, uint reserveOut) internal pure returns (uint amountOut) {

పైన ఉన్న quote ఫంక్షన్ జత మార్పిడిని ఉపయోగించడానికి ఎటువంటి రుసుము లేనప్పుడు గొప్పగా పనిచేస్తుంది. అయితే, 0.3% మార్పిడి రుసుము ఉన్నట్లయితే, మీరు వాస్తవానికి పొందే మొత్తం తక్కువగా ఉంటుంది. ఈ ఫంక్షన్ మార్పిడి రుసుము తర్వాత మొత్తాన్ని లెక్కిస్తుంది.


        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తో గుణిస్తాము, అదే ప్రభావాన్ని సాధిస్తాము.

    // ఒక ఆస్తి యొక్క అవుట్‌పుట్ మొత్తం మరియు జత నిల్వలు ఇచ్చినప్పుడు, ఇతర ఆస్తి యొక్క అవసరమైన ఇన్‌పుట్ మొత్తాన్ని తిరిగి ఇస్తుంది
    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);
    }

ఈ ఫంక్షన్ సుమారుగా అదే పని చేస్తుంది, కానీ ఇది అవుట్‌పుట్ మొత్తాన్ని పొంది, ఇన్‌పుట్‌ను అందిస్తుంది.

అనేక జతల మార్పిడిల ద్వారా వెళ్ళవలసిన అవసరం ఉన్నప్పుడు ఈ రెండు ఫంక్షన్‌లు విలువలను గుర్తించడంలో సహాయపడతాయి.

బదిలీ సహాయకం

ఈ లైబ్రరీ (opens in a new tab) ERC-20 మరియు ఇతీరియము బదిలీల చుట్టూ విజయవంతమైన తనిఖీలను జోడిస్తుంది, ఇది ఒక రివర్ట్ మరియు false విలువ రిటర్న్‌ను ఒకే విధంగా పరిగణిస్తుంది.

మనం వేరే కాంట్రాక్ట్‌ను రెండు మార్గాలలో ఒకటిగా కాల్ చేయవచ్చు:

  • ఫంక్షన్ కాల్‌ను సృష్టించడానికి ఇంటర్‌ఫేస్ నిర్వచనాన్ని ఉపయోగించండి
  • కాల్‌ను సృష్టించడానికి అప్లికేషన్ బైనరీ ఇంటర్‌ఫేస్ (ఎబిఐ) (opens in a new tab)ని "మాన్యువల్‌గా" ఉపయోగించండి. కోడ్ రచయిత దీన్ని చేయాలని నిర్ణయించుకున్నది ఇదే.
        require(
            success && (data.length == 0 || abi.decode(data, (bool))),
            'TransferHelper::safeApprove: approve failed'
        );
    }

ERC-20 ప్రమాణానికి ముందు సృష్టించబడిన టోకెన్‌లతో వెనుకబడిన అనుకూలత కోసం, ఒక ERC-20 కాల్ రివర్ట్ చేయడం ద్వారా (ఈ సందర్భంలో success false అవుతుంది) లేదా విజయవంతమై, false విలువను తిరిగి ఇవ్వడం ద్వారా (ఈ సందర్భంలో అవుట్‌పుట్ డేటా ఉంటుంది, మరియు దానిని బూలియన్‌గా డీకోడ్ చేస్తే false వస్తుంది) విఫలం కావచ్చు.

ఈ ఫంక్షన్ ERC-20 యొక్క బదిలీ కార్యాచరణను (opens in a new tab) అమలు చేస్తుంది, ఇది ఒక ఖాతా వేరే ఖాతా ద్వారా అందించబడిన భత్యాన్ని ఖర్చు చేయడానికి అనుమతిస్తుంది.

ఈ ఫంక్షన్ ERC-20 యొక్క transferFrom కార్యాచరణను (opens in a new tab) అమలు చేస్తుంది, ఇది ఒక ఖాతా వేరే ఖాతా ద్వారా అందించబడిన భత్యాన్ని ఖర్చు చేయడానికి అనుమతిస్తుంది.


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

ఈ ఫంక్షన్ ఒక ఖాతాకు ఈథర్‌ను బదిలీ చేస్తుంది. వేరే కాంట్రాక్ట్‌కు ఏదైనా కాల్ ఈథర్‌ను పంపడానికి ప్రయత్నించవచ్చు. మనకు ఏ ఫంక్షన్‌ను కాల్ చేయవలసిన అవసరం లేదు కాబట్టి, మనం కాల్‌తో ఏ డేటాను పంపము.

ముగింపు

ఇది సుమారు 50 పేజీల పొడవైన కథనం. మీరు ఇక్కడికి చేరుకున్నట్లయితే, అభినందనలు! ఇప్పటికి మీరు నిజ-జీవిత అప్లికేషన్‌ను వ్రాయడంలో (చిన్న నమూనా ప్రోగ్రామ్‌లకు విరుద్ధంగా) పరిగణనలను అర్థం చేసుకున్నారని మరియు మీ స్వంత వినియోగ కేసుల కోసం కాంట్రాక్టులను వ్రాయగలుగుతున్నారని ఆశిస్తున్నాము.

ఇప్పుడు వెళ్లి ఉపయోగకరమైనది ఏదైనా వ్రాసి మమ్మల్ని ఆశ్చర్యపరచండి.

నా మరిన్ని పనుల కోసం ఇక్కడ చూడండి (opens in a new tab).

పేజీ చివరి నవీకరణ: 3 ఏప్రిల్, 2026

ఈ ట్యుటోరియల్ ఉపయోగకరంగా ఉందా?