diff --git a/contracts/zero-ex/CHANGELOG.json b/contracts/zero-ex/CHANGELOG.json index 5b77ef6575..b139b90f40 100644 --- a/contracts/zero-ex/CHANGELOG.json +++ b/contracts/zero-ex/CHANGELOG.json @@ -1,4 +1,13 @@ [ + { + "version": "0.42.0", + "changes": [ + { + "note": "Add Trader Joe V2 support on Avalanche" + } + ], + "timestamp": 1681960738 + }, { "version": "0.41.0", "changes": [ diff --git a/contracts/zero-ex/contracts/src/transformers/bridges/AvalancheBridgeAdapter.sol b/contracts/zero-ex/contracts/src/transformers/bridges/AvalancheBridgeAdapter.sol index 4c4865515d..f2ce9879c6 100644 --- a/contracts/zero-ex/contracts/src/transformers/bridges/AvalancheBridgeAdapter.sol +++ b/contracts/zero-ex/contracts/src/transformers/bridges/AvalancheBridgeAdapter.sol @@ -26,6 +26,7 @@ import "./mixins/MixinKyberElastic.sol"; import "./mixins/MixinAaveV2.sol"; import "./mixins/MixinNerve.sol"; import "./mixins/MixinPlatypus.sol"; +import "./mixins/MixinTraderJoeV2.sol"; import "./mixins/MixinUniswapV2.sol"; import "./mixins/MixinWOOFi.sol"; import "./mixins/MixinZeroExBridge.sol"; @@ -41,6 +42,7 @@ contract AvalancheBridgeAdapter is MixinAaveV2, MixinNerve, MixinPlatypus, + MixinTraderJoeV2, MixinUniswapV2, MixinWOOFi, MixinZeroExBridge @@ -100,6 +102,11 @@ contract AvalancheBridgeAdapter is return (0, true); } boughtAmount = _tradePlatypus(buyToken, sellAmount, order.bridgeData); + } else if (protocolId == BridgeProtocols.TRADERJOEV2) { + if (dryRun) { + return (0, true); + } + boughtAmount = _tradeTraderJoeV2(buyToken, sellAmount, order.bridgeData); } else if (protocolId == BridgeProtocols.WOOFI) { if (dryRun) { return (0, true); diff --git a/contracts/zero-ex/contracts/src/transformers/bridges/BridgeProtocols.sol b/contracts/zero-ex/contracts/src/transformers/bridges/BridgeProtocols.sol index 9b68b76f6c..85e5897219 100644 --- a/contracts/zero-ex/contracts/src/transformers/bridges/BridgeProtocols.sol +++ b/contracts/zero-ex/contracts/src/transformers/bridges/BridgeProtocols.sol @@ -56,4 +56,5 @@ library BridgeProtocols { uint128 internal constant AAVEV3 = 32; uint128 internal constant KYBERELASTIC = 33; uint128 internal constant BARTER = 34; + uint128 internal constant TRADERJOEV2 = 35; } diff --git a/contracts/zero-ex/contracts/src/transformers/bridges/mixins/MixinTraderJoeV2.sol b/contracts/zero-ex/contracts/src/transformers/bridges/mixins/MixinTraderJoeV2.sol new file mode 100644 index 0000000000..7183af0c23 --- /dev/null +++ b/contracts/zero-ex/contracts/src/transformers/bridges/mixins/MixinTraderJoeV2.sol @@ -0,0 +1,82 @@ +// SPDX-License-Identifier: Apache-2.0 +/* + Copyright 2023 ZeroEx Intl. + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +pragma solidity ^0.6.5; +pragma experimental ABIEncoderV2; + +import "@0x/contracts-erc20/src/v06/LibERC20TokenV06.sol"; +import "@0x/contracts-erc20/src/IERC20Token.sol"; +import "../IBridgeAdapter.sol"; + +interface ILBRouter { + /// @notice Swaps exact tokens for tokens while performing safety checks + /// @param amountIn The amount of token to send + /// @param amountOutMin The min amount of token to receive + /// @param pairBinSteps The bin step of the pairs (0: V1, other values will use V2) + /// @param tokenPath The swap path using the binSteps following `_pairBinSteps` + /// @param to The address of the recipient + /// @param deadline The deadline of the tx + /// @return amountOut Output amount of the swap + function swapExactTokensForTokens( + uint256 amountIn, + uint256 amountOutMin, + uint256[] memory pairBinSteps, + IERC20Token[] memory tokenPath, + address to, + uint256 deadline + ) external returns (uint256 amountOut); +} + +contract MixinTraderJoeV2 { + using LibERC20TokenV06 for IERC20Token; + + function _tradeTraderJoeV2( + IERC20Token buyToken, + uint256 sellAmount, + bytes memory bridgeData + ) internal returns (uint256 boughtAmount) { + ILBRouter router; + IERC20Token[] memory tokenPath; + uint256[] memory pairBinSteps; + { + address[] memory _path; + (router, _path, pairBinSteps) = abi.decode(bridgeData, (ILBRouter, address[], uint256[])); + // To get around `abi.decode()` not supporting interface array types. + assembly { + tokenPath := _path + } + } + + require(tokenPath.length >= 2, "MixinTraderJoeV2/PATH_LENGTH_MUST_BE_AT_LEAST_TWO"); + require( + tokenPath.length == pairBinSteps.length + 1, + "MixinTraderJoeV2/PAIR_BIN_STEPS_LENGTH_MUST_BE_ONE_LESS_THAN_TOKEN_PATH_LENGTH" + ); + require( + tokenPath[tokenPath.length - 1] == buyToken, + "MixinTraderJoeV2/LAST_ELEMENT_OF_PATH_MUST_MATCH_OUTPUT_TOKEN" + ); + // Grant the Trader Joe V2 router an allowance to sell the first token. + tokenPath[0].approveIfBelow(address(router), sellAmount); + + boughtAmount = router.swapExactTokensForTokens( + sellAmount, + 1, + pairBinSteps, + tokenPath, + address(this), + block.timestamp + ); + } +} diff --git a/contracts/zero-ex/tests/addresses/SourceAddresses.json b/contracts/zero-ex/tests/addresses/SourceAddresses.json index b88fd41a2b..704e1e91a9 100644 --- a/contracts/zero-ex/tests/addresses/SourceAddresses.json +++ b/contracts/zero-ex/tests/addresses/SourceAddresses.json @@ -4,48 +4,62 @@ "UniswapV3Router": "0x0000000000000000000000000000000000000000", "KyberElasticQuoter": "0x0d125c15d54ca1f8a813c74a81aee34ebb508c1f", "KyberElasticRouter": "0xc1e7dfe73e1598e3910ef4c7845b68a9ab6f4c83", - "KyberElasticPool": "0x952ffc4c47d66b454a8181f5c68b6248e18b66ec" + "KyberElasticPool": "0x952ffc4c47d66b454a8181f5c68b6248e18b66ec", + "TraderJoeV2Pool": "0x0000000000000000000000000000000000000000", + "TraderJoeV2Router": "0x0000000000000000000000000000000000000000" }, "56": { "UniswapV2Router": "0x10ed43c718714eb63d5aa57b78b54704e256024e", "UniswapV3Router": "0x0000000000000000000000000000000000000000", "KyberElasticQuoter": "0x0d125c15d54ca1f8a813c74a81aee34ebb508c1f", "KyberElasticRouter": "0xc1e7dfe73e1598e3910ef4c7845b68a9ab6f4c83", - "KyberElasticPool": "0xfbfab68ba077d099cd4b66fa76920572cc0b557c" + "KyberElasticPool": "0xfbfab68ba077d099cd4b66fa76920572cc0b557c", + "TraderJoeV2Pool": "0x0000000000000000000000000000000000000000", + "TraderJoeV2Router": "0x0000000000000000000000000000000000000000" }, "137": { "UniswapV2Router": "0x1b02da8cb0d097eb8d57a175b88c7d8b47997506", "UniswapV3Router": "0x0000000000000000000000000000000000000000", "KyberElasticQuoter": "0x0d125c15d54ca1f8a813c74a81aee34ebb508c1f", "KyberElasticRouter": "0xc1e7dfe73e1598e3910ef4c7845b68a9ab6f4c83", - "KyberElasticPool": "0xf9cc934753a127100585812181ac04d07158a4c2" + "KyberElasticPool": "0xf9cc934753a127100585812181ac04d07158a4c2", + "TraderJoeV2Pool": "0x0000000000000000000000000000000000000000", + "TraderJoeV2Router": "0x0000000000000000000000000000000000000000" }, "43114": { "UniswapV2Router": "0x9Ad6C38BE94206cA50bb0d90783181662f0Cfa10", "UniswapV3Router": "0x0000000000000000000000000000000000000000", "KyberElasticQuoter": "0x0d125c15d54ca1f8a813c74a81aee34ebb508c1f", "KyberElasticRouter": "0xc1e7dfe73e1598e3910ef4c7845b68a9ab6f4c83", - "KyberElasticPool": "0x6038373de7f64da99b2a31951628b7d778b2c3cf" + "KyberElasticPool": "0x6038373de7f64da99b2a31951628b7d778b2c3cf", + "TraderJoeV2Pool": "0x1D7A1a79e2b4Ef88D2323f3845246D24a3c20F1d", + "TraderJoeV2Router": "0xE3Ffc583dC176575eEA7FD9dF2A7c65F7E23f4C3" }, "250": { "UniswapV2Router": "0x1b02da8cb0d097eb8d57a175b88c7d8b47997506", "UniswapV3Router": "0x0000000000000000000000000000000000000000", "KyberElasticQuoter": "0x0d125c15d54ca1f8a813c74a81aee34ebb508c1f", "KyberElasticRouter": "0xc1e7dfe73e1598e3910ef4c7845b68a9ab6f4c83", - "KyberElasticPool": "0x8dcf5fed6ae6bf0befb5e4f0c9414c2cb9a4ed01" + "KyberElasticPool": "0x8dcf5fed6ae6bf0befb5e4f0c9414c2cb9a4ed01", + "TraderJoeV2Pool": "0x0000000000000000000000000000000000000000", + "TraderJoeV2Router": "0x0000000000000000000000000000000000000000" }, "10": { "UniswapV2Router": "0x0000000000000000000000000000000000000000", "UniswapV3Router": "0x61ffe014ba17989e743c5f6cb21bf9697530b21e", "KyberElasticQuoter": "0x0d125c15d54ca1f8a813c74a81aee34ebb508c1f", "KyberElasticRouter": "0xc1e7dfe73e1598e3910ef4c7845b68a9ab6f4c83", - "KyberElasticPool": "0x7e29ccaa4bf2894aca02c77e6b99cafc1d24b2f5" + "KyberElasticPool": "0x7e29ccaa4bf2894aca02c77e6b99cafc1d24b2f5", + "TraderJoeV2Pool": "0x0000000000000000000000000000000000000000", + "TraderJoeV2Router": "0x0000000000000000000000000000000000000000" }, "42161": { "UniswapV2Router": "0x1b02da8cb0d097eb8d57a175b88c7d8b47997506", "UniswapV3Router": "0x0000000000000000000000000000000000000000", "KyberElasticQuoter": "0x0d125c15d54ca1f8a813c74a81aee34ebb508c1f", "KyberElasticRouter": "0xc1e7dfe73e1598e3910ef4c7845b68a9ab6f4c83", - "KyberElasticPool": "0x087abaab9cd85025a8b3916948c69fe173c837ea" + "KyberElasticPool": "0x087abaab9cd85025a8b3916948c69fe173c837ea", + "TraderJoeV2Pool": "0x0000000000000000000000000000000000000000", + "TraderJoeV2Router": "0x0000000000000000000000000000000000000000" } } diff --git a/contracts/zero-ex/tests/addresses/TokenAddresses.json b/contracts/zero-ex/tests/addresses/TokenAddresses.json index 0a07fe3eaf..4423a3500c 100644 --- a/contracts/zero-ex/tests/addresses/TokenAddresses.json +++ b/contracts/zero-ex/tests/addresses/TokenAddresses.json @@ -20,8 +20,8 @@ "43114": { "WrappedNativeToken": "0xb31f66aa3c1e785363f0875a1b74e27b85fd66c7", "DAI": "0xd586e7f844cea2f87f50152665bcbc2c279d8d70", - "USDC": "0xa7d7079b0fead91f3e65f86e8915cb59c1a4c664", - "USDT": "0xc7198437980c041c805A1EDcbA50c1Ce5db95118" + "USDC": "0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E", + "USDT": "0x9702230A8Ea53601f5cD2dc00fDBc13d4dF4A8c7" }, "250": { "WrappedNativeToken": "0x21be370d5312f44cb42ce377bc9b8a0cef1a4c83", diff --git a/contracts/zero-ex/tests/forked/SwapERC20ForERC20Test.t.sol b/contracts/zero-ex/tests/forked/SwapERC20ForERC20Test.t.sol index 1b81a1c4ab..abd24bc90c 100644 --- a/contracts/zero-ex/tests/forked/SwapERC20ForERC20Test.t.sol +++ b/contracts/zero-ex/tests/forked/SwapERC20ForERC20Test.t.sol @@ -54,6 +54,75 @@ contract SwapERC20ForERC20Test is Test, ForkUtils, TestUtils { } } + function test_swapERC20ForERC20OnTraderJoeV2() public { + for (uint256 i = 0; i < chains.length; i++) { + // TraderJoeV2 mixin only enabled on Avalanche + if (i != 3) { + continue; + } + vm.selectFork(forkIds[chains[i]]); + labelAddresses( + chains[i], + indexChainsByChain[chains[i]], + getTokens(i), + getContractAddresses(i), + getLiquiditySourceAddresses(i) + ); + swapOnTraderJoeV2(getTokens(i), getContractAddresses(i), getLiquiditySourceAddresses(i)); + } + } + + function swapOnTraderJoeV2( + TokenAddresses memory tokens, + ContractAddresses memory addresses, + LiquiditySources memory sources + ) public onlyForked { + if (sources.TraderJoeV2Router == address(0)) { + emit log_string("TraderJoeV2Router not available on this chain"); + return; + } + if (sources.TraderJoeV2Pool == address(0)) { + emit log_string("TraderJoeV2Pool not available on this chain"); + return; + } + + FillQuoteTransformer.TransformData memory fqtData; + fqtData.side = FillQuoteTransformer.Side.Sell; + fqtData.sellToken = IERC20Token(address(tokens.USDC)); + fqtData.buyToken = IERC20Token(address(tokens.USDT)); + fqtData.fillSequence = new FillQuoteTransformer.OrderType[](1); + fqtData.fillSequence[0] = FillQuoteTransformer.OrderType.Bridge; + fqtData.fillAmount = 1e6; + + (uint256 amountOut, uint256 binStep) = sampleTraderJoeV2( + fqtData.fillAmount, + address(fqtData.sellToken), + address(fqtData.buyToken), + sources.TraderJoeV2Router, + sources.TraderJoeV2Pool + ); + log_named_uint("amountOut", amountOut); + + IBridgeAdapter.BridgeOrder memory order; + { + address[] memory tokenPath = new address[](2); + tokenPath[0] = address(fqtData.sellToken); + tokenPath[1] = address(fqtData.buyToken); + uint256[] memory binSteps = new uint256[](1); + binSteps[0] = binStep; + order.bridgeData = abi.encode(address(sources.TraderJoeV2Router), tokenPath, binSteps); + } + + order.source = bytes32(uint256(BridgeProtocols.TRADERJOEV2) << 128); + order.takerTokenAmount = fqtData.fillAmount; + order.makerTokenAmount = amountOut; + + fqtData.bridgeOrders = new IBridgeAdapter.BridgeOrder[](1); + fqtData.bridgeOrders[0] = order; + + settleAndLogBalances(fqtData, tokens, addresses); + } + function swapOnKyberElastic( TokenAddresses memory tokens, ContractAddresses memory addresses, @@ -71,20 +140,6 @@ contract SwapERC20ForERC20Test is Test, ForkUtils, TestUtils { emit log_string("KyberElasticPool not available on this chain"); return; } - ITransformERC20Feature.Transformation[] memory transformations = new ITransformERC20Feature.Transformation[](2); - - transformations[0].deploymentNonce = _findTransformerNonce( - address(addresses.transformers.wethTransformer), - address(addresses.exchangeProxyTransformerDeployer) - ); - emit log_named_uint("WethTransformer nonce", transformations[0].deploymentNonce); - createNewFQT(tokens.WrappedNativeToken, addresses.exchangeProxy, addresses.exchangeProxyTransformerDeployer); - transformations[0].data = abi.encode(LibERC20Transformer.ETH_TOKEN_ADDRESS, 1e18); - transformations[1].deploymentNonce = _findTransformerNonce( - address(fillQuoteTransformer), - address(addresses.exchangeProxyTransformerDeployer) - ); - emit log_named_uint("FillQuoteTransformer nonce", transformations[1].deploymentNonce); FillQuoteTransformer.TransformData memory fqtData; fqtData.side = FillQuoteTransformer.Side.Sell; @@ -111,37 +166,8 @@ contract SwapERC20ForERC20Test is Test, ForkUtils, TestUtils { order.makerTokenAmount = amountOut; order.bridgeData = abi.encode(address(sources.KyberElasticRouter), path); fqtData.bridgeOrders[0] = order; - transformations[1].data = abi.encode(fqtData); - vm.deal(address(this), 1e18); - uint256 balanceETHBefore = address(this).balance; - uint256 balanceERC20Before = IERC20Token(tokens.USDT).balanceOf(address(this)); - - writeTokenBalance(address(this), address(tokens.USDC), 1e16); - uint256 balanceUSDCbefore = IERC20Token(tokens.USDC).balanceOf(address(this)); - - IERC20Token(address(tokens.USDC)).approve(addresses.exchangeProxy, 1e16); - - IZeroEx(payable(addresses.exchangeProxy)).transformERC20{value: 1e18}( - // input token - IERC20Token(address(tokens.USDC)), - // output token - IERC20Token(address(tokens.USDT)), - // input token amount - 1e6, - // min output token amount - order.makerTokenAmount, - // list of transform - transformations - ); - - log_named_uint("NativeAsset balance before", balanceETHBefore); - log_named_uint("ERC-20 balance before", balanceERC20Before); - log_named_uint("NativeAsset balance after", balanceETHBefore - address(this).balance); - log_named_uint("ERC-20 balance after", IERC20Token(tokens.USDT).balanceOf(address(this)) - balanceERC20Before); - log_named_uint("USDC balance before", balanceUSDCbefore); - log_named_uint("USDC balance after", IERC20Token(tokens.USDT).balanceOf(address(tokens.USDC))); - assert(IERC20Token(tokens.USDT).balanceOf(address(this)) > 0); + settleAndLogBalances(fqtData, tokens, addresses); } function sampleKyberElastic( @@ -150,7 +176,7 @@ contract SwapERC20ForERC20Test is Test, ForkUtils, TestUtils { address makerToken, address quoter, address pool - ) public returns (uint256 makerTokenAmount, bytes memory path) { + ) private returns (uint256 makerTokenAmount, bytes memory path) { log_string(" Sampling KyberElastic for tokens"); log_named_address(" ", takerToken); log_string(" -> "); @@ -167,4 +193,64 @@ contract SwapERC20ForERC20Test is Test, ForkUtils, TestUtils { (uint256 amountOut, , , ) = kyberQuoter.quoteExactInput(path, amount); return (amountOut, path); } + + function sampleTraderJoeV2( + uint256 amount, + address takerToken, + address makerToken, + address router, + address pool + ) private returns (uint256 makerTokenAmount, uint256 binStep) { + log_string("Sampling TraderJoeV2"); + log_named_address("takerToken", takerToken); + log_named_address("makerToken", makerToken); + log_named_address("router", router); + log_named_address("pool", pool); + + bool swapForY = ITraderJoeV2Pool(pool).tokenY() == makerToken; + + (makerTokenAmount, ) = ITraderJoeV2Router(router).getSwapOut(pool, amount, swapForY); + + binStep = ITraderJoeV2Pool(pool).feeParameters().binStep; + } + + function deployFQTAndGetDeploymentNonce( + TokenAddresses memory tokens, + ContractAddresses memory addresses + ) private returns (uint32) { + createNewFQT(tokens.WrappedNativeToken, addresses.exchangeProxy, addresses.exchangeProxyTransformerDeployer); + return + _findTransformerNonce(address(fillQuoteTransformer), address(addresses.exchangeProxyTransformerDeployer)); + } + + function settleAndLogBalances( + FillQuoteTransformer.TransformData memory fqtData, + TokenAddresses memory tokens, + ContractAddresses memory addresses + ) private { + ITransformERC20Feature.Transformation[] memory transformations = new ITransformERC20Feature.Transformation[](1); + transformations[0].deploymentNonce = deployFQTAndGetDeploymentNonce(tokens, addresses); + transformations[0].data = abi.encode(fqtData); + + address sellToken = address(fqtData.sellToken); + address buyToken = address(fqtData.buyToken); + + writeTokenBalance(address(this), sellToken, 1e16); + uint256 sellTokenBalanceBefore = IERC20Token(sellToken).balanceOf(address(this)); + uint256 buyTokenBalanceBefore = IERC20Token(buyToken).balanceOf(address(this)); + + IERC20Token(sellToken).approve(addresses.exchangeProxy, 1e16); + IZeroEx(payable(addresses.exchangeProxy)).transformERC20( + IERC20Token(sellToken), + IERC20Token(buyToken), + fqtData.fillAmount, + fqtData.bridgeOrders[0].makerTokenAmount, + transformations + ); + + log_named_uint("sellToken balance before", sellTokenBalanceBefore); + log_named_uint("sellToken balance after", IERC20Token(sellToken).balanceOf(address(this))); + log_named_uint("buyToken balance before", buyTokenBalanceBefore); + log_named_uint("buyToken balance after", IERC20Token(buyToken).balanceOf(address(this))); + } } diff --git a/contracts/zero-ex/tests/utils/ForkUtils.sol b/contracts/zero-ex/tests/utils/ForkUtils.sol index 54164367cd..4eae1057ce 100644 --- a/contracts/zero-ex/tests/utils/ForkUtils.sol +++ b/contracts/zero-ex/tests/utils/ForkUtils.sol @@ -88,10 +88,13 @@ struct TokenAddresses { IEtherToken WrappedNativeToken; } +// keep the names of the struct members in alphabetical order for correct json unparsing struct LiquiditySources { address KyberElasticPool; address KyberElasticQuoter; address KyberElasticRouter; + address TraderJoeV2Pool; + address TraderJoeV2Router; address UniswapV2Router; address UniswapV3Router; } @@ -100,6 +103,37 @@ interface IFQT { function bridgeAdapter() external returns (address); } +interface ITraderJoeV2Pool { + struct FeeParameters { + // 144 lowest bits in slot + uint16 binStep; + uint16 baseFactor; + uint16 filterPeriod; + uint16 decayPeriod; + uint16 reductionFactor; + uint24 variableFeeControl; + uint16 protocolShare; + uint24 maxVolatilityAccumulated; + // 112 highest bits in slot + uint24 volatilityAccumulated; + uint24 volatilityReference; + uint24 indexRef; + uint40 time; + } + + function feeParameters() external view returns (FeeParameters memory); + + function tokenY() external view returns (address); +} + +interface ITraderJoeV2Router { + function getSwapOut( + address pool, + uint256 amountIn, + bool swapForY + ) external view returns (uint256 amountOut, uint256 feesIn); +} + interface IKyberElasticQuoter { function quoteExactInput( bytes memory path, diff --git a/packages/contract-addresses/CHANGELOG.json b/packages/contract-addresses/CHANGELOG.json index 0d61574592..14254a1cc4 100644 --- a/packages/contract-addresses/CHANGELOG.json +++ b/packages/contract-addresses/CHANGELOG.json @@ -1,4 +1,13 @@ [ + { + "version": "8.5.0", + "changes": [ + { + "note": "Add Trader Joe V2 support on Avalanche" + } + ], + "timestamp": 1681960738 + }, { "version": "8.4.0", "changes": [ diff --git a/packages/contract-addresses/addresses.json b/packages/contract-addresses/addresses.json index 76df547af7..25c7938ccf 100644 --- a/packages/contract-addresses/addresses.json +++ b/packages/contract-addresses/addresses.json @@ -156,7 +156,7 @@ "wethTransformer": "0x9b8b52391071d71cd4ad1e61d7f273268fa34c6c", "payTakerTransformer": "0xb9a4c32547bc3cdc2ee2fb13cc1a0717dac9888f", "affiliateFeeTransformer": "0x105679f99d668001370b4621ad8648ac570c860f", - "fillQuoteTransformer": "0x7991f2c35ab19472dbfb6f27593f7f6f38fb3eab", + "fillQuoteTransformer": "0x540079df6023d39b2686fd9f6c06f1f8f66aca4a", "positiveSlippageFeeTransformer": "0xadbfdc58a24b6dbc16f21541800f43dd6e282250" } }, diff --git a/packages/protocol-utils/CHANGELOG.json b/packages/protocol-utils/CHANGELOG.json index 5e652a6aae..908f25f8e0 100644 --- a/packages/protocol-utils/CHANGELOG.json +++ b/packages/protocol-utils/CHANGELOG.json @@ -1,4 +1,13 @@ [ + { + "version": "11.21.0", + "changes": [ + { + "note": "Add Trader Joe V2 support on Avalanche" + } + ], + "timestamp": 1681960738 + }, { "version": "11.20.0", "changes": [ diff --git a/packages/protocol-utils/src/transformer_utils.ts b/packages/protocol-utils/src/transformer_utils.ts index dc3cee9d89..ec875941da 100644 --- a/packages/protocol-utils/src/transformer_utils.ts +++ b/packages/protocol-utils/src/transformer_utils.ts @@ -165,6 +165,7 @@ export enum BridgeProtocol { AaveV3, KyberElastic, Barter, + TraderJoeV2, } /**