सुरुचिपूर्ण और सरल! 200 लाइन कोड के साथ FMZ पर Uniswap V3 से कनेक्ट किया गया
हाल के वर्षों में डेफी अवधारणा की लोकप्रियता के साथ, यूनिस्वैप वी3 विकेंद्रीकृत वित्त (डीएफआई) के क्षेत्र में सबसे लोकप्रिय विषयों में से एक बन गया है। अग्रणी विकेन्द्रीकृत एक्सचेंज प्रोटोकॉल के रूप में, Uniswap V3 अधिक कुशल, अधिक सुरक्षित और बेहतर उपयोगकर्ता अनुभव प्रदान करता है। अब, केवल 200 लाइनों के कोड के साथ, व्यापारी और डेवलपर्स आसानी से FMZ प्लेटफॉर्म पर Uniswap V3 तक पहुंच सकते हैं।
एफएमजेड एक मात्रात्मक व्यापार मंच है जो मात्रात्मक व्यापार रणनीतियों के विकास, बैकटेस्टिंग और वास्तविक समय परिनियोजन का समर्थन करता है। इसके उपयोग में आसान इंटरफ़ेस और शक्तिशाली विशेषताओं के साथ, यह समझना आसान है कि FMZ DeFi व्यापारियों और डेवलपर्स के लिए पहली पसंद क्यों बन रहा है।
यूनिस्वैप V3 को FMZ में एकीकृत करने की प्रक्रिया सरल और समझने में आसान है, और इसे पूरा करने के लिए केवल 200 पंक्तियों के कोड की आवश्यकता होती है। इसका मतलब यह है कि भले ही आप कोडिंग में नए हों, आप आसानी से FMZ पर Uniswap V3 से जुड़ सकते हैं और तुरंत ट्रेडिंग शुरू कर सकते हैं।
FMZ ने बुनियादी वेब3 कार्यों की एक श्रृंखला को समाहित किया है। Uniswap के अलावा, अन्य DEX एक्सचेंजों को भी बहुत कम कोड के साथ समाहित किया जा सकता है। इसके बाद, मैं आपको DeFi अनुप्रयोगों में अवधारणाओं और तकनीकों को स्क्रैच से सीखने के लिए ले जाऊंगा। स्थान की कमी के कारण, निम्नलिखित विवरण सबसे सरल और सबसे समझने योग्य तरीके का उपयोग करने का प्रयास करता है। यह बहुत कठोर नहीं हो सकता है, लेकिन इसे समझना आसान है।
एफएमजेड मंच जनता के लिए खुला है「यूनिस्वैप V3 लेनदेन लाइब्रेरी」
कोड इस प्रकार है:
/* jshint esversion: 7 */
const ABI_Route = '[{"inputs":[{"internalType":"address","name":"_factoryV2","type":"address"},{"internalType":"address","name":"factoryV3","type":"address"},{"internalType":"address","name":"_positionManager","type":"address"},{"internalType":"address","name":"_WETH9","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"WETH9","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"approveMax","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"approveMaxMinusOne","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"approveZeroThenMax","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"approveZeroThenMaxMinusOne","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes","name":"data","type":"bytes"}],"name":"callPositionManager","outputs":[{"internalType":"bytes","name":"result","type":"bytes"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes[]","name":"paths","type":"bytes[]"},{"internalType":"uint128[]","name":"amounts","type":"uint128[]"},{"internalType":"uint24","name":"maximumTickDivergence","type":"uint24"},{"internalType":"uint32","name":"secondsAgo","type":"uint32"}],"name":"checkOracleSlippage","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"path","type":"bytes"},{"internalType":"uint24","name":"maximumTickDivergence","type":"uint24"},{"internalType":"uint32","name":"secondsAgo","type":"uint32"}],"name":"checkOracleSlippage","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"bytes","name":"path","type":"bytes"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"uint256","name":"amountOutMinimum","type":"uint256"}],"internalType":"struct IV3SwapRouter.ExactInputParams","name":"params","type":"tuple"}],"name":"exactInput","outputs":[{"internalType":"uint256","name":"amountOut","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"tokenIn","type":"address"},{"internalType":"address","name":"tokenOut","type":"address"},{"internalType":"uint24","name":"fee","type":"uint24"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"uint256","name":"amountOutMinimum","type":"uint256"},{"internalType":"uint160","name":"sqrtPriceLimitX96","type":"uint160"}],"internalType":"struct IV3SwapRouter.ExactInputSingleParams","name":"params","type":"tuple"}],"name":"exactInputSingle","outputs":[{"internalType":"uint256","name":"amountOut","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"internalType":"bytes","name":"path","type":"bytes"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amountOut","type":"uint256"},{"internalType":"uint256","name":"amountInMaximum","type":"uint256"}],"internalType":"struct IV3SwapRouter.ExactOutputParams","name":"params","type":"tuple"}],"name":"exactOutput","outputs":[{"internalType":"uint256","name":"amountIn","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"tokenIn","type":"address"},{"internalType":"address","name":"tokenOut","type":"address"},{"internalType":"uint24","name":"fee","type":"uint24"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amountOut","type":"uint256"},{"internalType":"uint256","name":"amountInMaximum","type":"uint256"},{"internalType":"uint160","name":"sqrtPriceLimitX96","type":"uint160"}],"internalType":"struct IV3SwapRouter.ExactOutputSingleParams","name":"params","type":"tuple"}],"name":"exactOutputSingle","outputs":[{"internalType":"uint256","name":"amountIn","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"factory","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"factoryV2","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"getApprovalType","outputs":[{"internalType":"enum IApproveAndCall.ApprovalType","name":"","type":"uint8"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"token0","type":"address"},{"internalType":"address","name":"token1","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"amount0Min","type":"uint256"},{"internalType":"uint256","name":"amount1Min","type":"uint256"}],"internalType":"struct IApproveAndCall.IncreaseLiquidityParams","name":"params","type":"tuple"}],"name":"increaseLiquidity","outputs":[{"internalType":"bytes","name":"result","type":"bytes"}],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"token0","type":"address"},{"internalType":"address","name":"token1","type":"address"},{"internalType":"uint24","name":"fee","type":"uint24"},{"internalType":"int24","name":"tickLower","type":"int24"},{"internalType":"int24","name":"tickUpper","type":"int24"},{"internalType":"uint256","name":"amount0Min","type":"uint256"},{"internalType":"uint256","name":"amount1Min","type":"uint256"},{"internalType":"address","name":"recipient","type":"address"}],"internalType":"struct IApproveAndCall.MintParams","name":"params","type":"tuple"}],"name":"mint","outputs":[{"internalType":"bytes","name":"result","type":"bytes"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"previousBlockhash","type":"bytes32"},{"internalType":"bytes[]","name":"data","type":"bytes[]"}],"name":"multicall","outputs":[{"internalType":"bytes[]","name":"","type":"bytes[]"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"bytes[]","name":"data","type":"bytes[]"}],"name":"multicall","outputs":[{"internalType":"bytes[]","name":"","type":"bytes[]"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes[]","name":"data","type":"bytes[]"}],"name":"multicall","outputs":[{"internalType":"bytes[]","name":"results","type":"bytes[]"}],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"positionManager","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"pull","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"refundETH","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"selfPermit","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"uint256","name":"expiry","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"selfPermitAllowed","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"uint256","name":"expiry","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"selfPermitAllowedIfNecessary","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"selfPermitIfNecessary","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"uint256","name":"amountOutMin","type":"uint256"},{"internalType":"address[]","name":"path","type":"address[]"},{"internalType":"address","name":"to","type":"address"}],"name":"swapExactTokensForTokens","outputs":[{"internalType":"uint256","name":"amountOut","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountOut","type":"uint256"},{"internalType":"uint256","name":"amountInMax","type":"uint256"},{"internalType":"address[]","name":"path","type":"address[]"},{"internalType":"address","name":"to","type":"address"}],"name":"swapTokensForExactTokens","outputs":[{"internalType":"uint256","name":"amountIn","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amountMinimum","type":"uint256"},{"internalType":"address","name":"recipient","type":"address"}],"name":"sweepToken","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amountMinimum","type":"uint256"}],"name":"sweepToken","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amountMinimum","type":"uint256"},{"internalType":"uint256","name":"feeBips","type":"uint256"},{"internalType":"address","name":"feeRecipient","type":"address"}],"name":"sweepTokenWithFee","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amountMinimum","type":"uint256"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"feeBips","type":"uint256"},{"internalType":"address","name":"feeRecipient","type":"address"}],"name":"sweepTokenWithFee","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"int256","name":"amount0Delta","type":"int256"},{"internalType":"int256","name":"amount1Delta","type":"int256"},{"internalType":"bytes","name":"_data","type":"bytes"}],"name":"uniswapV3SwapCallback","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountMinimum","type":"uint256"},{"internalType":"address","name":"recipient","type":"address"}],"name":"unwrapWETH9","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountMinimum","type":"uint256"}],"name":"unwrapWETH9","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountMinimum","type":"uint256"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"feeBips","type":"uint256"},{"internalType":"address","name":"feeRecipient","type":"address"}],"name":"unwrapWETH9WithFee","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountMinimum","type":"uint256"},{"internalType":"uint256","name":"feeBips","type":"uint256"},{"internalType":"address","name":"feeRecipient","type":"address"}],"name":"unwrapWETH9WithFee","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"value","type":"uint256"}],"name":"wrapETH","outputs":[],"stateMutability":"payable","type":"function"},{"stateMutability":"payable","type":"receive"}]';
const ABI_Pool = '[{\"inputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"int24\",\"name\":\"tickLower\",\"type\":\"int24\"},{\"indexed\":true,\"internalType\":\"int24\",\"name\":\"tickUpper\",\"type\":\"int24\"},{\"indexed\":false,\"internalType\":\"uint128\",\"name\":\"amount\",\"type\":\"uint128\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount0\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount1\",\"type\":\"uint256\"}],\"name\":\"Burn\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"int24\",\"name\":\"tickLower\",\"type\":\"int24\"},{\"indexed\":true,\"internalType\":\"int24\",\"name\":\"tickUpper\",\"type\":\"int24\"},{\"indexed\":false,\"internalType\":\"uint128\",\"name\":\"amount0\",\"type\":\"uint128\"},{\"indexed\":false,\"internalType\":\"uint128\",\"name\":\"amount1\",\"type\":\"uint128\"}],\"name\":\"Collect\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint128\",\"name\":\"amount0\",\"type\":\"uint128\"},{\"indexed\":false,\"internalType\":\"uint128\",\"name\":\"amount1\",\"type\":\"uint128\"}],\"name\":\"CollectProtocol\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount0\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount1\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"paid0\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"paid1\",\"type\":\"uint256\"}],\"name\":\"Flash\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint16\",\"name\":\"observationCardinalityNextOld\",\"type\":\"uint16\"},{\"indexed\":false,\"internalType\":\"uint16\",\"name\":\"observationCardinalityNextNew\",\"type\":\"uint16\"}],\"name\":\"IncreaseObservationCardinalityNext\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint160\",\"name\":\"sqrtPriceX96\",\"type\":\"uint160\"},{\"indexed\":false,\"internalType\":\"int24\",\"name\":\"tick\",\"type\":\"int24\"}],\"name\":\"Initialize\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"int24\",\"name\":\"tickLower\",\"type\":\"int24\"},{\"indexed\":true,\"internalType\":\"int24\",\"name\":\"tickUpper\",\"type\":\"int24\"},{\"indexed\":false,\"internalType\":\"uint128\",\"name\":\"amount\",\"type\":\"uint128\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount0\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount1\",\"type\":\"uint256\"}],\"name\":\"Mint\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint8\",\"name\":\"feeProtocol0Old\",\"type\":\"uint8\"},{\"indexed\":false,\"internalType\":\"uint8\",\"name\":\"feeProtocol1Old\",\"type\":\"uint8\"},{\"indexed\":false,\"internalType\":\"uint8\",\"name\":\"feeProtocol0New\",\"type\":\"uint8\"},{\"indexed\":false,\"internalType\":\"uint8\",\"name\":\"feeProtocol1New\",\"type\":\"uint8\"}],\"name\":\"SetFeeProtocol\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"int256\",\"name\":\"amount0\",\"type\":\"int256\"},{\"indexed\":false,\"internalType\":\"int256\",\"name\":\"amount1\",\"type\":\"int256\"},{\"indexed\":false,\"internalType\":\"uint160\",\"name\":\"sqrtPriceX96\",\"type\":\"uint160\"},{\"indexed\":false,\"internalType\":\"uint128\",\"name\":\"liquidity\",\"type\":\"uint128\"},{\"indexed\":false,\"internalType\":\"int24\",\"name\":\"tick\",\"type\":\"int24\"}],\"name\":\"Swap\",\"type\":\"event\"},{\"inputs\":[{\"internalType\":\"int24\",\"name\":\"tickLower\",\"type\":\"int24\"},{\"internalType\":\"int24\",\"name\":\"tickUpper\",\"type\":\"int24\"},{\"internalType\":\"uint128\",\"name\":\"amount\",\"type\":\"uint128\"}],\"name\":\"burn\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"amount0\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"amount1\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"},{\"internalType\":\"int24\",\"name\":\"tickLower\",\"type\":\"int24\"},{\"internalType\":\"int24\",\"name\":\"tickUpper\",\"type\":\"int24\"},{\"internalType\":\"uint128\",\"name\":\"amount0Requested\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"amount1Requested\",\"type\":\"uint128\"}],\"name\":\"collect\",\"outputs\":[{\"internalType\":\"uint128\",\"name\":\"amount0\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"amount1\",\"type\":\"uint128\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"},{\"internalType\":\"uint128\",\"name\":\"amount0Requested\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"amount1Requested\",\"type\":\"uint128\"}],\"name\":\"collectProtocol\",\"outputs\":[{\"internalType\":\"uint128\",\"name\":\"amount0\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"amount1\",\"type\":\"uint128\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"factory\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"fee\",\"outputs\":[{\"internalType\":\"uint24\",\"name\":\"\",\"type\":\"uint24\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"feeGrowthGlobal0X128\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"feeGrowthGlobal1X128\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount0\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"amount1\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"}],\"name\":\"flash\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint16\",\"name\":\"observationCardinalityNext\",\"type\":\"uint16\"}],\"name\":\"increaseObservationCardinalityNext\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint160\",\"name\":\"sqrtPriceX96\",\"type\":\"uint160\"}],\"name\":\"initialize\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"liquidity\",\"outputs\":[{\"internalType\":\"uint128\",\"name\":\"\",\"type\":\"uint128\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"maxLiquidityPerTick\",\"outputs\":[{\"internalType\":\"uint128\",\"name\":\"\",\"type\":\"uint128\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"},{\"internalType\":\"int24\",\"name\":\"tickLower\",\"type\":\"int24\"},{\"internalType\":\"int24\",\"name\":\"tickUpper\",\"type\":\"int24\"},{\"internalType\":\"uint128\",\"name\":\"amount\",\"type\":\"uint128\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"}],\"name\":\"mint\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"amount0\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"amount1\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"observations\",\"outputs\":[{\"internalType\":\"uint32\",\"name\":\"blockTimestamp\",\"type\":\"uint32\"},{\"internalType\":\"int56\",\"name\":\"tickCumulative\",\"type\":\"int56\"},{\"internalType\":\"uint160\",\"name\":\"secondsPerLiquidityCumulativeX128\",\"type\":\"uint160\"},{\"internalType\":\"bool\",\"name\":\"initialized\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint32[]\",\"name\":\"secondsAgos\",\"type\":\"uint32[]\"}],\"name\":\"observe\",\"outputs\":[{\"internalType\":\"int56[]\",\"name\":\"tickCumulatives\",\"type\":\"int56[]\"},{\"internalType\":\"uint160[]\",\"name\":\"secondsPerLiquidityCumulativeX128s\",\"type\":\"uint160[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"name\":\"positions\",\"outputs\":[{\"internalType\":\"uint128\",\"name\":\"liquidity\",\"type\":\"uint128\"},{\"internalType\":\"uint256\",\"name\":\"feeGrowthInside0LastX128\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"feeGrowthInside1LastX128\",\"type\":\"uint256\"},{\"internalType\":\"uint128\",\"name\":\"tokensOwed0\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"tokensOwed1\",\"type\":\"uint128\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"protocolFees\",\"outputs\":[{\"internalType\":\"uint128\",\"name\":\"token0\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"token1\",\"type\":\"uint128\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint8\",\"name\":\"feeProtocol0\",\"type\":\"uint8\"},{\"internalType\":\"uint8\",\"name\":\"feeProtocol1\",\"type\":\"uint8\"}],\"name\":\"setFeeProtocol\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"slot0\",\"outputs\":[{\"internalType\":\"uint160\",\"name\":\"sqrtPriceX96\",\"type\":\"uint160\"},{\"internalType\":\"int24\",\"name\":\"tick\",\"type\":\"int24\"},{\"internalType\":\"uint16\",\"name\":\"observationIndex\",\"type\":\"uint16\"},{\"internalType\":\"uint16\",\"name\":\"observationCardinality\",\"type\":\"uint16\"},{\"internalType\":\"uint16\",\"name\":\"observationCardinalityNext\",\"type\":\"uint16\"},{\"internalType\":\"uint8\",\"name\":\"feeProtocol\",\"type\":\"uint8\"},{\"internalType\":\"bool\",\"name\":\"unlocked\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"int24\",\"name\":\"tickLower\",\"type\":\"int24\"},{\"internalType\":\"int24\",\"name\":\"tickUpper\",\"type\":\"int24\"}],\"name\":\"snapshotCumulativesInside\",\"outputs\":[{\"internalType\":\"int56\",\"name\":\"tickCumulativeInside\",\"type\":\"int56\"},{\"internalType\":\"uint160\",\"name\":\"secondsPerLiquidityInsideX128\",\"type\":\"uint160\"},{\"internalType\":\"uint32\",\"name\":\"secondsInside\",\"type\":\"uint32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"},{\"internalType\":\"bool\",\"name\":\"zeroForOne\",\"type\":\"bool\"},{\"internalType\":\"int256\",\"name\":\"amountSpecified\",\"type\":\"int256\"},{\"internalType\":\"uint160\",\"name\":\"sqrtPriceLimitX96\",\"type\":\"uint160\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"}],\"name\":\"swap\",\"outputs\":[{\"internalType\":\"int256\",\"name\":\"amount0\",\"type\":\"int256\"},{\"internalType\":\"int256\",\"name\":\"amount1\",\"type\":\"int256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"int16\",\"name\":\"\",\"type\":\"int16\"}],\"name\":\"tickBitmap\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"tickSpacing\",\"outputs\":[{\"internalType\":\"int24\",\"name\":\"\",\"type\":\"int24\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"int24\",\"name\":\"\",\"type\":\"int24\"}],\"name\":\"ticks\",\"outputs\":[{\"internalType\":\"uint128\",\"name\":\"liquidityGross\",\"type\":\"uint128\"},{\"internalType\":\"int128\",\"name\":\"liquidityNet\",\"type\":\"int128\"},{\"internalType\":\"uint256\",\"name\":\"feeGrowthOutside0X128\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"feeGrowthOutside1X128\",\"type\":\"uint256\"},{\"internalType\":\"int56\",\"name\":\"tickCumulativeOutside\",\"type\":\"int56\"},{\"internalType\":\"uint160\",\"name\":\"secondsPerLiquidityOutsideX128\",\"type\":\"uint160\"},{\"internalType\":\"uint32\",\"name\":\"secondsOutside\",\"type\":\"uint32\"},{\"internalType\":\"bool\",\"name\":\"initialized\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"token0\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"token1\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"}]'
const ABI_Factory = '[{\"inputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint24\",\"name\":\"fee\",\"type\":\"uint24\"},{\"indexed\":true,\"internalType\":\"int24\",\"name\":\"tickSpacing\",\"type\":\"int24\"}],\"name\":\"FeeAmountEnabled\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"oldOwner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"OwnerChanged\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"token0\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"token1\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"uint24\",\"name\":\"fee\",\"type\":\"uint24\"},{\"indexed\":false,\"internalType\":\"int24\",\"name\":\"tickSpacing\",\"type\":\"int24\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"pool\",\"type\":\"address\"}],\"name\":\"PoolCreated\",\"type\":\"event\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"tokenA\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"tokenB\",\"type\":\"address\"},{\"internalType\":\"uint24\",\"name\":\"fee\",\"type\":\"uint24\"}],\"name\":\"createPool\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"pool\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint24\",\"name\":\"fee\",\"type\":\"uint24\"},{\"internalType\":\"int24\",\"name\":\"tickSpacing\",\"type\":\"int24\"}],\"name\":\"enableFeeAmount\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint24\",\"name\":\"\",\"type\":\"uint24\"}],\"name\":\"feeAmountTickSpacing\",\"outputs\":[{\"internalType\":\"int24\",\"name\":\"\",\"type\":\"int24\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"},{\"internalType\":\"uint24\",\"name\":\"\",\"type\":\"uint24\"}],\"name\":\"getPool\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"parameters\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"factory\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"token0\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"token1\",\"type\":\"address\"},{\"internalType\":\"uint24\",\"name\":\"fee\",\"type\":\"uint24\"},{\"internalType\":\"int24\",\"name\":\"tickSpacing\",\"type\":\"int24\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_owner\",\"type\":\"address\"}],\"name\":\"setOwner\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]'
let ContractV3Factory = "0x1F98431c8aD98523631AE4a59f267346ea31F984"
let ContractV3SwapRouterV2 = "0x68b3465833fb72A70ecDF485E0e4C7bD8665Fc45"
function computePoolPrice(decimals0, decimals1, sqrtPriceX96) {
[decimals0, decimals1, sqrtPriceX96] = [decimals0, decimals1, sqrtPriceX96].map(BigInt);
const TWO = BigInt(2);
const TEN = BigInt(10);
const SIX_TENTH = BigInt(1000000);
const Q192 = (TWO ** BigInt(96)) ** TWO;
return (
Number((sqrtPriceX96 ** TWO * TEN ** decimals0 * SIX_TENTH) / (Q192 * TEN ** decimals1)) /
Number(SIX_TENTH)
);
}
function toAmount(s, decimals) {
return Number((BigDecimal(BigInt(s))/BigDecimal(Math.pow(10, decimals))).toString())
}
function toInnerAmount(n, decimals) {
return (BigDecimal(n)*BigDecimal(Math.pow(10,decimals))).toFixed(0)
}
$.NewUniswapV3 = function(e) {
e = e || exchange
if (e.GetName() !== 'Web3') {
panic("only support Web3 exchange")
}
let self = {
tokenInfo: {},
walletAddress: e.IO("address"),
pool: {}
}
// register
e.IO("abi", ContractV3Factory, ABI_Factory)
e.IO("abi", ContractV3SwapRouterV2, ABI_Route)
self.addToken = function(name, address) {
let ret = e.IO("api", address, "decimals")
if (!ret) {
throw "get token decimals failed"
}
let decimals = Number(ret)
self.tokenInfo[name] = {
name: name,
decimals: decimals,
address: address
}
}
self.waitMined = function(tx) {
while (true) {
Sleep(1000)
let info = e.IO("api", "eth", "eth_getTransactionReceipt", tx)
if (info && info.gasUsed) {
return true
}
Log('Transaction not yet mined', tx)
}
}
self.swapToken = function(tokenIn, amountInDecimal, tokenOut, options) {
// options like {gasPrice: 11, gasLimit: 111, nonce: 111}
let tokenInInfo = self.tokenInfo[tokenIn]
let tokenOutInfo = self.tokenInfo[tokenOut]
if (!tokenInInfo) {
throw "not found token info " + tokenIn
}
if (!tokenOutInfo) {
throw "not found token info " + tokenOut
}
let amountIn = toInnerAmount(amountInDecimal, tokenInInfo.decimals)
let recipientAddress = self.walletAddress
if (tokenInInfo.name != 'ETH') {
let allowanceAmount = e.IO("api", tokenInInfo.address, "allowance", self.walletAddress, ContractV3SwapRouterV2);
let realAmount = toAmount(allowanceAmount, tokenInInfo.decimals)
if (realAmount < toAmount(amountIn, tokenInInfo.decimals)) {
Log("realAmount is", realAmount, "too small, try to approve large amount")
if (tokenInInfo.name == 'USDT') {
// As described in Tether code: To change the approve amount you first have to reduce the addresses allowance to 0 calling approve(spender, 0)
let txApprove = e.IO("api", tokenInInfo.address, "approve", ContractV3SwapRouterV2, 0)
if (!txApprove) {
throw "approve error"
}
Log("wait reduce approve", txApprove)
self.waitMined(txApprove)
}
let txApprove = e.IO("api", tokenInInfo.address, "approve", ContractV3SwapRouterV2, '0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff');
if (!txApprove) {
throw "approve error"
}
Log("wait approve", txApprove)
self.waitMined(txApprove)
Log("approve success amountIn", amountIn)
} else {
Log("allowance", realAmount, "no need to approve")
}
}
if (tokenOutInfo.name == 'ETH' || tokenOutInfo.address.toLowerCase() == '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2') {
/*
ADDRESS_THIS https://degencode.substack.com/p/uniswapv3-multicall
https://etherscan.io/address/0x68b3465833fb72a70ecdf485e0e4c7bd8665fc45#code
*/
recipientAddress = '0x0000000000000000000000000000000000000002'
}
let swapToken = e.IO("encode", ContractV3SwapRouterV2, "swapExactTokensForTokens", amountIn, 1, [tokenInInfo.address, tokenOutInfo.address], recipientAddress)
let data = [swapToken]
if (tokenOutInfo.name == 'ETH') {
data.push(e.IO("encode", ContractV3SwapRouterV2, "unwrapWETH9(uint256,address)", 1, self.walletAddress))
}
let tx = e.IO("api", ContractV3SwapRouterV2, "multicall(uint256,bytes[])", (tokenInInfo.name == 'ETH' ? amountIn : 0), (new Date().getTime() / 1000) + 3600, data, options || {})
if (tx) {
Log("tx: ", tx)
self.waitMined(tx)
Log("swap", tokenInInfo.name, "to", tokenOutInfo.name, "success")
return true
} else {
Log("trans error")
return false
}
}
self.getETHBalance = function(address) {
return toAmount(e.IO("api", "eth", "eth_getBalance", address || self.walletAddress, "latest"), 18)
}
self.balanceOf = function(token, address) {
let tokenInfo = self.tokenInfo[token]
if (!tokenInfo) {
throw "not found token info " + token
}
return toAmount(e.IO("api", tokenInfo.address, "balanceOf", address || self.walletAddress), tokenInfo.decimals)
}
self.sendETH = function(to, amount, options) {
return e.IO("api", "eth", "send", to, toInnerAmount(amount, 18), options || {})
}
self.getPrice = function(pair) {
let arr = pair.split('_')
let token0 = self.tokenInfo[arr[0]]
if (!token0) {
throw "token " + arr[0] + "not found"
}
let token1 = self.tokenInfo[arr[1]]
if (!token1) {
throw "token " + arr[1] + "not found"
}
let reverse = false
if (BigInt(token0.address) > BigInt(token1.address)) {
let tmp = token0
token0 = token1
token1 = tmp
reverse = true
}
let key = token0.address + '/' + token1.address
if (typeof(self.pool[key]) == 'undefined') {
let pool = e.IO("api", ContractV3Factory, "getPool", token0.address, token1.address, 3000)
if (pool) {
self.pool[key] = pool
// register pool address
e.IO("abi", pool, ABI_Pool)
}
}
if (typeof(self.pool[key]) == 'undefined') {
throw "pool " + pair + " not found"
}
let slot0 = e.IO("api", self.pool[key], "slot0")
if (!slot0) {
return null
}
let price = computePoolPrice(token0.decimals, token1.decimals, slot0.sqrtPriceX96)
if (reverse) {
price = 1 / price
}
return price
}
return self
}
$.testUniswap = function() {
let ex = $.NewUniswapV3()
Log("walletAddress: ", ex.walletAddress)
let tokenAddressMap = {
"ETH": "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", // WETH
"USDT": "0xdac17f958d2ee523a2206206994597c13d831ec7",
"1INCH": "0x111111111117dC0aa78b770fA6A738034120C302",
}
for (let name in tokenAddressMap) {
ex.addToken(name, tokenAddressMap[name])
}
Log(ex.getPrice('ETH_USDT'))
Log(ex.getPrice('1INCH_USDT'))
// swap 0.01 ETH to USDT
Log(ex.swapToken('ETH', 0.01, 'USDT'))
let usdtBalance = ex.balanceOf('USDT')
Log("balance of USDT", usdtBalance)
// swap reverse
Log(ex.swapToken('USDT', usdtBalance, 'ETH'))
Log("balance of ETH", ex.getETHBalance())
// Log(ex.sendETH('0x11111', 0.02))
}
एथेरियम नेटवर्क
एथेरियम नेटवर्क को एक सॉफ्टवेयर इंफ्रास्ट्रक्चर के रूप में समझा जा सकता है जिस पर विभिन्न स्मार्ट कॉन्ट्रैक्ट्स को तैनात और चलाया जा सकता है। स्मार्ट कॉन्ट्रैक्ट्स के विभिन्न कार्य और अनुप्रयोग परिदृश्य होते हैं। एथेरियम क्लाइंट चलाने वाले उपकरण एथेरियम नेटवर्क में नोड्स का गठन करते हैं।
Uniswap V3 में कुछ अवधारणाएँ
अनजानUniswap V3जो लोग इस प्रोटोकॉल में नए हैं, उन्हें पहले कुछ अवधारणाओं को समझना होगा।Uniswap V3यह भी एक स्मार्ट अनुबंध है जो एथेरियम पर तैनात और संचालित होता है।
- रूट: रूट भी एक स्मार्ट अनुबंध है जिसका उपयोग प्रबंधन के लिए किया जाता है
tokenअदला-बदली। - पूल: पूल भी एक स्मार्ट अनुबंध है जिसका उपयोग दो एथेरियम टोकन को संग्रहीत करने और उनके बीच विनिमय करने के लिए किया जाता है।
- फैक्ट्री अनुबंध: फैक्ट्री अनुबंध एक स्मार्ट अनुबंध है जिसका उपयोग पूल बनाने के लिए किया जाता है।
- एबीआई: (एप्लीकेशन बाइनरी इंटरफ़ेस) एक विनिर्देश है जो बताता है कि स्मार्ट अनुबंध बाहरी दुनिया के साथ कैसे संचार करते हैं। यह स्मार्ट अनुबंध के फ़ंक्शन नाम, पैरामीटर प्रकार और वापसी मान प्रकार को निर्दिष्ट करता है, साथ ही डेटा को एनकोड और डिकोड करने का तरीका भी बताता है, और स्मार्ट अनुबंध के बाहरी इंटरफ़ेस को निर्धारित करता है। यह समझा जा सकता है कि किसी निश्चित इंटरफ़ेस को कॉल करने के लिए, इसे इंटरफ़ेस द्वारा सहमत मानकों के अनुसार कॉल किया जाना चाहिए, और एबीआई सहमत मानकों की एक श्रृंखला रिकॉर्ड करता है।
एक बार जब कोई स्मार्ट अनुबंध एथेरियम पर तैनात हो जाता है, तो उसका एक पता होता है।
Uniswap V3 ट्रेडिंग लाइब्रेरी कोड का विश्लेषण
Uniswap V3 ट्रेडिंग लाइब्रेरी कोड मुख्य रूप से 4 भागों में विभाजित है। आइए उन्हें एक-एक करके समझाते हैं।
भाग 1: Uniswap V3 के साथ इंटरैक्ट करते समय उपयोग किए जाने वाले स्थिरांक
const ABI_Route = '[{"inputs":[{"internalType":"address...
const ABI_Pool = '[{\"inputs\":[],\"stateMutability\":\"nonpayable...
const ABI_Factory = '[{\"inputs\":[],\"stateMutability\":\"...
let ContractV3Factory = "0x1F98431c8aD98523631AE4a59f267346ea31F984"
let ContractV3SwapRouterV2 = "0x68b3465833fb72A70ecDF485E0e4C7bD8665Fc45"
उपरोक्त बुनियादी अवधारणाओं को समझ लेने के बाद, इसे समझना आसान है।
ABI_Routeइस स्थिरांक में संग्रहीत स्ट्रिंग राउटर स्मार्ट अनुबंध का ABI है।
ABI_Poolभंडारण पूल अनुबंध का एबीआई.
ABI_Factoryफैक्ट्री अनुबंध के लिए एबीआई.
चूंकि ये तार बहुत लंबे हैं, इसलिए ये केवल अंश हैं। ये सामग्री प्रोग्राम को स्मार्ट अनुबंध विधियों को कॉल करने के लिए मानक प्रदान करती है (उदाहरण के लिए, इस स्मार्ट अनुबंध इंटरफ़ेस के पैरामीटर क्या हैं, कितने पैरामीटर हैं, वे किस प्रकार के हैं, लौटाया गया डेटा किस प्रकार का है, आदि)।
हमने पहले बताया था कि जब कोई स्मार्ट अनुबंध एथेरियम पर तैनात हो जाता है, तो उसका एक पता होता है।
ContractV3Factory: फैक्ट्री अनुबंध का पता रिकॉर्ड करता है।
ContractV3SwapRouterV2: Uniswap V3 का राउटर V2 पता। कृपया ध्यान दें कि Uniswap में V1 और V2 हैं, और Uniswap V3 के राउटर में भी V1 और V2 हैं। विभिन्न अनुबंधों के पते अलग-अलग हैं।
भाग 2: उपकरण कार्य
1、computePoolPriceइस फ़ंक्शन का उपयोग पूल में टोकन की कीमत की गणना करने के लिए किया जाता है।
function computePoolPrice(decimals0, decimals1, sqrtPriceX96) {
[decimals0, decimals1, sqrtPriceX96] = [decimals0, decimals1, sqrtPriceX96].map(BigInt); // 使用BigInt函数处理,因为JavaScript语言数值精度的原因,需要使用FMZ的一个底层处理函数BigInt来处理
const TWO = BigInt(2); // 定义常量2用于计算
const TEN = BigInt(10); // 定义常量10用于计算
const SIX_TENTH = BigInt(1000000); // 定义常量10的6次方,即1e6
const Q192 = (TWO ** BigInt(96)) ** TWO; // 2^192
return (
Number((sqrtPriceX96 ** TWO * TEN ** decimals0 * SIX_TENTH) / (Q192 * TEN ** decimals1)) /
Number(SIX_TENTH)
);
}
यदि ट्रेडिंग जोड़ी हैETH_USDT,इसलिएtoken0हाँETH,token1हाँUSDT。decimals0वह हैtoken0सटीकता डेटा,decimals1वह हैtoken1सटीकता डेटा.sqrtPriceX96यह मूल्य से संबंधित डेटा है (प्रत्यक्ष मूल्य मान नहीं), जिसे पूल अनुबंध से प्राप्त किया जा सकता हैslot0विधि अधिग्रहण.
sqrtPriceX96 : The current price of the pool as a sqrt(token1/token0) Q64.96 value
Q64.96 एक डेटा प्रसंस्करण भंडारण मानक है।
decimals0,decimals1, sqrtPriceX96ये तीन डेटा पैरामीटर के रूप में पास किए जाते हैंcomputePoolPriceफ़ंक्शन लेनदेन युग्म की गणना कर सकता हैETH_USDTकीमत। समारोह अंततःreturnकथन में एल्गोरिथ्म को परिवर्तित करना हैsqrtPriceX96पुनर्स्थापित करनाtoken1/token0प्रक्रिया। उदाहरण के लिए, इस समय, पूल में टोकन0 (ETH) की संख्या 1 है, और टोकन1 (USDT) की संख्या 1100 है। इसलिए1100/1=1100, वर्तमान व्यापारिक जोड़ीETH_USDTपूल में कीमत 1100 है।
2、toAmountइस फ़ंक्शन का उपयोग ऑन-चेन संख्यात्मक डेटा को पठनीय डेटा में परिवर्तित करने के लिए किया जाता है।
function toAmount(s, decimals) {
return Number((BigDecimal(BigInt(s))/BigDecimal(Math.pow(10, decimals))).toString())
}
सरल शब्दों में कहें तो, उदाहरण के लिए, चेन पर प्रदर्शित ETH टोकन की मात्रा 1e18 है, जो 10 की 18वीं घात है, क्योंकि ETH का परिशुद्धता डेटा 18 है। सभी टोकन की परिशुद्धता 18 नहीं होती। USDT की परिशुद्धता ETH से भिन्न होती है।toAmountयह फ़ंक्शन 1e18 को 1 में परिवर्तित करता है।
3、toInnerAmountकार्य औरtoAmountइसके बजाय, यह पठनीय डेटा को ऑन-चेन उपयोग किए जाने वाले संख्यात्मक मानों में परिवर्तित करता है।
function toInnerAmount(n, decimals) {
return (BigDecimal(n)*BigDecimal(Math.pow(10,decimals))).toFixed(0)
}
इसके बाद, आइए “यूनिस्वैप V3 ट्रेडिंग लाइब्रेरी” के कोड का एक साथ विश्लेषण करें।
भाग 3: Uniswap V3 ऑपरेशन ऑब्जेक्ट का कंस्ट्रक्टर
इस टेम्पलेट क्लास लाइब्रेरी का मूल Uniswap V3 ऑपरेशन ऑब्जेक्ट है, जो Uniswap V3 पर बुनियादी ऑपरेशनों को लागू करता है। भविष्य में और अधिक कार्यों को उन्नत किया जा सकता है। इस कोड उदाहरण का विश्लेषण करके, भले ही आप FMZ प्लेटफ़ॉर्म का उपयोग न करते हों, आप अपनी समझ बढ़ाएँगेUniswapयहDEXप्रत्येक लिंक की प्रक्रियाओं और विवरणों को समझने के लिए, आइए अब जानें कि इन बुनियादी कार्यों को FMZ पर कैसे डिज़ाइन और कार्यान्वित किया जाता है।
Uniswap V3 ऑपरेशन ऑब्जेक्ट का कंस्ट्रक्टर कोड:
javascript
$.NewUniswapV3 = function(e) {
e = e || exchange // 如果没有传参数e,就使用交易所对象exchange,即策略上第一个添加的交易所
if (e.GetName() !== 'Web3') { // 判断交易所对象是否是Web3,因为这个模板只支持Web3交易所对象
panic("only support Web3 exchange")
}
let self = { // 当前函数是一个构造函数,构造的对象就是self这个对象
tokenInfo: {}, // self对象的成员变量,用于记录token的注册信息
walletAddress: e.IO("address"), // 记录当前交易所对象绑定的钱包地址
pool: {} // 用于记录注册的池信息
}
// register
e.IO("abi", ContractV3Factory, ABI_Factory) // 注册工厂合约的ABI
e.IO("abi", ContractV3SwapRouterV2, ABI_Route) // 注册路由合约的ABI
self.addToken = function(name, address) { // 用于注册token
let ret = e.IO("api", address, "decimals") // 调用decimals方法,获取token精度信息
if (!ret) {
throw "get token decimals failed"
}
let decimals = Number(ret)
self.tokenInfo[name] = {
name: name,
decimals: decimals,
address: address
}
}
self.waitMined = function(tx) { // 用于等待以太坊上某个操作的结果,哈希为tx参数
while (true) {
Sleep(1000)
let info = e.IO("api", "eth", "eth_getTransactionReceipt", tx) // 查询结果使用eth_getTransactionReceipt方法,没有查询到,循环继续查询
if (info && info.gasUsed) {
return true
}
Log('Transaction not yet mined', tx)
}
}
self.swapToken = function(tokenIn, amountInDecimal, tokenOut, options) { // 用于token兑换
// options like {gasPrice: 11, gasLimit: 111, nonce: 111}
let tokenInInfo = self.tokenInfo[tokenIn] // 拿到兑换出去的token的信息
let tokenOutInfo = self.tokenInfo[tokenOut] // 拿到兑换回来的token的信息
if (!tokenInInfo) {
throw "not found token info " + tokenIn
}
if (!tokenOutInfo) {
throw "not found token info " + tokenOut
}
let amountIn = toInnerAmount(amountInDecimal, tokenInInfo.decimals) // 转换为智能合约上使用的数据
let recipientAddress = self.walletAddress
if (tokenInInfo.name != 'ETH') {
let allowanceAmount = e.IO("api", tokenInInfo.address, "allowance", self.walletAddress, ContractV3SwapRouterV2); // 查询授权的数量
let realAmount = toAmount(allowanceAmount, tokenInInfo.decimals)
if (realAmount < toAmount(amountIn, tokenInInfo.decimals)) { // 如果授权数量不足
Log("realAmount is", realAmount, "too small, try to approve large amount")
if (tokenInInfo.name == 'USDT') {
// As described in Tether code: To change the approve amount you first have to reduce the addresses allowance to 0 calling approve(spender, 0)
let txApprove = e.IO("api", tokenInInfo.address, "approve", ContractV3SwapRouterV2, 0) // 如果授权的token是USDT,需要先授权为0
if (!txApprove) {
throw "approve error"
}
Log("wait reduce approve", txApprove)
self.waitMined(txApprove)
}
let txApprove = e.IO("api", tokenInInfo.address, "approve", ContractV3SwapRouterV2, '0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff'); // 授权Router合约操作钱包的代币
if (!txApprove) {
throw "approve error"
}
Log("wait approve", txApprove)
self.waitMined(txApprove)
Log("approve success amountIn", amountIn)
} else {
Log("allowance", realAmount, "no need to approve")
}
}
if (tokenOutInfo.name == 'ETH' || tokenOutInfo.address.toLowerCase() == '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2') {
/*
ADDRESS_THIS https://degencode.substack.com/p/uniswapv3-multicall
https://etherscan.io/address/0x68b3465833fb72a70ecdf485e0e4c7bd8665fc45#code
*/
recipientAddress = '0x0000000000000000000000000000000000000002'
// 其它币换成 WETH的时候,要让合约HOLD住WETH才可以赎回
}
let swapToken = e.IO("encode", ContractV3SwapRouterV2, "swapExactTokensForTokens", amountIn, 1, [tokenInInfo.address, tokenOutInfo.address], recipientAddress) // 打包swapExactTokensForTokens调用
let data = [swapToken]
if (tokenOutInfo.name == 'ETH') { // 如果兑换时,兑换回来的token是ETH,这里实际是WETH,则需要解包
data.push(e.IO("encode", ContractV3SwapRouterV2, "unwrapWETH9(uint256,address)", 1, self.walletAddress)) // 所以这里再打包一个unwrapWETH9解包调用
}
let tx = e.IO("api", ContractV3SwapRouterV2, "multicall(uint256,bytes[])", (tokenInInfo.name == 'ETH' ? amountIn : 0), (new Date().getTime() / 1000) + 3600, data, options || {}) // 使用multicall执行这些打包的操作(swapExactTokensForTokens、unwrapWETH9)
if (tx) {
Log("tx: ", tx)
self.waitMined(tx)
Log("swap", tokenInInfo.name, "to", tokenOutInfo.name, "success")
return true
} else {
Log("trans error")
return false
}
}
self.getETHBalance = function(address) { // 查询钱包的ETH余额
return toAmount(e.IO("api", "eth", "eth_getBalance", address || self.walletAddress, "latest"), 18)
}
self.balanceOf = function(token, address) { // 查询钱包的某个token余额(根据参数确定)
let tokenInfo = self.tokenInfo[token]
if (!tokenInfo) {
throw "not found token info " + token
}
return toAmount(e.IO("api", tokenInfo.address, "balanceOf", address || self.walletAddress), tokenInfo.decimals)
}
self.sendETH = function(to, amount, options) { // 向某个地址发送ETH代币,即转账
return e.IO("api", "eth", "send", to, toInnerAmount(amount, 18), options || {})
}
self.getPrice = function(pair, fee) { // 获取交易对价格
let arr = pair.split('_')
let token0 = self.tokenInfo[arr[0]]
if (!token0) {
throw "token " + arr[0] + "not found"
}
let token1 = self.tokenInfo[arr[1]] // 首先拿到构成交易对的两个token信息
if (!token1) {
throw "token " + arr[1] + "not found"
}
let reverse = false
if (BigInt(token0.address) > BigInt(token1.address)) {
let tmp = token0
token0 = token1
token1 = tmp
reverse = true
}
let key = token0.address + '/' + token1.address
if (typeof(self.pool[key]) == 'undefined') {
let pool = e.IO("api", ContractV3Factory, "getPool", token0.address, token1.address, typeof(fee) === 'number' ? fee : 3000) // 调用工厂合约的getPool方法,获取兑换池的地址
if (pool) {
self.pool[key] = pool // 注册池地址,并注册池合约的ABI
// register pool address
e.IO("abi", pool, ABI_Pool)
}
}
if (typeof(self.pool[key]) == 'undefined') {
throw "pool " + pair + " not found"
}
let slot0 = e.IO("api", self.pool[key], "slot0") // 调用池合约的slot0方法,拿到价格相关信息
if (!slot0) {
return null
}
let price = computePoolPrice(token0.decimals, token1.decimals, slot0.sqrtPriceX96) // 计算出可读的价格
if (reverse) {
price = 1 / price
}
return price
}
return self
}
जो छात्र FMZ से परिचित नहीं हैं, वे इस फ़ंक्शन को देख सकते हैं$.NewUniswapV3नामकरण थोड़ा अजीब है,$.शुरुआत में फ़ंक्शन इंगित करता है कि यह फ़ंक्शन FMZ पर टेम्पलेट लाइब्रेरी का इंटरफ़ेस फ़ंक्शन है (टेम्पलेट लाइब्रेरी क्या हो सकती है)देखना), सामान्य शर्तों में$.NewUniswapV3फ़ंक्शन इसके लिए अन्य संदर्भों की अनुमति देता हैटेम्पलेट लाइब्रेरीरणनीति को सीधे बुलाया जाता है। यह रणनीति सीधे स्वामित्व में हैUniswap V3समारोह।
यह$.NewUniswapV3फ़ंक्शन सीधे एक ऑब्जेक्ट का निर्माण और सृजन करता है, और इस ऑब्जेक्ट का उपयोग कुछ ऑपरेशन करने के लिए किया जा सकता है:
- टोकन विनिमय: वस्तु द्वारा
swapTokenविधि कार्यान्वयन. - ETH बैलेंस क्वेरी: ऑब्जेक्ट द्वारा
getETHBalanceविधि कार्यान्वयन. - टोकन बैलेंस क्वेरी: ऑब्जेक्ट द्वारा
balanceOfविधि कार्यान्वयन. - ट्रेडिंग जोड़ी मूल्य क्वेरी: ऑब्जेक्ट द्वारा
getPriceविधि कार्यान्वयन. - स्थानांतरण के लिए ETH भेजें:
sendETHविधि कार्यान्वयन.
भविष्य में यह लाइब्रेरी इन कार्यों तक सीमित नहीं रह सकती है, तथा इसमें "तरलता जोड़ने" जैसे कार्यों को जोड़ने के लिए अपग्रेड भी किया जा सकता है। आइये कोड का विश्लेषण जारी रखें:
e = e || exchange
if (e.GetName() !== 'Web3') {
panic("only support Web3 exchange")
}
let self = {
tokenInfo: {},
walletAddress: e.IO("address"),
pool: {}
}
// register
e.IO("abi", ContractV3Factory, ABI_Factory)
e.IO("abi", ContractV3SwapRouterV2, ABI_Route)
निर्माता$.NewUniswapV3केवल एक पैरामीटरe, यह e एक्सचेंज ऑब्जेक्ट (FMZ पर एक्सचेंज कॉन्फ़िगरेशन) का प्रतिनिधित्व करता है। क्योंकि एफएमजेड पर रणनीति को बहु-विनिमय के रूप में डिजाइन किया जा सकता है, यदि कोई विशिष्ट एक्सचेंज यहां पारित किया जाता है, तो इसका मतलब है कि इसे बनाया गया है।Uniswap V3ऑब्जेक्ट का उपयोग एक्सचेंज ऑब्जेक्ट को संचालित करने के लिए किया जाता है। यदि कोई पैरामीटर पास नहीं किया गया हैe, डिफ़ॉल्ट ऑपरेशन पहला जोड़ा गया एक्सचेंज ऑब्जेक्ट है।
एक्सचेंज ऑब्जेक्ट बनाने के लिए नोड सेवा पता और निजी कुंजी (निजी कुंजी को स्थानीय रूप से तैनात किया जा सकता है, और स्थानीय तैनाती के लिए केवल पथ को कॉन्फ़िगर करने की आवश्यकता होती है) को कॉन्फ़िगर करें। इसे वास्तविक ट्रेडिंग के दौरान रणनीति में जोड़ा जा सकता है। यह ऑब्जेक्ट रणनीति कोड में इस प्रकार दर्शाया गया हैexchangeवह हैexchanges[0]यदि आप दूसरा जोड़ते हैं,exchanges[1], एक तिहाई जोड़ेंexchanges[2],...
स्क्रीनशॉट में मैंने जो नोड पता कॉन्फ़िगर किया है: https://mainnet.infura.io/v3/xxx वह Infura का नोड है। इसे व्यक्तियों द्वारा लागू किया जा सकता है। प्रत्येक खाते का अपना विशिष्ट पता होता है। xxx मास्क है। xxx प्रत्येक खाते के लिए भाग अलग-अलग है।
कोड के साथ आगे बढ़ते हुए, कन्स्ट्रक्टर यह निर्धारित करना शुरू करता है कि एक्सचेंज ऑब्जेक्ट Web3 है या नहीं, और यदि यह Web3 नहीं है तो त्रुटि रिपोर्ट करता है। फिर एक चर बनायाself, यह स्वयं वह ऑब्जेक्ट है जिसे अंततः कन्स्ट्रक्टर द्वारा लौटाया जाता है। बाद के कन्स्ट्रक्टर इस ऑब्जेक्ट में विभिन्न फ़ंक्शन जोड़ते हैं और विशिष्ट फ़ंक्शन लागू करते हैं। self चर में 3 विशेषताएं हैं:
- टोकन जानकारी: ऑब्जेक्ट में पंजीकृत टोकन जानकारी रिकॉर्ड करता है। टोकन जानकारी में टोकन पता, टोकन परिशुद्धता और टोकन नाम शामिल हैं।
- वॉलेटएड्रेस: वर्तमान एक्सचेंज ऑब्जेक्ट का वॉलेट पता.
- पूल: इस ऑब्जेक्ट में पंजीकृत एक्सचेंज पूल जानकारी, मुख्य रूप से एक्सचेंज पूल नाम और एक्सचेंज पूल पता।
फिर हमने पिछले लेख में सीखी गई अवधारणाओं का उपयोग किया:
e.IO("abi", ContractV3Factory, ABI_Factory) // 注册Uniswap V3 工厂合约的ABI
e.IO("abi", ContractV3SwapRouterV2, ABI_Route) // 注册Uniswap Router V2 路由的ABI
हमें इन इंटरफ़ेस सूचनाओं को पंजीकृत करने की आवश्यकता क्यों है?
क्योंकि बाद में क्रियान्वित किये जाने वाले कुछ कार्यों के लिए इन स्मार्ट अनुबंधों के इंटरफेस को कॉल करने की आवश्यकता होती है। अगला कदम सेल्फ ऑब्जेक्ट में विभिन्न विधियों को जोड़ना है। ऊपर बताए गए तरीकों के अलावा: टोकन रिडीम करना, बैलेंस चेक करना, आदि, सेल्फ ऑब्जेक्ट से संबंधित कुछ टूल फ़ंक्शन भी हैं। आइए पहले इन टूल फ़ंक्शन का विश्लेषण करें।
स्वयं ऑब्जेक्ट के लिए उपयोगिता फ़ंक्शन
1、self.addToken = function(name, address)
इस फ़ंक्शन के विशिष्ट कोड का अवलोकन करने पर, हम देख सकते हैं कि यह फ़ंक्शन वर्तमान ऑब्जेक्ट को देता हैselfअभिलेखtokenजानकारी के सदस्यtokenInfoटोकन जानकारी जोड़ें (दूसरे शब्दों में: रजिस्टर करें)। क्योंकिtoken(टोकन) का परिशुद्धता डेटा अक्सर बाद की गणनाओं में उपयोग किया जाता है, इसलिए जब यह फ़ंक्शन (पंजीकृत) टोकन जानकारी जोड़ता है, तो यह कॉल करता हैlet ret = e.IO("api", address, "decimals")फ़ंक्शन, FMZ द्वारा एनकैप्सुलेट किए गए एक्सचेंज.IO फ़ंक्शन के माध्यम से (जैसा कि हमने पहले उल्लेख किया है, ई आने वाली एक्सचेंज ऑब्जेक्ट है), टोकन टोकन अनुबंध को कॉल करता है"decimals"टोकन की परिशुद्धता प्राप्त करने की विधि.
इसलिएself.tokenInfoयह एक शब्दकोश संरचना है। प्रत्येक कुंजी नाम टोकन नाम है, और कुंजी मान इस टोकन की जानकारी है, जिसमें शामिल हैं: पता, नाम और परिशुद्धता। यह कुछ इस तरह दिख सकता है:
{
"ETH": {name: "ETH", decimals: 18, address: "0x..."},
"USDT": {name: "USDT", decimals: 6, address: "0x..."},
...
}
2、self.waitMined = function(tx)
इस फ़ंक्शन का उपयोग एथेरियम पर स्मार्ट कॉन्ट्रैक्ट के निष्पादन परिणाम की प्रतीक्षा करने के लिए किया जाता है। इस फ़ंक्शन के कार्यान्वयन कोड से, हम देख सकते हैं कि इस फ़ंक्शन को लूप में बुलाया गया है।let info = e.IO("api", "eth", "eth_getTransactionReceipt", tx), एथेरियम की RPC विधि को कॉल करकेeth_getTransactionReceipt, पूछताछ करने के लिएलेनदेन हैश लेनदेन की रसीद लौटाता है,पैरामीटरtxवह हैलेनदेन हैश。
eth_getTransactionReceiptसंबंधित जानकारी के लिए, कृपया देखें: https://ethereum.org/zh/developers/docs/apis/json-rpc/#eth_gettransactionreceipt
कुछ छात्र पूछ सकते हैं: इस फ़ंक्शन का उपयोग क्यों करें?
उत्तर: कुछ कार्य करते समय, जैसे टोकन एक्सचेंज, आपको परिणामों की प्रतीक्षा करनी पड़ती है।
आगे, आइए देखें$.NewUniswapV3आइए फ़ंक्शन द्वारा स्वयं निर्मित ऑब्जेक्ट के अन्य मुख्य फ़ंक्शनों के सरलतम कार्यान्वयन से शुरू करें।
मुख्य कार्य
1、self.getETHBalance = function(address)
टोकन बैलेंस की जांच करने के दो तरीके हैं: ETH (इथेरियम) बैलेंस की जांच करें और अन्य ERC20 टोकन का बैलेंस जांच करें। सेल्फ ऑब्जेक्ट के getETHBalance फ़ंक्शन का उपयोग ETH बैलेंस को क्वेरी करने के लिए किया जाता है। जब विशिष्ट वॉलेट एड्रेस पैरामीटर एड्रेस पास किया जाता है, तो इस एड्रेस के ETH बैलेंस को क्वेरी किया जाता है। यदि कोई पता पैरामीटर पास नहीं किया गया है, तो क्वेरीself.walletAddressपते का ETH शेष (अर्थात वर्तमान एक्सचेंज पर कॉन्फ़िगर किया गया वॉलेट).
ये कार्य एथेरियम की RPC विधियों को कॉल करके किया जाता हैeth_getBalanceपूरा करना।
2、self.balanceOf = function(token, address)
ETH के अलावा अन्य टोकन के शेष राशि की जानकारी प्राप्त करने के लिए, आपको पैरामीटर टोकन पास करना होगा, जो टोकन का नाम है, जैसे USDT। पूछताछ के लिए वॉलेट पता पास करें। यदि कोई पता पास नहीं किया गया है, तो पूछताछ करेंself.walletAddressपते का संतुलन. इस फ़ंक्शन द्वारा कार्यान्वित कोड का अवलोकन करते हुए, हम देख सकते हैं कि हमें पास करने की आवश्यकता हैself.addTokenकेवल फ़ंक्शन द्वारा पंजीकृत टोकन की ही क्वेरी की जा सकती है, क्योंकि टोकन को कॉल करने वाला अनुबंधbalanceOfविधि में, टोकन की परिशुद्धता जानकारी और पता आवश्यक है।
3、self.sendETH = function(to, amount, options)
इसका कार्य ETH को वॉलेट पते पर स्थानांतरित करना है (उपयोग करके)toपैरामीटर सेटिंग्स) ETH की एक निश्चित राशि स्थानांतरित करें (उपयोग करके)amountपैरामीटर सेटिंग्स), आप एक और सेट कर सकते हैंoptionsपैरामीटर (डेटा संरचना:{gasPrice: 111, gasLimit: 111, nonce: 111}) का प्रयोग निर्दिष्ट करने के लिए किया जाता हैgasLimit/gasPrice/nonceयदि आप विकल्प पैरामीटर पास नहीं करते हैं, तो सिस्टम डिफ़ॉल्ट सेटिंग्स का उपयोग किया जाएगा।
gasLimit/gasPriceइथेरियम पर संचालन करते समय खपत होने वाले ETH को प्रभावित करता है (इथेरियम पर कुछ संचालन गैस की खपत करते हैं, अर्थात, ETH टोकन की एक निश्चित मात्रा का उपभोग करते हैं)।
4、self.getPrice = function(pair, fee)
इस फ़ंक्शन का उपयोग Uniswap पर ट्रेडिंग जोड़ी की कीमत प्राप्त करने के लिए किया जाता है। फ़ंक्शन कार्यान्वयन कोड से, हम देख सकते हैं कि जब फ़ंक्शन निष्पादित होना शुरू होता है, तो बेस करेंसी और कोट करेंसी प्राप्त करने के लिए ट्रेडिंग जोड़ी जोड़ी को पहले पार्स किया जाएगा। उदाहरण के लिए, यदि ट्रेडिंग जोड़ी ETH_USDT है, तो इसे ETH और USDT में विभाजित किया जाएगा। फिर क्वेरीself.tokenInfoजाँच करें कि डेटा में इन दो टोकन के बारे में जानकारी है या नहीं। यदि नहीं, तो एक त्रुटि रिपोर्ट की जाएगी।
यूनिस्वैप पर एक्सचेंज पूल पता दो भाग लेने वाले टोकन पते और शुल्क (दर मानक) से बना है, इसलिए जब पूछताछ की जाती हैself.poolजब पूल पता दर्ज किया जाता है (self.pool जैसा कि हमने पहले उल्लेख किया है, आप इसकी जांच कर सकते हैं), यदि यह नहीं मिलता है, तो पूल पते की गणना करने के लिए दो टोकन और शुल्क के पते का उपयोग किया जाता है। इसलिए एक ट्रेडिंग जोड़ी में कई पूल हो सकते हैं क्योंकि शुल्क अलग-अलग हो सकता है।
Uniswap V3 फैक्ट्री कॉन्ट्रैक्ट को कॉल करके एक्सचेंज पूल के पते की क्वेरी करें और गणना करेंgetPoolविधि प्राप्त (इसलिए फैक्ट्री अनुबंध का एबीआई शुरुआत में पंजीकृत होना चाहिए)।
एक बार जब आपको इस ट्रेडिंग जोड़ी का पूल पता मिल जाता है, तो आप पूल अनुबंध के ABI को पंजीकृत कर सकते हैं। इस तरह, पूल (स्मार्ट अनुबंध) को बुलाया जा सकता हैslot0मूल्य डेटा प्राप्त करने की विधि. बेशक, इस विधि द्वारा लौटाया गया डेटा मानव-पठनीय मूल्य नहीं है, बल्कि मूल्य से संबंधित डेटा संरचना है। पठनीय मूल्य प्राप्त करने के लिए आगे की प्रक्रिया की आवश्यकता होती है। यह तब होता है जब हम इसका उपयोग करते हैंcomputePoolPriceसमारोह।
5、self.swapToken = function(tokenIn, amountInDecimal, tokenOut, options)
फ़ंक्शन टोकन का आदान-प्रदान करना है। पैरामीटर tokenIn विनिमय करते समय भुगतान किए गए टोकन का नाम है, पैरामीटर tokenOut विनिमय करते समय प्राप्त टोकन का नाम है, पैरामीटर amountInDecimal विनिमय राशि (मानव-पठनीय राशि) है, और पैरामीटर विकल्प वही है जैसा कि हमने पहले बताया था। उसी तरह, आप एक्सचेंज करते समय गैस की खपत, नॉन्स आदि सेट कर सकते हैं।
जब फ़ंक्शन निष्पादित होता है, तो इसे सबसे पहले पास किया जाता हैself.tokenInfoटोकन की जानकारी वैरिएबल से प्राप्त की जाती है। एक्सचेंज में भी कई विवरण हैं। सबसे पहले, यदि एक्सचेंज में शामिल टोकन ETH नहीं है, तो आपको यह देना होगामार्ग(विनिमय के लिए जिम्मेदार स्मार्ट अनुबंध) प्राधिकरण। प्राधिकरण से पहले, जाँच लें कि क्या प्राधिकरण राशि पर्याप्त है।
let allowanceAmount = e.IO("api", tokenInInfo.address, "allowance", self.walletAddress, ContractV3SwapRouterV2);
अधिकृत राशि जानने के लिए टोकन अनुबंध भत्ता विधि का उपयोग करें। वर्तमान मोचन राशि के साथ अधिकृत राशि की तुलना करने पर, यदि अधिकृत राशि मोचन के लिए पर्याप्त है, तो किसी अतिरिक्त प्राधिकरण की आवश्यकता नहीं है। यदि राशि अपर्याप्त है, तो प्राधिकरण प्रसंस्करण किया जाता है।
यहाँ प्राधिकरण में भी एक विवरण है। यदि अधिकृत टोकन USDT है, तो आपको अधिकृत करने से पहले प्राधिकरण मात्रा को 0 पर रीसेट करना होगा। टोकन अनुबंध की अनुमोदन विधि के उपयोग को अधिकृत करें। ध्यान दें कि अनुमोदन प्राधिकरण विधि एक गैस-खपत विधि है और यह एक निश्चित मात्रा में ETH का उपभोग करेगी। इसलिए आपको प्रसंस्करण परिणाम की प्रतीक्षा करने के लिए self.waitMined फ़ंक्शन का उपयोग करना होगा।
बार-बार प्राधिकरण और अनावश्यक ETH भुगतान से बचने के लिए, यह प्राधिकरण ऑपरेशन एक बार में अधिकतम मूल्य को अधिकृत करता है।
let txApprove = e.IO("api", tokenInInfo.address, "approve", ContractV3SwapRouterV2, '0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff');
यदि आपके पास पर्याप्त विनिमय कोटा है, तो आप विनिमय कर सकते हैं। लेकिन यहाँ भी विवरण हैं। यदि एक्सचेंज में शामिल टोकन ETH है, तो प्राप्त करने वाले पते को संशोधित करने की आवश्यकता है:
recipientAddress = '0x0000000000000000000000000000000000000002'
विशिष्ट कारण काफी जटिल हैं और यहाँ उन पर विस्तार से चर्चा नहीं की जाएगी। कृपया देखें:
ADDRESS_THIS https://degencode.substack.com/p/uniswapv3-multicall
https://etherscan.io/address/0x68b3465833fb72a70ecdf485e0e4c7bd8665fc45#code
फिर FMZ प्लेटफॉर्म द्वारा समाहित पैकेजिंग फ़ंक्शन का उपयोग करेंe.IO("encode", ..., राउटर (स्मार्ट कॉन्ट्रैक्ट) के लिए स्वैपएक्सैक्टटोकनफॉरटोकन विधि कॉल को पैकेज करें। यदि एक्सचेंज के बाद प्राप्त टोकन ETH है, तो आपको WETH9 अनपैकिंग ऑपरेशन का एक चरण जोड़ना होगा:
data.push(e.IO("encode", ContractV3SwapRouterV2, "unwrapWETH9(uint256,address)", 1, self.walletAddress))
क्योंकि एक्सचेंज में शामिल टोकन WETH है, जो ETH का एक पैकेज्ड टोकन है। वास्तविक ETH में बदलने के लिए, एक अनपैकिंग ऑपरेशन की आवश्यकता होती है। अनपैकिंग ऑपरेशन को पैकेजिंग करने के बाद, ऑपरेशन की इस श्रृंखला को निष्पादित करने के लिए राउटर (स्मार्ट कॉन्ट्रैक्ट) की मल्टीकॉल विधि को कॉल किया जा सकता है। एक और विवरण है जिस पर आपको अतिरिक्त ध्यान देने की आवश्यकता है। यदि एक्सचेंज में शामिल लेनदेन जोड़ी ETH है, तो आपको निम्नलिखित चरणों में स्थानांतरित की जाने वाली ETH की राशि निर्धारित करनी होगी। यदि यह ETH नहीं है, तो इसे सेट करें 0.
let tx = e.IO("api", ContractV3SwapRouterV2, "multicall(uint256,bytes[])", (tokenInInfo.name == 'ETH' ? amountIn : 0), (new Date().getTime() / 1000) + 3600, data, options || {})
यह सेटिंग यहां दर्शाई गई है:(tokenInInfo.name == 'ETH' ? amountIn : 0). संपादक ने पहले इसका पता नहीं लगाया और जब tokenIn, ETH टोकन के बराबर नहीं था, तो 0 सेट नहीं किया, जिसके कारण ETH का गलत हस्तांतरण हो गया। इसलिए ट्रांसफर कोड लिखते समय अतिरिक्त सावधानी बरतें।
भाग 4: Uniswap V3 ऑपरेशन ऑब्जेक्ट का उपयोग कैसे करें
इस टेम्पलेट में कोड की कार्यक्षमता वास्तव में 200 से कम पंक्तियों की है। निम्नलिखित अनुभाग वास्तव में एक उपयोग प्रदर्शन है।
$.testUniswap = function() {
let ex = $.NewUniswapV3()
Log("walletAddress: ", ex.walletAddress)
let tokenAddressMap = {
"ETH": "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", // WETH
"USDT": "0xdac17f958d2ee523a2206206994597c13d831ec7",
"1INCH": "0x111111111117dC0aa78b770fA6A738034120C302",
}
for (let name in tokenAddressMap) {
ex.addToken(name, tokenAddressMap[name])
}
Log(ex.getPrice('ETH_USDT'))
Log(ex.getPrice('1INCH_USDT'))
// swap 0.01 ETH to USDT
Log(ex.swapToken('ETH', 0.01, 'USDT'))
let usdtBalance = ex.balanceOf('USDT')
Log("balance of USDT", usdtBalance)
// swap reverse
Log(ex.swapToken('USDT', usdtBalance, 'ETH'))
Log("balance of ETH", ex.getETHBalance())
// Log(ex.sendETH('0x11111', 0.02))
}
$.testUniswap = function()यह फ़ंक्शन केवल एक प्रदर्शन है, यदि इसका कोई व्यावहारिक उपयोग नहीं है तो कृपया इसे न बुलाएं। हम इस फ़ंक्शन का उपयोग यह देखने के लिए करेंगे कि Uniswap V3 के फ़ंक्शन को संचालित करने के लिए इस टेम्पलेट लाइब्रेरी का उपयोग कैसे किया जाए।
कोड को पहले निष्पादित किया जाता हैlet ex = $.NewUniswapV3()Uniswap V3 ऑपरेशन ऑब्जेक्ट का निर्माण करता है। यदि आप वर्तमान एक्सचेंज से जुड़ा वॉलेट पता प्राप्त करना चाहते हैं, तो आप इसका उपयोग कर सकते हैंex.walletAddressपाना। फिर इसे कोड में उपयोग करेंex.addTokenतीन टोकन पंजीकृत किए गए, अर्थात् ETH, USDT और 1INCH।
ट्रेडिंग जोड़ी का मूल्य प्रिंट करें (टोकन को पहले पंजीकृत करना होगा):
Log(ex.getPrice('ETH_USDT'))
Log(ex.getPrice('1INCH_USDT'))
यदि getPrice फ़ंक्शन शुल्क निर्धारित नहीं करता है, तो 3000 की डिफ़ॉल्ट दर का उपयोग किया जाता है, जिसे 0.3% के पठनीय मान में परिवर्तित किया जाता है।
यदि आप 0.01 ETH को USDT में बदलना चाहते हैं, शेष राशि की जांच करना चाहते हैं, और फिर उसे वापस बदलना चाहते हैं, तो कोड का उपयोग करें:
Log(ex.swapToken('ETH', 0.01, 'USDT'))
let usdtBalance = ex.balanceOf('USDT') // 查询兑换后的USDT余额
Log("balance of USDT", usdtBalance)
Log(ex.swapToken('USDT', usdtBalance, 'ETH')) // 把USDT兑换为ETH
Log("balance of ETH", ex.getETHBalance()) // 查询ETH余额
// Log(ex.sendETH('0x11111', 0.02)) // ETH转账操作
गोएर्ली टेस्टनेट का उपयोग करके परीक्षण करें
- टेस्टनेट एक्सचेंज ऑब्जेक्ट को कॉन्फ़िगर करें
कृपया ध्यान दें कि नोड सेट करते समय, आपको इसे परीक्षण नेटवर्क गोएर्ली के नोड पर सेट करना होगा।
- एक रणनीति लिखें और इसे परीक्षण नेटवर्क गोएर्ली पर परीक्षण करें।
function main() {
let ex = $.NewUniswapV3()
Log("walletAddress: ", ex.walletAddress)
let tokenAddressMap = {
"ETH" : "0xB4FBF271143F4FBf7B91A5ded31805e42b2208d6", // WETH
"LINK" : "0x326C977E6efc84E512bB9C30f76E30c160eD06FB",
"UNI" : "0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984",
}
for (let name in tokenAddressMap) {
ex.addToken(name, tokenAddressMap[name])
}
// ETH_UNI 、 UNI_ETH
Log("ETH_UNI:", ex.getPrice('ETH_UNI'))
Log("UNI_ETH:", ex.getPrice('UNI_ETH'))
// ETH
Log("balance of ETH", ex.getETHBalance())
// UNI
let uniBalance = ex.balanceOf('UNI')
Log("balance of UNI", uniBalance)
// LINK
let linkBalance = ex.balanceOf('LINK')
Log("balance of LINK", linkBalance)
// swap 0.001 ETH to UNI
Log(ex.swapToken('ETH', 0.001, 'UNI'))
// swap UNI to LINK
Log(ex.swapToken('UNI', ex.balanceOf('UNI') - uniBalance, 'LINK'))
}
परीक्षण कोड में, हमने वॉलेट पते प्रिंट करने, टोकन जानकारी पंजीकृत करने, परिसंपत्ति शेष राशि प्रिंट करने और निरंतर विनिमय करने का परीक्षण किया।ETH -> UNI -> LINK. यह ध्यान दिया जाना चाहिए कि यहाँ पंजीकृत टोकन पता एथेरियम परीक्षण नेटवर्क गोएर्ली पर है, इसलिए समान नाम वाले टोकन पते अलग-अलग हैं। परीक्षण सिक्कों के लिए, आप परीक्षण टोकन के लिए आवेदन करने के लिए इस परीक्षण नेटवर्क के नल का उपयोग कर सकते हैं। आप विस्तृत जानकारी के लिए गूगल पर जांच कर सकते हैं।
ध्यान दें कि इसका उपयोग करने के लिए आपको "यूनिस्वैप V3 ट्रेडिंग लाइब्रेरी" टेम्पलेट की जांच करनी होगी$.NewUniswapV3()फ़ंक्शन, यदि आपके FMZ खाते में यह टेम्पलेट नहीं है, तो आप क्लिक कर सकते हैंइसे यहां प्राप्त करें。
रणनीति संचालन लॉग:
Uniswap पृष्ठ पर प्रदर्शित परिसंपत्ति मूल्य
इन कार्यों को श्रृंखला पर भी पूछा जा सकता है:
ETH से UNI का विनिमय एक बार निष्पादित किया गया, UNI का प्राधिकरण एक बार निष्पादित किया गया, तथा UNI से LINK का विनिमय एक बार निष्पादित किया गया।
END
इस लाइब्रेरी में कई फ़ंक्शन हैं जिन्हें विस्तारित किया जा सकता है, और यहां तक कि कई रिडेम्प्शन को पैकेज करने के लिए भी विस्तारित किया जा सकता हैtokenA -> tokenB -> tokenCपथ विनिमय. इसे विशिष्ट आवश्यकताओं के अनुसार अनुकूलित और विस्तारित किया जा सकता है। इस प्रकार का लाइब्रेरी कोड मुख्य रूप से शिक्षण के लिए प्रदान किया जाता है।
नवीकरण
उन्नतswapTokenकार्य, समर्थनtokenA -> tokenB -> tokenC ... -> tokenDसतत मोचन समारोह. आप FMZ पर स्ट्रेटेजी स्क्वायर पर प्रकाशित इस टेम्पलेट का नवीनतम कोड देख सकते हैं।
- 1








