Type/to search
8
Follow
1364
Followers
Elegant und einfach! Verbunden mit Uniswap V3 auf FMZ mit 200 Zeilen Code
Discussions
Created 2023-01-30 15:38:02  Updated 2023-09-18 19:39:40
 5
 3104

img

Elegant und einfach! Verbunden mit Uniswap V3 auf FMZ mit 200 Zeilen Code

Mit der Popularität des Defi-Konzepts in den letzten Jahren ist Uniswap V3 zu einem der beliebtesten Themen im Bereich der dezentralen Finanzen (DeFi) geworden. Als führendes dezentrales Austauschprotokoll bietet Uniswap V3 ein effizienteres, sichereres und besseres Benutzererlebnis. Jetzt können Händler und Entwickler mit nur 200 Codezeilen problemlos auf Uniswap V3 auf der FMZ-Plattform zugreifen.

FMZ ist eine quantitative Handelsplattform, die die Entwicklung, das Backtesting und die Echtzeitbereitstellung quantitativer Handelsstrategien unterstützt. Mit seiner benutzerfreundlichen Oberfläche und den leistungsstarken Funktionen ist es leicht zu verstehen, warum FMZ zur ersten Wahl für DeFi-Händler und -Entwickler wird.

Der Prozess der Integration von Uniswap V3 in FMZ ist einfach und leicht verständlich und erfordert zur Fertigstellung nur 200 Zeilen Code. Dies bedeutet, dass Sie sich auch dann problemlos mit Uniswap V3 auf FMZ verbinden und sofort mit dem Handel beginnen können, wenn Sie keine Erfahrung mit der Codierung haben.

FMZ hat eine Reihe grundlegender Web3-Funktionen gekapselt. Neben Uniswap können auch andere DEX-Börsen mit sehr wenig Code gekapselt werden. Als Nächstes möchte ich Ihnen die Konzepte und Technologien von DeFi-Anwendungen von Grund auf näherbringen. Aus Platzgründen wird in der folgenden Beschreibung versucht, den einfachsten und verständlichsten Weg zu verwenden. Dies ist vielleicht nicht sehr streng, aber leicht zu verstehen.

Die FMZ-Plattform ist öffentlich zugänglich„Uniswap V3-Transaktionsbibliothek“

Der Code lautet wie folgt:

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

Ethereum-Netzwerk

Das Ethereum-Netzwerk kann als Software-Infrastruktur verstanden werden, auf der verschiedene Smart Contracts bereitgestellt und ausgeführt werden können. Smart Contracts haben verschiedene Funktionen und Anwendungsszenarien. Geräte, auf denen der Ethereum-Client läuft, stellen Knoten im Ethereum-Netzwerk dar.

Einige Konzepte in Uniswap V3

UnbekanntUniswap V3Wenn Sie mit dem Protokoll noch nicht vertraut sind, müssen Sie zunächst einige Konzepte verstehen.Uniswap V3Es handelt sich außerdem um einen Smart Contract, der auf Ethereum bereitgestellt und ausgeführt wird.

  1. Route: Route ist auch ein Smart Contract zur VerwaltungtokenAustausch.
  2. Pool: Ein Pool ist ebenfalls ein Smart Contract, der zum Speichern und Austauschen von zwei Ethereum-Tokens zwischen ihnen dient.
  3. Fabrikvertrag: Der Fabrikvertrag ist ein Smart Contract, der zum Erstellen eines Pools verwendet wird.
  4. ABI: (Application Binary Interface) ist eine Spezifikation, die beschreibt, wie Smart Contracts mit der Außenwelt kommunizieren. Es gibt den Funktionsnamen, die Parametertypen und die Rückgabewerttypen des Smart Contracts an, sowie wie Daten kodiert und dekodiert werden, und bestimmt die externe Schnittstelle des Smart Contracts. Es versteht sich, dass zum Aufrufen einer bestimmten Schnittstelle dieser gemäß den von der Schnittstelle vereinbarten Standards aufgerufen werden muss und die ABI eine Reihe vereinbarter Standards aufzeichnet.

Sobald ein Smart Contract auf Ethereum bereitgestellt wird, verfügt er über eine Adresse.

Analyse des Codes der Uniswap V3-Handelsbibliothek

Der Code der Uniswap V3-Handelsbibliothek ist hauptsächlich in vier Teile unterteilt. Lassen Sie uns diese nacheinander erklären.

Teil 1: Konstanten, die bei der Interaktion mit Uniswap V3 verwendet werden

const ABI_Route = '[{"inputs":[{"internalType":"address... const ABI_Pool = '[{\"inputs\":[],\"stateMutability\":\"nonpayable... const ABI_Factory = '[{\"inputs\":[],\"stateMutability\":\"... let ContractV3Factory = "0x1F98431c8aD98523631AE4a59f267346ea31F984" let ContractV3SwapRouterV2 = "0x68b3465833fb72A70ecDF485E0e4C7bD8665Fc45"

Wenn man die oben genannten Grundkonzepte aufgewärmt hat, ist es hier leicht zu verstehen.

ABI_RouteDie in dieser Konstante gespeicherte Zeichenfolge ist die ABI des Router-Smart-Vertrags.
ABI_PoolABI des Speicherpoolvertrags.
ABI_FactoryDas ABI zum Werksvertrag.

Da diese Strings sehr lang sind, handelt es sich hierbei lediglich um Auszüge. Diese Inhalte stellen dem Programm Standards zum Aufrufen von Smart-Contract-Methoden zur Verfügung (z. B. was sind die Parameter dieser Smart-Contract-Schnittstelle, wie viele Parameter gibt es, um welchen Typ es sich handelt, um welche Art von zurückgegebenen Daten es sich handelt usw.).

Wir haben bereits erwähnt, dass ein Smart Contract eine Adresse hat, sobald er auf Ethereum bereitgestellt wird.

ContractV3Factory: Erfasst die Adresse des Fabrikvertrags.
ContractV3SwapRouterV2:Die Router-V2-Adresse von Uniswap V3. Bitte beachten Sie, dass Uniswap V1 und V2 hat und der Router von Uniswap V3 auch V1 und V2 hat. Die Adressen verschiedener Verträge sind unterschiedlich.

Teil 2: Werkzeugfunktionen

1、computePoolPriceDie Funktion wird verwendet, um den Preis der Token im Pool zu berechnen.

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

Wenn das HandelspaarETH_USDT,Alsotoken0JaETHtoken1JaUSDTdecimals0das heißttoken0Die Genauigkeitsdaten,decimals1das heißttoken1Die Genauigkeitsdaten.sqrtPriceX96Es handelt sich hierbei um preisbezogene Daten (keine direkten Preiswerte), die aus dem Poolvertrag entnommen werden können.slot0Methodenerwerb.

sqrtPriceX96 : The current price of the pool as a sqrt(token1/token0) Q64.96 value
Q64.96 ist ein Speicherstandard für die Datenverarbeitung.

decimals0decimals1sqrtPriceX96Diese drei Daten werden als Parameter übergebencomputePoolPriceDie Funktion kann das Transaktionspaar berechnenETH_USDTPreis. Die Funktion schließlichreturnDer Algorithmus in der Anweisung besteht darin,sqrtPriceX96Wiederherstellentoken1/token0Verfahren. Beispielsweise beträgt derzeit die Anzahl der Token0 (ETH) im Pool 1 und die Anzahl der Token1 (USDT) 1100. Also1100/1=1100, das aktuelle HandelspaarETH_USDTDer Preis im Pool beträgt 1100.

2、toAmountDie Funktion wird verwendet, um die numerischen Daten in der Kette in lesbare Daten umzuwandeln.

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

Um es einfach auszudrücken: Die Menge eines in der Kette dargestellten ETH-Tokens beträgt beispielsweise 1e18, also 10 hoch 18, da die Genauigkeitsdaten von ETH 18 betragen. Nicht alle Token haben eine Genauigkeit von 18. Die Genauigkeit von USDT unterscheidet sich von der von ETH.toAmountDie Funktion wandelt 1e18 in 1 um.

3、toInnerAmountFunktion undtoAmountStattdessen wandelt es lesbare Daten in numerische Werte um, die in der Kette verwendet werden.

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

Als nächstes analysieren wir gemeinsam den Code der „Uniswap V3 Trading Library“.

Teil 3: Konstruktor des Uniswap V3-Operationsobjekts

Der Kern dieser Vorlagenklassenbibliothek ist das Uniswap V3-Operationsobjekt, das grundlegende Operationen auf Uniswap V3 implementiert. Möglicherweise werden in Zukunft noch weitere Funktionen nachgerüstet. Durch die Analyse dieses Codebeispiels, auch wenn Sie die FMZ-Plattform nicht verwenden, verbessern Sie Ihr Verständnis fürUniswapDasDEXUm die Prozesse und Details der einzelnen Links zu verstehen, wollen wir uns nun ansehen, wie diese grundlegenden Funktionen auf FMZ konzipiert und implementiert werden.

Konstruktorcode des Uniswap V3-Operationsobjekts:

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 }

Studierende, die mit FMZ nicht vertraut sind, sehen diese Funktion möglicherweise$.NewUniswapV3Die Namensgebung ist etwas seltsam, mit$.Die Funktion am Anfang zeigt an, dass diese Funktion die Schnittstellenfunktion der Vorlagenbibliothek auf FMZ ist (was die Vorlagenbibliothek sein kannSicht), einfach ausgedrückt$.NewUniswapV3Funktion ermöglicht andere Verweise auf dieseVorlagenbibliothekDie Strategie wird direkt aufgerufen. Die Strategie ist direkt im BesitzUniswap V3Funktion.

Das$.NewUniswapV3Die Funktion erstellt und erstellt direkt ein Objekt. Mit diesem Objekt können einige Vorgänge ausgeführt werden:

  • Token-Austausch: nach ObjektswapTokenImplementierung der Methode.
  • ETH-Guthabenabfrage: nach ObjektgetETHBalanceImplementierung der Methode.
  • Abfrage des Token-Guthabens: nach ObjektbalanceOfImplementierung der Methode.
  • Handelspaarpreisabfrage: nach ObjektgetPriceImplementierung der Methode.
  • Senden Sie ETH zur Überweisung:sendETHImplementierung der Methode.

Diese Bibliothek ist in Zukunft möglicherweise nicht mehr auf diese Funktionen beschränkt und kann sogar um Funktionen wie das „Hinzufügen von Liquidität“ erweitert werden. Lassen Sie uns mit der Analyse des Codes fortfahren:

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)

Konstruktor$.NewUniswapV3Nur ein Parametere, dieses e stellt das Austauschobjekt dar (Austauschkonfiguration auf FMZ). Da die Strategie auf FMZ als Multi-Exchange-Strategie konzipiert werden kann, bedeutet die Übergabe eines bestimmten Exchanges hier, dass dieser erstellt wurde.Uniswap V3Das Objekt dient zum Betrieb des Tauschobjektes. Wenn keine Parameter übergeben werdene, die Standardoperation ist das erste hinzugefügte Austauschobjekt.

Konfigurieren Sie die Knotendienstadresse und den privaten Schlüssel (der private Schlüssel kann lokal bereitgestellt werden und für die lokale Bereitstellung muss nur der Pfad konfiguriert werden), um ein Austauschobjekt zu erstellen. Es kann der Strategie während des realen Handels hinzugefügt werden. Dieses Objekt wird im Strategiecode alsexchangeDas heißtexchanges[0]Wenn Sie ein zweites hinzufügen,exchanges[1], fügen Sie ein drittesexchanges[2],...

img

Die Knotenadresse, die ich im Screenshot konfiguriert habe: https://mainnet.infura.io/v3/xxx ist der Knoten von Infura. Dies kann von Einzelpersonen angewendet werden. Jedes Konto hat seine eigene spezifische Adresse. xxx ist die Maske. Die xxx Teil ist für jedes Konto anders.

Im weiteren Verlauf des Codes beginnt der Konstruktor zu ermitteln, ob es sich bei dem Austauschobjekt um Web3 handelt, und meldet einen Fehler, wenn es sich nicht um Web3 handelt. Dann erstellte man eine Variableself, dieses Selbst ist das Objekt, das schließlich vom Konstruktor zurückgegeben wird. Nachfolgende Konstruktoren fügen diesem Objekt verschiedene Funktionen hinzu und implementieren bestimmte Funktionen. Die Variable „self“ hat drei Attribute:

  • tokenInfo: zeichnet die im Objekt registrierten Tokeninformationen auf. Die Tokeninformationen umfassen die Tokenadresse, die Tokenpräzision und den Tokennamen.
  • walletAddress: Die Wallet-Adresse des aktuellen Exchange-Objekts.
  • Pool: Die in diesem Objekt registrierten Exchange-Pool-Informationen, hauptsächlich der Exchange-Pool-Name und die Exchange-Pool-Adresse.

Dann haben wir die Konzepte verwendet, die wir im vorherigen Artikel gelernt haben:

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

Warum müssen wir diese Schnittstelleninformationen registrieren?

Denn manche später umzusetzende Funktionen erfordern den Aufruf der Schnittstellen dieser Smart Contracts. Der nächste Schritt besteht darin, dem Self-Objekt verschiedene Methoden hinzuzufügen. Zusätzlich zu den oben genannten Methoden (Token einlösen, Guthaben prüfen usw.) gehören auch einige Tool-Funktionen zum Self-Objekt. Lassen Sie uns zunächst diese Tool-Funktionen analysieren.

Hilfsfunktionen für das Selbstobjekt

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

Wenn wir den spezifischen Code dieser Funktion beobachten, können wir sehen, dass diese Funktion das aktuelle Objekt zurückgibtselfAufzeichnentokenMitglieder der InformationsstelletokenInfoFügen Sie eine Token-Information hinzu (mit anderen Worten: registrieren Sie sie). WeiltokenDie Genauigkeitsdaten des (Tokens) werden häufig in nachfolgenden Berechnungen verwendet. Wenn diese Funktion also (registrierte) Token-Informationen hinzufügt, ruft sie auflet ret = e.IO("api", address, "decimals")Funktion, durch die von FMZ gekapselte Funktion exchange.IO (wie bereits erwähnt, ist e das eingehende Austauschobjekt), ruft den Token-Token-Vertrag auf"decimals"Methode zum Abrufen der Token-Präzision.

Alsoself.tokenInfoEs handelt sich um eine Wörterbuchstruktur. Jeder Schlüsselname ist der Tokenname und der Schlüsselwert sind die Informationen dieses Tokens, einschließlich: Adresse, Name und Genauigkeit. Es könnte so aussehen:

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

2、self.waitMined = function(tx)

Diese Funktion wird verwendet, um auf das Ausführungsergebnis des Smart Contracts auf Ethereum zu warten. Aus dem Implementierungscode dieser Funktion können wir erkennen, dass diese Funktion in einer Schleife aufgerufen wurde.let info = e.IO("api", "eth", "eth_getTransactionReceipt", tx), durch Aufruf der RPC-Methode von Ethereumeth_getTransactionReceipt, abfragenDer Transaktions-Hash gibt die Quittung für die Transaktion zurück,ParametertxDas heißtTransaktions-Hash

eth_getTransactionReceiptWeitere Informationen finden Sie unter: https://ethereum.org/zh/developers/docs/apis/json-rpc/#eth_gettransactionreceipt

Einige Studierende fragen sich vielleicht: Warum diese Funktion verwenden?

A: Wenn Sie einige Vorgänge durchführen, z. B. einen Token-Austausch, müssen Sie auf die Ergebnisse warten.

Als nächstes schauen wir uns an$.NewUniswapV3Beginnen wir mit der einfachsten Implementierung anderer Hauptfunktionen des von der Funktion selbst erstellten Objekts.

Hauptfunktionen

1、self.getETHBalance = function(address)

Es gibt zwei Möglichkeiten, den Token-Saldo zu überprüfen: Überprüfen Sie den ETH-Saldo (Ethereum) und überprüfen Sie den Saldo anderer ERC20-Token. Die getETHBalance-Funktion des Self-Objekts wird verwendet, um den ETH-Kontostand abzufragen. Wenn der spezifische Wallet-Adressparameter address übergeben wird, wird der ETH-Kontostand dieser Adresse abgefragt. Wenn kein Adressparameter übergeben wird, Abfrageself.walletAddressDer ETH-Guthaben der Adresse (d. h. das auf der aktuellen Börse konfigurierte Wallet).

Dies geschieht durch den Aufruf der RPC-Methoden von Ethereumeth_getBalanceerreichen.

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

Um den Kontostand anderer Token als ETH abzufragen, müssen Sie den Parameter „Token“ übergeben. Dabei handelt es sich um den Token-Namen, z. B. USDT. Geben Sie die abzufragende Wallet-Adresse ein. Wenn keine Adresse übergeben wird, wird abgefragtself.walletAddressDer Restbetrag der Adresse. Wenn wir den von dieser Funktion implementierten Code betrachten, können wir sehen, dass wir übergeben müssenself.addTokenEs können nur Token abgefragt werden, die von der Funktion registriert wurden, da der Vertrag, der das Token aufruftbalanceOfMethode werden die Genauigkeitsinformationen und die Adresse des Tokens benötigt.

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

Die Funktion besteht darin, ETH an eine Wallet-Adresse zu übertragen (mithilfetoParametereinstellungen) übertragen Sie eine bestimmte Menge ETH (mitamountParametereinstellungen) können Sie eine andereoptionsParameter (Datenstruktur:{gasPrice: 111, gasLimit: 111, nonce: 111}) wird verwendet, um anzugebengasLimit/gasPrice/nonceWenn Sie den Optionsparameter nicht übergeben, werden die Standardeinstellungen des Systems verwendet.

gasLimit/gasPriceBeeinflusst den ETH-Verbrauch bei der Durchführung von Operationen auf Ethereum (einige Operationen auf Ethereum verbrauchen Gas, d. h. sie verbrauchen eine bestimmte Menge an ETH-Token).

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

Diese Funktion wird verwendet, um den Preis eines Handelspaars auf Uniswap zu ermitteln. Aus dem Funktionsimplementierungscode können wir ersehen, dass beim Ausführen der Funktion zuerst das Handelspaar analysiert wird, um die Basiswährung und die Kurswährung zu ermitteln. Wenn das Handelspaar beispielsweise ETH_USDT ist, wird es in ETH und USDT aufgeteilt. Dann fragen Sieself.tokenInfoÜberprüfen Sie, ob in den Daten Informationen zu diesen beiden Token vorhanden sind. Wenn nicht, wird ein Fehler gemeldet.

Die Exchange-Pool-Adresse auf Uniswap setzt sich aus den beiden teilnehmenden Token-Adressen und der Gebühr (Ratenstandard) zusammen. Bei der Abfrageself.poolWenn die Pooladresse in (self.pool, wie bereits erwähnt, können Sie sie überprüfen) aufgezeichnet ist und sie nicht gefunden wird, werden die Adressen der beiden Token und die Gebühr zum Berechnen der Pooladresse verwendet. Ein Handelspaar kann also mehrere Pools haben, da die Gebühr unterschiedlich sein kann.

Abfrage und Berechnung der Adresse des Exchange-Pools durch Aufruf des Uniswap V3 Factory-VertragsgetPoolMethode erhalten (daher muss der ABI des Fabrikvertrags zu Beginn registriert werden).
Sobald Sie die Pooladresse dieses Handelspaares erhalten, können Sie die ABI des Poolvertrags registrieren. Auf diese Weise kann der Pool (Smart Contract) aufgerufen werdenslot0Methode zum Abrufen von Preisdaten. Natürlich sind die von dieser Methode zurückgegebenen Daten kein für Menschen lesbarer Preis, sondern eine mit dem Preis verbundene Datenstruktur. Um einen lesbaren Preis zu erhalten, ist eine weitere Verarbeitung erforderlich. In diesem Fall verwenden wir diecomputePoolPriceFunktion.

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

Die Funktion besteht darin, Token auszutauschen. Der Parameter tokenIn ist der Name des Tokens, der beim Austausch bezahlt wurde, der Parameter tokenOut ist der Name des Tokens, der beim Austausch erhalten wurde, der Parameter amountInDecimal ist der Austauschbetrag (für Menschen lesbarer Betrag) und Die Parameteroptionen sind dieselben wie zuvor erwähnt. Genau wie können Sie beim Austausch Gasverbrauch, Nonce usw. festlegen.

Wenn die Funktion ausgeführt wird, wird ihr zunächstself.tokenInfoDie Token-Informationen werden aus der Variable abgerufen. Der Austausch hat auch viele Details. Zunächst einmal müssen Sie angeben, wenn der am Austausch beteiligte Token nicht ETH istRoutenplanung(Smart Contract, der für den Austausch verantwortlich ist) Autorisierung. Prüfen Sie vor der Autorisierung, ob der Autorisierungsbetrag ausreichend ist.

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

Verwenden Sie die Token-Vertragszuteilungsmethode, um den autorisierten Betrag abzufragen. Durch Vergleich des autorisierten Betrags mit dem aktuellen Einlösebetrag wird festgestellt, dass, wenn der autorisierte Betrag für die Einlösung ausreicht, keine weitere Autorisierung erforderlich ist. Wenn der Betrag nicht ausreicht, wird eine Autorisierungsverarbeitung durchgeführt.

Auch hier gibt es ein Detail bei der Autorisierung. Wenn der autorisierte Token USDT ist, müssen Sie vor der Autorisierung die Autorisierungsmenge auf 0 zurücksetzen. Autorisieren Sie die Verwendung der Genehmigungsmethode des Token-Vertrags. Beachten Sie, dass die Autorisierungsmethode „Genehmigen“ eine gasverbrauchende Methode ist und eine bestimmte Menge an ETH verbraucht. Sie müssen also die Funktion self.waitMined verwenden, um auf das Verarbeitungsergebnis zu warten.

Um häufige Autorisierungen und unnötige ETH-Zahlungen zu vermeiden, autorisiert dieser Autorisierungsvorgang den Höchstwert auf einmal.

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

Wenn Ihr Umtauschkontingent ausreicht, können Sie den Umtausch vornehmen. Aber auch hier gibt es Details. Handelt es sich bei dem Token, um den es beim Tausch geht, um ETH, muss die Empfangsadresse angepasst werden:

recipientAddress = '0x0000000000000000000000000000000000000002'

Die genauen Gründe sind recht komplex und werden hier nicht näher erläutert. Bitte beachten Sie:

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

Dann nutzen Sie die in der FMZ-Plattform gekapselte Verpackungsfunktione.IO("encode", ..., packen Sie den Methodenaufruf swapExactTokensForTokens für den Router (Smart Contract). Wenn das nach dem Austausch erhaltene Token ETH ist, müssen Sie einen Schritt der WETH9-Entpackungsoperation hinzufügen:

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

Denn der an der Börse beteiligte Token ist WETH, ein verpackter Token von ETH. Zur Konvertierung in echtes ETH ist ein Entpackvorgang erforderlich. Nach dem Verpacken des Entpackvorgangs kann die Multicall-Methode des Routers (Smart Contract) aufgerufen werden, um diese Reihe von Vorgängen auszuführen. Es gibt noch ein weiteres Detail, auf das Sie besonders achten müssen. Wenn das an der Börse beteiligte Transaktionspaar ETH ist, müssen Sie in den folgenden Schritten den zu übertragenden ETH-Betrag festlegen. Wenn es nicht ETH ist, legen Sie es auf 0.

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

Diese Einstellung wird hier wiedergegeben:(tokenInInfo.name == 'ETH' ? amountIn : 0). Der Editor hat dies vorher nicht erkannt und nicht 0 gesetzt, wenn tokenIn nicht dem ETH-Token entsprach, was zur irrtümlichen Übertragung von ETH führte. Seien Sie beim Schreiben von Transfercode daher besonders vorsichtig.

Teil 4: So verwenden Sie Uniswap V3-Operationsobjekte

Der Code in dieser Vorlage hat tatsächlich weniger als 200 Zeilen Funktionalität. Der folgende Abschnitt ist eigentlich eine Anwendungsdemonstration.

$.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()Diese Funktion dient nur zu einer Demonstration. Bitte rufen Sie sie nicht auf, wenn sie keinen praktischen Nutzen hat. Wir werden diese Funktion verwenden, um zu sehen, wie diese Vorlagenbibliothek zum Bedienen der Funktionen von Uniswap V3 verwendet wird.

Der Code wird zuerst ausgeführtlet ex = $.NewUniswapV3()Erstellt ein Uniswap V3-Operationsobjekt. Wenn Sie die an die aktuelle Börse gebundene Wallet-Adresse erhalten möchten, können Sie Folgendes verwenden:ex.walletAddressErhalten. Verwenden Sie es dann im Codeex.addTokenEs wurden drei Token registriert, nämlich ETH, USDT und 1INCH.

Drucken Sie den Preis eines Handelspaares (Token muss zuerst registriert werden):

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

Wenn die Funktion getPrice keine Gebühr festlegt, wird der Standardsatz von 3000 verwendet, der in einen lesbaren Wert von 0,3 % umgewandelt wird.

Wenn Sie 0,01 ETH in USDT umtauschen, den Kontostand prüfen und dann zurücktauschen möchten, verwenden Sie den Code:

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

Testen mit dem Goerli-Testnetz

  1. Konfigurieren Sie das Testnet-Austauschobjekt

Bitte beachten Sie, dass Sie beim Setzen des Knotens unbedingt den Knoten des Testnetzes Goerli einstellen müssen.

img

  1. Schreiben Sie eine Strategie und testen Sie diese im Testnetzwerk 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')) }

Im Testcode haben wir das Drucken von Wallet-Adressen, das Registrieren von Token-Informationen, das Drucken von Vermögensguthaben und die Durchführung eines kontinuierlichen Austauschs getestet.ETH -> UNI -> LINK. Es ist zu beachten, dass sich die hier registrierte Token-Adresse im Ethereum-Testnetzwerk Goerli befindet, sodass die Token-Adressen mit demselben Namen unterschiedlich sind. Was Testmünzen betrifft, können Sie den Wasserhahn dieses Testnetzwerks verwenden, um Testtoken zu beantragen. Sie Weitere Einzelheiten finden Sie bei Google.

img

Beachten Sie, dass Sie die Vorlage „Uniswap V3 Trading Library“ überprüfen müssen, um sie verwenden zu können$.NewUniswapV3()Funktion, wenn Ihr FMZ-Konto diese Vorlage nicht hat, können Sie aufHier erhalten Sie es

Protokoll der Strategieoperationen:

img

img

Auf der Uniswap-Seite angezeigte Vermögenswerte

https://app.uniswap.org/

img

Diese Operationen können auch in der Kette abgefragt werden:

https://goerli.etherscan.io/

img

Der Umtausch von ETH in UNI wurde einmal ausgeführt, die Autorisierung von UNI wurde einmal ausgeführt und der Umtausch von UNI in LINK wurde einmal ausgeführt.

END

Diese Bibliothek verfügt über viele erweiterbare Funktionen und kann sogar erweitert werden, um mehrere Einlösungen zu verpackentokenA -> tokenB -> tokenCWegeaustausch. Er kann je nach Bedarf optimiert und erweitert werden. Diese Art von Bibliothekscode wird hauptsächlich für den Unterricht bereitgestellt.

erneuern

VerbessertswapTokenFunktion, unterstützttokenA -> tokenB -> tokenC ... -> tokenDKontinuierliche Einlösefunktion. Sie können den neuesten Code dieser Vorlage anzeigen, der auf Strategy Square auf FMZ veröffentlicht wurde.

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)