Type/to search
8
Follow
1364
Followers
Elegan dan ringkas! Disambungkan ke Uniswap V3 pada FMZ dengan 200 baris kod
Discussions
Created 2023-01-30 15:38:02  Updated 2023-09-18 19:39:40
 5
 3104

img

Elegan dan ringkas! Disambungkan ke Uniswap V3 pada FMZ dengan 200 baris kod

Dengan populariti konsep Defi dalam beberapa tahun kebelakangan ini, Uniswap V3 telah menjadi salah satu topik paling popular dalam bidang kewangan terdesentralisasi (DeFi). Sebagai protokol pertukaran terdesentralisasi terkemuka, Uniswap V3 menyediakan pengalaman pengguna yang lebih cekap, lebih selamat dan lebih baik. Kini, dengan hanya 200 baris kod, pedagang dan pembangun boleh mengakses Uniswap V3 dengan mudah pada platform FMZ.

FMZ ialah platform dagangan kuantitatif yang menyokong pembangunan, ujian belakang dan penggunaan masa nyata strategi dagangan kuantitatif. Dengan antara muka yang mudah digunakan dan ciri yang berkuasa, mudah untuk memahami sebab FMZ menjadi pilihan pertama untuk pedagang dan pembangun DeFi.

Proses menyepadukan Uniswap V3 ke dalam FMZ adalah mudah dan mudah difahami, dan hanya memerlukan 200 baris kod untuk diselesaikan. Ini bermakna walaupun anda baru dalam pengekodan, anda boleh menyambung dengan mudah ke Uniswap V3 di FMZ dan mula berdagang dengan segera.

FMZ telah merangkum satu siri fungsi web3 asas Selain Uniswap, pertukaran DEX lain juga boleh dikapsulkan dengan kod yang sangat sedikit. Seterusnya, izinkan saya membawa anda mempelajari konsep dan teknologi dalam aplikasi DeFi dari awal Oleh kerana kekangan ruang, penerangan berikut cuba menggunakan cara yang paling mudah dan mudah difahami.

Platform FMZ terbuka kepada orang ramai「Pustaka transaksi Uniswap V3」

Kodnya adalah seperti berikut:

/* 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)) }

Rangkaian Ethereum

Rangkaian Ethereum boleh difahami sebagai infrastruktur perisian di mana pelbagai kontrak pintar boleh digunakan dan dijalankan Kontrak pintar mempunyai pelbagai fungsi dan senario aplikasi. Peranti yang menjalankan pelanggan Ethereum membentuk nod dalam rangkaian Ethereum.

Beberapa konsep dalam Uniswap V3

Tidak dikenaliUniswap V3Bagi mereka yang baru menggunakan protokol, anda perlu terlebih dahulu memahami beberapa konsep.Uniswap V3Ia juga merupakan kontrak pintar yang digunakan dan dijalankan pada Ethereum.

  1. Laluan: Laluan juga merupakan kontrak pintar yang digunakan untuk mengurustokenpertukaran.
  2. Pool: Pool juga merupakan kontrak pintar yang digunakan untuk menyimpan dua token Ethereum dan bertukar antara mereka.
  3. Kontrak Kilang: Kontrak kilang ialah kontrak pintar yang digunakan untuk mencipta kolam.
  4. ABI: (Antaramuka Binari Aplikasi) ialah spesifikasi yang menerangkan cara kontrak pintar berkomunikasi dengan dunia luar. Ia menentukan nama fungsi, jenis parameter dan jenis nilai pulangan kontrak pintar, serta cara mengekod dan menyahkod data, dan menentukan antara muka luaran kontrak pintar. Ia boleh difahami bahawa untuk memanggil antara muka tertentu, ia mesti dipanggil mengikut piawaian yang dipersetujui oleh antara muka, dan ABI merekodkan satu siri piawaian yang dipersetujui.

Sebaik sahaja kontrak pintar digunakan pada Ethereum, ia mempunyai alamat.

Membongkar kod perpustakaan perdagangan Uniswap V3

Kod perpustakaan perdagangan Uniswap V3 terutamanya dibahagikan kepada 4 bahagian Mari kita jelaskan satu persatu.

Bahagian 1: Pemalar digunakan semasa berinteraksi dengan 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"

Dengan konsep asas di atas dipanaskan, ia mudah difahami di sini.

ABI_RouteRentetan yang disimpan dalam pemalar ini ialah ABI kontrak pintar penghala.
ABI_PoolABI kontrak kolam simpanan.
ABI_FactoryABI untuk kontrak kilang.

Oleh kerana rentetan ini sangat panjang, ia hanya petikan. Kandungan ini menyediakan program dengan piawaian untuk memanggil kaedah kontrak pintar (contohnya, apakah parameter antara muka kontrak pintar ini, berapa banyak parameter yang ada, jenisnya, jenis data yang dikembalikan, dsb.).

Kami menyebut sebelum ini bahawa sebaik sahaja kontrak pintar digunakan pada Ethereum, ia mempunyai alamat.

ContractV3Factory: Merekod alamat kontrak kilang.
ContractV3SwapRouterV2:Alamat V2 penghala Uniswap V3 Sila ambil perhatian bahawa Uniswap mempunyai V1 dan V2, dan penghala Uniswap V3 juga mempunyai V1 dan V2 alamat kontrak yang berbeza.

Bahagian 2: Fungsi Alat

1、computePoolPriceFungsi ini digunakan untuk mengira harga token dalam kolam.

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

Jika pasangan dagangan adalahETH_USDT, Jaditoken0yaETHtoken1yaUSDTdecimals0iaitutoken0Data ketepatan,decimals1iaitutoken1Data ketepatan.sqrtPriceX96Ini adalah data berkaitan harga (bukan nilai harga langsung), yang boleh diperoleh daripada kontrak kumpulanslot0Pemerolehan kaedah.

sqrtPriceX96 : The current price of the pool as a sqrt(token1/token0) Q64.96 value
Q64.96 ialah standard storan pemprosesan data.

decimals0decimals1sqrtPriceX96Ketiga-tiga data ini dihantar sebagai parametercomputePoolPriceFungsi ini boleh mengira pasangan transaksiETH_USDTharga. Fungsi akhirnyareturnAlgoritma dalam pernyataan itu adalah untuk menukarsqrtPriceX96Pulihkantoken1/token0proses. Sebagai contoh, pada masa ini, bilangan token0 (ETH) dalam kumpulan ialah 1, dan bilangan token1 (USDT) ialah 1100. Jadi1100/1=1100, pasangan dagangan semasaETH_USDTHarga di dalam kolam ialah 1100.

2、toAmountFungsi ini digunakan untuk menukar data berangka pada rantaian kepada data yang boleh dibaca.

function toAmount(s, decimals) { return Number((BigDecimal(BigInt(s))/BigDecimal(Math.pow(10, decimals))).toString()) }

Secara ringkasnya, sebagai contoh, kuantiti token ETH yang diwakili pada rantaian ialah 1e18, iaitu 10 hingga kuasa ke-18, kerana data ketepatan ETH ialah 18. Tidak semua token mempunyai ketepatan 18. Ketepatan USDT adalah berbeza daripada ketepatan ETH.toAmountFungsi menukar 1e18 kepada 1.

3、toInnerAmountFungsi dantoAmountSebaliknya, ia menukar data yang boleh dibaca kepada nilai berangka yang digunakan dalam rantaian.

function toInnerAmount(n, decimals) { return (BigDecimal(n)*BigDecimal(Math.pow(10,decimals))).toFixed(0) }

Seterusnya, mari kita analisa kod "Perpustakaan Dagangan Uniswap V3" bersama-sama.

Bahagian 3: Pembina objek operasi Uniswap V3

Teras perpustakaan kelas templat ini ialah objek operasi Uniswap V3, yang melaksanakan operasi asas pada Uniswap V3. Lebih banyak fungsi mungkin dinaik taraf pada masa hadapan. Dengan menganalisis contoh kod ini, walaupun anda tidak menggunakan platform FMZ, anda akan meningkatkan pemahaman anda tentangUniswapiniDEXUntuk memahami proses dan butiran setiap pautan, mari kita ketahui cara fungsi asas ini direka bentuk dan dilaksanakan pada FMZ.

Kod pembina objek operasi 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 }

Pelajar yang mungkin tidak biasa dengan FMZ boleh melihat fungsi ini$.NewUniswapV3Penamaan itu agak pelik, dengan$.Fungsi pada permulaan menunjukkan bahawa fungsi ini ialah fungsi antara muka perpustakaan templat pada FMZ (apakah perpustakaan templat bolehLihat), secara ringkas$.NewUniswapV3Fungsi membenarkan rujukan lain untuk iniPerpustakaan TemplatStrategi dipanggil terus. Strategi itu dimiliki secara langsungUniswap V3fungsi.

ini$.NewUniswapV3Fungsi secara langsung membina dan mencipta objek, dan objek ini boleh digunakan untuk melaksanakan beberapa operasi:

  • Pertukaran token: oleh objekswapTokenPelaksanaan kaedah.
  • Pertanyaan baki ETH: oleh objekgetETHBalancePelaksanaan kaedah.
  • Pertanyaan baki token: oleh objekbalanceOfPelaksanaan kaedah.
  • Pertanyaan harga pasangan dagangan: mengikut objekgetPricePelaksanaan kaedah.
  • Hantar ETH untuk pemindahan:sendETHPelaksanaan kaedah.

Pustaka ini mungkin tidak terhad kepada fungsi ini pada masa hadapan, malah mungkin dinaik taraf untuk menambah fungsi seperti "menambah kecairan". Mari teruskan membedah kod:

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)

Pembina$.NewUniswapV3Hanya satu parametere, e ini mewakili objek pertukaran (konfigurasi pertukaran pada FMZ). Oleh kerana strategi pada FMZ boleh direka bentuk untuk menjadi multi-exchange, jika pertukaran tertentu diluluskan di sini, ini bermakna ia telah dibuat.Uniswap V3Objek digunakan untuk mengendalikan objek pertukaran. Jika tiada parameter diluluskane, operasi lalai ialah objek pertukaran yang pertama ditambah.

Konfigurasikan alamat perkhidmatan nod dan kunci persendirian (kunci persendirian boleh digunakan secara setempat dan penggunaan tempatan hanya memerlukan konfigurasi laluan) untuk mencipta objek pertukaran. Ia boleh ditambah kepada strategi semasa perdagangan sebenar Objek ini ditunjukkan dalam kod strategi sebagaiexchangeiaituexchanges[0]Jika anda menambah yang kedua,exchanges[1], tambah satu pertigaexchanges[2],...

img

Alamat nod yang saya konfigurasikan dalam tangkapan skrin: https://mainnet.infura.io/v3/xxx ialah nod Infura Ini boleh digunakan oleh individu Setiap akaun mempunyai alamat khusus xxx bahagian adalah berbeza untuk setiap akaun.

Meneruskan kod, pembina mula menentukan sama ada objek pertukaran ialah Web3, dan melaporkan ralat jika ia bukan Web3. Kemudian mencipta pembolehubahself, diri ini ialah objek yang akhirnya dikembalikan oleh pembina Pembina seterusnya menambah pelbagai fungsi pada objek ini dan melaksanakan fungsi tertentu. Pembolehubah diri mempunyai 3 atribut:

  • tokenInfo: merekodkan maklumat token yang didaftarkan dalam objek Maklumat token termasuk alamat token, ketepatan token dan nama token.
  • walletAddress: Alamat dompet objek pertukaran semasa.
  • pool: Maklumat kolam pertukaran yang didaftarkan dalam objek ini, terutamanya nama kolam pertukaran dan alamat kolam pertukaran.

Kemudian kami menggunakan konsep yang kami pelajari dalam artikel sebelumnya:

e.IO("abi", ContractV3Factory, ABI_Factory) // 注册Uniswap V3 工厂合约的ABI e.IO("abi", ContractV3SwapRouterV2, ABI_Route) // 注册Uniswap Router V2 路由的ABI

Mengapa kita perlu mendaftarkan maklumat antara muka ini?

Kerana beberapa fungsi yang akan dilaksanakan kemudian memerlukan panggilan antara muka kontrak pintar ini. Langkah seterusnya ialah menambah pelbagai kaedah pada objek diri Selain kaedah yang disebutkan di atas: tebus token, semak baki, dll., terdapat juga beberapa fungsi alat yang dimiliki oleh objek diri ini.

Fungsi utiliti untuk objek diri

1、self.addToken = function(name, address)

Memerhati kod khusus fungsi ini, kita dapat melihat bahawa fungsi ini adalah untuk memberikan objek semasaselfRekodtokenAhli maklumattokenInfoTambah (dengan kata lain: daftar) maklumat token. sebabtokenData ketepatan (token) sering digunakan dalam pengiraan seterusnya, jadi apabila fungsi ini menambah maklumat token (berdaftar), ia memanggillet ret = e.IO("api", address, "decimals")Fungsi, melalui fungsi pertukaran.IO yang dikapsulkan oleh FMZ (seperti yang kami nyatakan sebelum ini, e ialah objek pertukaran masuk), memanggil kontrak token token"decimals"Kaedah untuk mendapatkan ketepatan token.

jadiself.tokenInfoIa ialah struktur kamus Setiap nama kunci ialah nama token, dan nilai kunci ialah maklumat token ini, termasuk: alamat, nama dan ketepatan. Ia mungkin kelihatan seperti ini:

{ "ETH": {name: "ETH", decimals: 18, address: "0x..."}, "USDT": {name: "USDT", decimals: 6, address: "0x..."}, ... }

2、self.waitMined = function(tx)

Fungsi ini digunakan untuk menunggu keputusan pelaksanaan kontrak pintar pada Ethereum Daripada kod pelaksanaan fungsi ini, kita dapat melihat bahawa fungsi ini telah dipanggil dalam gelung.let info = e.IO("api", "eth", "eth_getTransactionReceipt", tx), dengan memanggil kaedah RPC Ethereumeth_getTransactionReceipt, untuk bertanyaCincang transaksi mengembalikan resit untuk transaksi,parametertxiaituHash Transaksi

eth_getTransactionReceiptUntuk maklumat berkaitan, sila rujuk: https://ethereum.org/zh/developers/docs/apis/json-rpc/#eth_gettransactionreceipt

Sesetengah pelajar mungkin bertanya: Mengapa menggunakan fungsi ini?

J: Apabila melakukan beberapa operasi, seperti pertukaran token, anda perlu menunggu keputusan.

Seterusnya, mari kita lihat$.NewUniswapV3Mari kita mulakan dengan pelaksanaan paling mudah bagi fungsi utama lain bagi objek sendiri yang dicipta oleh fungsi tersebut.

Fungsi utama

1、self.getETHBalance = function(address)

Terdapat dua cara untuk menyemak baki token: semak baki ETH (Ethereum) dan semak baki token ERC20 yang lain. Fungsi getETHBalance bagi objek diri digunakan untuk menanyakan baki ETH Apabila alamat parameter alamat dompet tertentu dihantar, baki ETH alamat ini akan ditanya. Jika tiada parameter alamat diluluskan, pertanyaanself.walletAddressBaki ETH alamat (iaitu dompet yang dikonfigurasikan pada pertukaran semasa).

Ini dilakukan dengan memanggil kaedah RPC Ethereumeth_getBalancecapai.

2、self.balanceOf = function(token, address)

Untuk menanyakan baki token selain ETH, anda perlu memasukkan token parameter, iaitu nama token, seperti USDT. Masukkan alamat dompet untuk ditanya Jika tiada alamat dihantar, tanyaself.walletAddressBaki alamat. Memerhatikan kod yang dilaksanakan oleh fungsi ini, kita dapat melihat bahawa kita perlu lulusself.addTokenHanya token yang didaftarkan oleh fungsi boleh ditanya, kerana kontrak yang memanggil tokenbalanceOfkaedah, maklumat ketepatan dan alamat token diperlukan.

3、self.sendETH = function(to, amount, options)

Fungsinya adalah untuk memindahkan ETH ke alamat dompet (menggunakantoTetapan parameter) memindahkan sejumlah ETH tertentu (menggunakanamountTetapan parameter), anda boleh menetapkan yang lainoptionsParameter (struktur data:{gasPrice: 111, gasLimit: 111, nonce: 111}) digunakan untuk menentukangasLimit/gasPrice/nonceJika anda tidak lulus dalam parameter pilihan, tetapan lalai sistem akan digunakan.

gasLimit/gasPriceMempengaruhi ETH yang digunakan semasa melakukan operasi pada Ethereum (sesetengah operasi pada Ethereum menggunakan gas, iaitu, menggunakan sejumlah token ETH tertentu).

4、self.getPrice = function(pair, fee)

Fungsi ini digunakan untuk mendapatkan harga pasangan dagangan pada Uniswap Daripada kod pelaksanaan fungsi, kita dapat melihat bahawa apabila fungsi mula dilaksanakan, pasangan dagangan akan dihuraikan terlebih dahulu untuk mendapatkan mata wang asas dan sebut harga. Sebagai contoh, jika pasangan dagangan ialah ETH_USDT, ia akan dibahagikan kepada ETH dan USDT. Kemudian pertanyaanself.tokenInfoSemak sama ada terdapat maklumat tentang dua token ini dalam data Jika tidak, ralat akan dilaporkan.

Alamat kolam pertukaran di Uniswap terdiri daripada dua alamat token yang mengambil bahagian dan Yuran (standard kadar), jadi apabila membuat pertanyaanself.poolApabila alamat kolam direkodkan dalam (self.pool seperti yang kami nyatakan sebelum ini, anda boleh menyemaknya), jika ia tidak dijumpai, alamat dua token dan Yuran digunakan untuk mengira alamat kumpulan. Jadi satu pasangan dagangan mungkin mempunyai berbilang kumpulan kerana Yuran mungkin berbeza.

Tanya dan hitung alamat kolam pertukaran dengan menghubungi kontrak kilang Uniswap V3getPoolKaedah yang diperolehi (jadi ABI kontrak kilang mesti didaftarkan pada permulaan).
Sebaik sahaja anda mendapat alamat kumpulan pasangan dagangan ini, anda boleh mendaftarkan ABI kontrak kumpulan. Dengan cara ini, kolam (kontrak pintar) boleh dipanggilslot0kaedah untuk mendapatkan data harga. Sudah tentu, data yang dikembalikan melalui kaedah ini bukanlah harga yang boleh dibaca oleh manusia, tetapi struktur data yang berkaitan dengan harga diperlukan untuk mendapatkan harga yang boleh dibacacomputePoolPricefungsi.

5、self.swapToken = function(tokenIn, amountInDecimal, tokenOut, options)

Fungsinya adalah untuk menukar token Parameter tokenIn ialah nama token yang dibayar semasa pertukaran, parameter tokenOut ialah nama token yang diperoleh semasa pertukaran, parameter amountInDecimal ialah jumlah pertukaran (jumlah yang boleh dibaca manusia), dan pilihan parameter adalah sama seperti yang kami nyatakan sebelum ini. Sama seperti , anda boleh menetapkan penggunaan gas, nonce, dsb. semasa bertukar.

Apabila fungsi itu dilaksanakan, ia pertama kali diluluskanself.tokenInfoMaklumat token diperoleh daripada pembolehubah Pertukaran juga mempunyai banyak butiran Pertama sekali, jika token yang terlibat dalam pertukaran bukan ETH, anda perlu memberipenghalaan(Kontrak pintar bertanggungjawab untuk pertukaran) kebenaran. Sebelum kebenaran, semak sama ada terdapat jumlah kebenaran yang mencukupi.

let allowanceAmount = e.IO("api", tokenInInfo.address, "allowance", self.walletAddress, ContractV3SwapRouterV2);

Gunakan kaedah elaun kontrak token untuk menanyakan jumlah yang dibenarkan. Dengan membandingkan amaun yang dibenarkan dengan amaun penebusan semasa, jika jumlah yang dibenarkan adalah mencukupi untuk penebusan, tiada kebenaran lanjut diperlukan. Jika jumlahnya tidak mencukupi, pemprosesan kebenaran dilakukan.

Terdapat juga butiran dalam kebenaran di sini Jika token yang dibenarkan ialah USDT, anda perlu menetapkan semula kuantiti kebenaran kepada 0 sebelum membenarkan. Benarkan penggunaan kaedah kelulusan kontrak token. Ambil perhatian bahawa kaedah kebenaran meluluskan ialah kaedah menggunakan gas dan akan menggunakan sejumlah ETH tertentu. Jadi anda perlu menggunakan fungsi self.waitMined untuk menunggu hasil pemprosesan.

Untuk mengelakkan kebenaran yang kerap dan pembayaran ETH yang tidak diperlukan, operasi kebenaran ini membenarkan nilai maksimum pada satu masa.

let txApprove = e.IO("api", tokenInInfo.address, "approve", ContractV3SwapRouterV2, '0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff');

Jika anda mempunyai kuota pertukaran yang mencukupi, anda boleh membuat pertukaran. Tetapi terdapat juga butiran di sini Jika token yang terlibat dalam pertukaran adalah ETH, alamat penerima perlu diubah suai:

recipientAddress = '0x0000000000000000000000000000000000000002'

Sebab khusus agak rumit dan tidak akan dihuraikan di sini.

ADDRESS_THIS https://degencode.substack.com/p/uniswapv3-multicall
https://etherscan.io/address/0x68b3465833fb72a70ecdf485e0e4c7bd8665fc45#code

Kemudian gunakan fungsi pembungkusan yang dikapsulkan oleh platform FMZe.IO("encode", ..., pakejkan panggilan kaedah swapExactTokensForTokens untuk penghala (kontrak pintar Jika token yang diperoleh selepas pertukaran ialah ETH, anda perlu menambah langkah operasi pembongkaran WETH9:

data.push(e.IO("encode", ContractV3SwapRouterV2, "unwrapWETH9(uint256,address)", 1, self.walletAddress))

Kerana token yang terlibat dalam pertukaran adalah WETH, iaitu token ETH yang dibungkus. Untuk menukar kepada ETH sebenar, operasi pembongkaran diperlukan Selepas pembungkusan operasi pembongkaran, kaedah berbilang panggilan penghala (kontrak pintar) boleh dipanggil untuk melaksanakan siri operasi ini. Terdapat satu lagi butiran yang anda perlu beri perhatian tambahan Jika pasangan urus niaga yang terlibat dalam pertukaran adalah ETH, anda perlu menetapkan jumlah ETH untuk dipindahkan dalam langkah berikut. Jika ia bukan ETH, tetapkannya 0.

let tx = e.IO("api", ContractV3SwapRouterV2, "multicall(uint256,bytes[])", (tokenInInfo.name == 'ETH' ? amountIn : 0), (new Date().getTime() / 1000) + 3600, data, options || {})

Tetapan ini ditunjukkan di sini:(tokenInInfo.name == 'ETH' ? amountIn : 0). Editor tidak memikirkannya sebelum ini dan tidak menetapkan 0 apabila tokenIn tidak sama dengan token ETH, yang membawa kepada pemindahan ETH yang tersilap. Jadi lebih berhati-hati semasa menulis kod pemindahan.

Bahagian 4: Cara menggunakan objek operasi Uniswap V3

Kod dalam templat ini sebenarnya mempunyai kurang daripada 200 baris fungsi Bahagian berikut sebenarnya adalah demonstrasi penggunaan.

$.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()Fungsi ini hanyalah demonstrasi, sila jangan panggil ia jika ia tidak mempunyai kegunaan praktikal. Kami akan menggunakan fungsi ini untuk melihat cara menggunakan perpustakaan templat ini untuk mengendalikan fungsi Uniswap V3.

Kod pertama kali dilaksanakanlet ex = $.NewUniswapV3()Membina objek operasi Uniswap V3 Jika anda ingin mendapatkan alamat dompet yang terikat pada pertukaran semasa, anda boleh menggunakanex.walletAddressDapatkan. Kemudian gunakannya dalam kodex.addTokenTiga token telah didaftarkan, iaitu ETH, USDT, dan 1INCH.

Cetak harga pasangan dagangan (token perlu didaftarkan dahulu):

Log(ex.getPrice('ETH_USDT')) Log(ex.getPrice('1INCH_USDT'))

Jika fungsi getPrice tidak menetapkan Yuran, kadar lalai 3000 digunakan, yang ditukar kepada nilai boleh dibaca sebanyak 0.3%.

Jika anda ingin menukar 0.01 ETH kepada USDT, semak baki, dan kemudian tukar semula, gunakan kod:

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转账操作

Uji menggunakan testnet Goerli

  1. Konfigurasikan objek pertukaran testnet

Sila ambil perhatian bahawa apabila menetapkan nod, anda perlu menetapkannya kepada nod rangkaian ujian Goerli.

img

  1. Tulis strategi dan uji pada rangkaian ujian Goerli.
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')) }

Dalam kod ujian, kami menguji mencetak alamat dompet, mendaftarkan maklumat token, mencetak baki aset dan melakukan pertukaran berterusan.ETH -> UNI -> LINK. Perlu diingatkan bahawa alamat token yang didaftarkan di sini adalah pada rangkaian ujian Ethereum Goerli, jadi alamat token dengan nama yang sama adalah berbeza Bagi syiling ujian, anda boleh menggunakan keran rangkaian ujian ini untuk memohon token ujian. Anda boleh menyemak Google untuk butiran.

img

Ambil perhatian bahawa anda perlu menyemak templat "Perpustakaan Dagangan Uniswap V3" untuk menggunakannya$.NewUniswapV3()Fungsi, jika akaun FMZ anda tidak mempunyai template ini, anda boleh klikDapatkan di sini

Log operasi strategi:

img

img

Nilai aset dipaparkan pada halaman Uniswap

https://app.uniswap.org/

img

Operasi ini juga boleh ditanya pada rantaian:

https://goerli.etherscan.io/

img

Pertukaran ETH kepada UNI telah dilaksanakan sekali, kebenaran UNI dilaksanakan sekali, dan pertukaran UNI kepada LINK dilaksanakan sekali.

END

Pustaka ini mempunyai banyak fungsi yang boleh dikembangkan, malah boleh dikembangkan untuk membungkus berbilang penebusantokenA -> tokenB -> tokenCPertukaran laluan. Ia boleh dioptimumkan dan dikembangkan mengikut keperluan khusus Kod perpustakaan jenis ini disediakan terutamanya untuk pengajaran.

memperbaharui

Dinaik tarafswapTokenFungsi, menyokongtokenA -> tokenB -> tokenC ... -> tokenDFungsi penebusan berterusan. Anda boleh melihat kod terbaharu templat ini yang diterbitkan di Strategy Square di FMZ.

Comment
All comments (3)

    梦总,有python版本的吗

    3 years ago

    可以回头移植一个,调用都一样。

    3 years ago

    学习中,mark

    3 years ago
  • 1
iPhone Download
Forums
PINE Language
© 2015 - ∞ INVENTOR PTE LTD (SG)