
Com a popularidade do conceito Defi nos últimos anos, o Uniswap V3 se tornou um dos tópicos mais populares no campo das finanças descentralizadas (DeFi). Como o principal protocolo de troca descentralizada, o Uniswap V3 oferece uma experiência de usuário mais eficiente, mais segura e melhor. Agora, com apenas 200 linhas de código, traders e desenvolvedores podem acessar facilmente o Uniswap V3 na plataforma FMZ.
FMZ é uma plataforma de negociação quantitativa que oferece suporte ao desenvolvimento, backtesting e implantação em tempo real de estratégias de negociação quantitativa. Com sua interface fácil de usar e recursos poderosos, é fácil entender por que a FMZ está se tornando a primeira escolha para traders e desenvolvedores DeFi.
O processo de integração do Uniswap V3 no FMZ é simples e fácil de entender, e requer apenas 200 linhas de código para ser concluído. Isso significa que, mesmo que você seja novo em codificação, você pode se conectar facilmente ao Uniswap V3 na FMZ e começar a negociar imediatamente.
A FMZ encapsulou uma série de funções básicas da web3. Além da Uniswap, outras exchanges DEX também podem ser encapsuladas com muito pouco código. Em seguida, deixe-me levá-lo para aprender os conceitos e tecnologias em aplicativos DeFi do zero. Devido a restrições de espaço, a descrição a seguir tenta usar a maneira mais simples e compreensível. Pode não ser muito rigoroso, mas é fácil de entender.
A plataforma FMZ está aberta ao público「Biblioteca de transações Uniswap V3」
O código é o seguinte:
/* 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))
}
A rede Ethereum pode ser entendida como uma infraestrutura de software na qual vários contratos inteligentes podem ser implantados e executados. Os contratos inteligentes têm várias funções e cenários de aplicação. Dispositivos que executam o cliente Ethereum constituem nós na rede Ethereum.
DesconhecidoUniswap V3Para aqueles que são novos no protocolo, é preciso primeiro entender alguns conceitos.Uniswap V3É também um contrato inteligente implantado e executado no Ethereum.
tokenintercâmbio.Quando um contrato inteligente é implantado no Ethereum, ele tem um endereço.
O código da biblioteca de negociação Uniswap V3 é dividido principalmente em 4 partes. Vamos explicá-las uma por uma.
const ABI_Route = '[{"inputs":[{"internalType":"address...
const ABI_Pool = '[{\"inputs\":[],\"stateMutability\":\"nonpayable...
const ABI_Factory = '[{\"inputs\":[],\"stateMutability\":\"...
let ContractV3Factory = "0x1F98431c8aD98523631AE4a59f267346ea31F984"
let ContractV3SwapRouterV2 = "0x68b3465833fb72A70ecDF485E0e4C7bD8665Fc45"
Com os conceitos básicos acima aquecidos, fica fácil entender aqui.
ABI_RouteA string armazenada nesta constante é o ABI do contrato inteligente do roteador.
ABI_PoolABI do contrato do pool de armazenamento.
ABI_FactoryO ABI para o contrato de fábrica.
Como essas sequências são muito longas, elas são apenas trechos. Esses conteúdos fornecem ao programa padrões para chamar métodos de contrato inteligente (por exemplo, quais são os parâmetros dessa interface de contrato inteligente, quantos parâmetros existem, quais são os tipos, que tipo de dados retornados são, etc.).
Mencionamos anteriormente que, quando um contrato inteligente é implantado no Ethereum, ele tem um endereço.
ContractV3Factory: Registra o endereço do contrato de fábrica.
ContractV3SwapRouterV2:O endereço do roteador V2 do Uniswap V3. Observe que o Uniswap tem V1 e V2, e o roteador do Uniswap V3 também tem V1 e V2. Os endereços de diferentes contratos são diferentes.
1、computePoolPriceA função é usada para calcular o preço dos tokens no pool.
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)
);
}
Se o par de negociação forETH_USDT,Entãotoken0simETH,token1simUSDT。decimals0aquilo étoken0Os dados de precisão,decimals1aquilo étoken1Os dados de precisão.sqrtPriceX96Estes são dados relacionados com o preço (não valores de preços diretos), que podem ser obtidos no contrato do poolslot0Aquisição de método.
sqrtPriceX96 : The current price of the pool as a sqrt(token1/token0) Q64.96 value Q64.96 é um padrão de armazenamento de processamento de dados.
decimals0,decimals1, sqrtPriceX96Esses três dados são passados como parâmetroscomputePoolPriceA função pode calcular o par de transaçõesETH_USDTpreço. A função finalmentereturnO algoritmo na declaração é convertersqrtPriceX96Restaurartoken1/token0processo. Por exemplo, neste momento, o número de token0 (ETH) no pool é 1, e o número de token1 (USDT) é 1100. Então1100/1=1100, o par de negociação atualETH_USDTO preço na piscina é 1100.
2、toAmountA função é usada para converter os dados numéricos na cadeia em dados legíveis.
function toAmount(s, decimals) {
return Number((BigDecimal(BigInt(s))/BigDecimal(Math.pow(10, decimals))).toString())
}
Para simplificar, por exemplo, a quantidade de um token ETH representado na cadeia é 1e18, que é 10 elevado à 18ª potência, porque os dados de precisão do ETH são 18. Nem todos os tokens têm uma precisão de 18. A precisão do USDT é diferente daquela do ETH.toAmountA função converte 1e18 em 1.
3、toInnerAmountFunção etoAmountEm vez disso, ele converte dados legíveis em valores numéricos usados na cadeia.
function toInnerAmount(n, decimals) {
return (BigDecimal(n)*BigDecimal(Math.pow(10,decimals))).toFixed(0)
}
A seguir, vamos analisar juntos o código da “Uniswap V3 Trading Library”.
O núcleo desta biblioteca de classes de modelo é o objeto de operação Uniswap V3, que implementa operações básicas no Uniswap V3. Mais funções podem ser atualizadas posteriormente. Ao analisar este exemplo de código, mesmo que você não use a plataforma FMZ, você aumentará sua compreensão deUniswapesseDEXPara entender os processos e detalhes de cada link, vamos agora aprender como essas funções básicas são projetadas e implementadas no FMZ.
Código do construtor do objeto de operação Uniswap V3:
”`js $.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 = [sw