diff --git a/contracts/zero-ex/CHANGELOG.json b/contracts/zero-ex/CHANGELOG.json index 16612d6d71..2521ac9ea4 100644 --- a/contracts/zero-ex/CHANGELOG.json +++ b/contracts/zero-ex/CHANGELOG.json @@ -1,4 +1,12 @@ [ + { + "version": "0.43.0", + "changes": [ + { + "note": "Add Trader Joe V2.1 Router Support for MixIn" + } + ] + }, { "timestamp": 1682334742, "version": "0.42.1", diff --git a/contracts/zero-ex/contracts/src/transformers/bridges/mixins/MixinTraderJoeV2.sol b/contracts/zero-ex/contracts/src/transformers/bridges/mixins/MixinTraderJoeV2.sol index 7183af0c23..613ccbf93a 100644 --- a/contracts/zero-ex/contracts/src/transformers/bridges/mixins/MixinTraderJoeV2.sol +++ b/contracts/zero-ex/contracts/src/transformers/bridges/mixins/MixinTraderJoeV2.sol @@ -20,19 +20,43 @@ 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 + /** + * @dev This enum represents the version of the pair requested + * - V1: Joe V1 pair + * - V2: LB pair V2. Also called legacyPair + * - V2_1: LB pair V2.1 (current version) + */ + enum Version { + V1, + V2, + V2_1 + } + + /** + * @dev The path parameters, such as: + * - pairBinSteps: The list of bin steps of the pairs to go through + * - versions: The list of versions of the pairs to go through + * - tokenPath: The list of tokens in the path to go through + */ + struct Path { + uint256[] pairBinSteps; + Version[] versions; + IERC20Token[] tokenPath; + } + + /** + * @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 path The path of the swap + * @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, + Path memory path, address to, uint256 deadline ) external returns (uint256 amountOut); @@ -49,12 +73,16 @@ contract MixinTraderJoeV2 { ILBRouter router; IERC20Token[] memory tokenPath; uint256[] memory pairBinSteps; + ILBRouter.Version[] memory versions; { - address[] memory _path; - (router, _path, pairBinSteps) = abi.decode(bridgeData, (ILBRouter, address[], uint256[])); + address[] memory _tokenPath; + (router, _tokenPath, pairBinSteps, versions) = abi.decode( + bridgeData, + (ILBRouter, address[], uint256[], ILBRouter.Version[]) + ); // To get around `abi.decode()` not supporting interface array types. assembly { - tokenPath := _path + tokenPath := _tokenPath } } @@ -63,6 +91,10 @@ contract MixinTraderJoeV2 { tokenPath.length == pairBinSteps.length + 1, "MixinTraderJoeV2/PAIR_BIN_STEPS_LENGTH_MUST_BE_ONE_LESS_THAN_TOKEN_PATH_LENGTH" ); + require( + versions.length == pairBinSteps.length, + "MixinTraderJoeV2/VERSIONS_LENGTH_MUST_BE_EQUAL_TO_PAIR_BIN_STEPS_LENGTH" + ); require( tokenPath[tokenPath.length - 1] == buyToken, "MixinTraderJoeV2/LAST_ELEMENT_OF_PATH_MUST_MATCH_OUTPUT_TOKEN" @@ -70,13 +102,11 @@ contract MixinTraderJoeV2 { // 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 - ); + ILBRouter.Path memory path = ILBRouter.Path({ + pairBinSteps: pairBinSteps, + versions: versions, + tokenPath: tokenPath + }); + boughtAmount = router.swapExactTokensForTokens(sellAmount, 1, path, address(this), block.timestamp); } } diff --git a/contracts/zero-ex/tests/addresses/SourceAddresses.json b/contracts/zero-ex/tests/addresses/SourceAddresses.json index 704e1e91a9..db85844b69 100644 --- a/contracts/zero-ex/tests/addresses/SourceAddresses.json +++ b/contracts/zero-ex/tests/addresses/SourceAddresses.json @@ -5,7 +5,7 @@ "KyberElasticQuoter": "0x0d125c15d54ca1f8a813c74a81aee34ebb508c1f", "KyberElasticRouter": "0xc1e7dfe73e1598e3910ef4c7845b68a9ab6f4c83", "KyberElasticPool": "0x952ffc4c47d66b454a8181f5c68b6248e18b66ec", - "TraderJoeV2Pool": "0x0000000000000000000000000000000000000000", + "TraderJoeV2Quoter": "0x0000000000000000000000000000000000000000", "TraderJoeV2Router": "0x0000000000000000000000000000000000000000" }, "56": { @@ -14,7 +14,7 @@ "KyberElasticQuoter": "0x0d125c15d54ca1f8a813c74a81aee34ebb508c1f", "KyberElasticRouter": "0xc1e7dfe73e1598e3910ef4c7845b68a9ab6f4c83", "KyberElasticPool": "0xfbfab68ba077d099cd4b66fa76920572cc0b557c", - "TraderJoeV2Pool": "0x0000000000000000000000000000000000000000", + "TraderJoeV2Quoter": "0x0000000000000000000000000000000000000000", "TraderJoeV2Router": "0x0000000000000000000000000000000000000000" }, "137": { @@ -23,7 +23,7 @@ "KyberElasticQuoter": "0x0d125c15d54ca1f8a813c74a81aee34ebb508c1f", "KyberElasticRouter": "0xc1e7dfe73e1598e3910ef4c7845b68a9ab6f4c83", "KyberElasticPool": "0xf9cc934753a127100585812181ac04d07158a4c2", - "TraderJoeV2Pool": "0x0000000000000000000000000000000000000000", + "TraderJoeV2Quoter": "0x0000000000000000000000000000000000000000", "TraderJoeV2Router": "0x0000000000000000000000000000000000000000" }, "43114": { @@ -32,8 +32,8 @@ "KyberElasticQuoter": "0x0d125c15d54ca1f8a813c74a81aee34ebb508c1f", "KyberElasticRouter": "0xc1e7dfe73e1598e3910ef4c7845b68a9ab6f4c83", "KyberElasticPool": "0x6038373de7f64da99b2a31951628b7d778b2c3cf", - "TraderJoeV2Pool": "0x1D7A1a79e2b4Ef88D2323f3845246D24a3c20F1d", - "TraderJoeV2Router": "0xE3Ffc583dC176575eEA7FD9dF2A7c65F7E23f4C3" + "TraderJoeV2Quoter": "0x3660268Ed43583a2cdd09e3fC7079ff07DBD4Caa", + "TraderJoeV2Router": "0xb4315e873dBcf96Ffd0acd8EA43f689D8c20fB30" }, "250": { "UniswapV2Router": "0x1b02da8cb0d097eb8d57a175b88c7d8b47997506", @@ -41,7 +41,7 @@ "KyberElasticQuoter": "0x0d125c15d54ca1f8a813c74a81aee34ebb508c1f", "KyberElasticRouter": "0xc1e7dfe73e1598e3910ef4c7845b68a9ab6f4c83", "KyberElasticPool": "0x8dcf5fed6ae6bf0befb5e4f0c9414c2cb9a4ed01", - "TraderJoeV2Pool": "0x0000000000000000000000000000000000000000", + "TraderJoeV2Quoter": "0x0000000000000000000000000000000000000000", "TraderJoeV2Router": "0x0000000000000000000000000000000000000000" }, "10": { @@ -50,7 +50,7 @@ "KyberElasticQuoter": "0x0d125c15d54ca1f8a813c74a81aee34ebb508c1f", "KyberElasticRouter": "0xc1e7dfe73e1598e3910ef4c7845b68a9ab6f4c83", "KyberElasticPool": "0x7e29ccaa4bf2894aca02c77e6b99cafc1d24b2f5", - "TraderJoeV2Pool": "0x0000000000000000000000000000000000000000", + "TraderJoeV2Quoter": "0x0000000000000000000000000000000000000000", "TraderJoeV2Router": "0x0000000000000000000000000000000000000000" }, "42161": { @@ -59,7 +59,7 @@ "KyberElasticQuoter": "0x0d125c15d54ca1f8a813c74a81aee34ebb508c1f", "KyberElasticRouter": "0xc1e7dfe73e1598e3910ef4c7845b68a9ab6f4c83", "KyberElasticPool": "0x087abaab9cd85025a8b3916948c69fe173c837ea", - "TraderJoeV2Pool": "0x0000000000000000000000000000000000000000", + "TraderJoeV2Quoter": "0x0000000000000000000000000000000000000000", "TraderJoeV2Router": "0x0000000000000000000000000000000000000000" } } diff --git a/contracts/zero-ex/tests/forked/SwapERC20ForERC20Test.t.sol b/contracts/zero-ex/tests/forked/SwapERC20ForERC20Test.t.sol index abd24bc90c..4d32c0a877 100644 --- a/contracts/zero-ex/tests/forked/SwapERC20ForERC20Test.t.sol +++ b/contracts/zero-ex/tests/forked/SwapERC20ForERC20Test.t.sol @@ -81,8 +81,8 @@ contract SwapERC20ForERC20Test is Test, ForkUtils, TestUtils { emit log_string("TraderJoeV2Router not available on this chain"); return; } - if (sources.TraderJoeV2Pool == address(0)) { - emit log_string("TraderJoeV2Pool not available on this chain"); + if (sources.TraderJoeV2Quoter == address(0)) { + emit log_string("TraderJoeV2Quoter not available on this chain"); return; } @@ -94,12 +94,11 @@ contract SwapERC20ForERC20Test is Test, ForkUtils, TestUtils { fqtData.fillSequence[0] = FillQuoteTransformer.OrderType.Bridge; fqtData.fillAmount = 1e6; - (uint256 amountOut, uint256 binStep) = sampleTraderJoeV2( + (uint256 amountOut, uint256 binStep, uint256 version) = sampleTraderJoeV2( fqtData.fillAmount, address(fqtData.sellToken), address(fqtData.buyToken), - sources.TraderJoeV2Router, - sources.TraderJoeV2Pool + sources.TraderJoeV2Quoter ); log_named_uint("amountOut", amountOut); @@ -110,7 +109,9 @@ contract SwapERC20ForERC20Test is Test, ForkUtils, TestUtils { tokenPath[1] = address(fqtData.buyToken); uint256[] memory binSteps = new uint256[](1); binSteps[0] = binStep; - order.bridgeData = abi.encode(address(sources.TraderJoeV2Router), tokenPath, binSteps); + uint256[] memory versions = new uint256[](1); + versions[0] = version; + order.bridgeData = abi.encode(address(sources.TraderJoeV2Router), tokenPath, binSteps, versions); } order.source = bytes32(uint256(BridgeProtocols.TRADERJOEV2) << 128); @@ -198,20 +199,23 @@ contract SwapERC20ForERC20Test is Test, ForkUtils, TestUtils { uint256 amount, address takerToken, address makerToken, - address router, - address pool - ) private returns (uint256 makerTokenAmount, uint256 binStep) { + address quoter + ) private returns (uint256 makerTokenAmount, uint256 binStep, uint256 version) { log_string("Sampling TraderJoeV2"); log_named_address("takerToken", takerToken); log_named_address("makerToken", makerToken); - log_named_address("router", router); - log_named_address("pool", pool); + log_named_address("quoter", quoter); - bool swapForY = ITraderJoeV2Pool(pool).tokenY() == makerToken; + address[] memory tokenPath = new address[](2); + tokenPath[0] = takerToken; + tokenPath[1] = makerToken; - (makerTokenAmount, ) = ITraderJoeV2Router(router).getSwapOut(pool, amount, swapForY); + ITraderJoeV2Quoter.Quote memory quote = ITraderJoeV2Quoter(quoter).findBestPathFromAmountIn( + tokenPath, + uint128(amount) + ); - binStep = ITraderJoeV2Pool(pool).feeParameters().binStep; + return (quote.amounts[1], quote.binSteps[0], uint256(quote.versions[0])); } function deployFQTAndGetDeploymentNonce( diff --git a/contracts/zero-ex/tests/utils/ForkUtils.sol b/contracts/zero-ex/tests/utils/ForkUtils.sol index 4eae1057ce..aeab0fce3d 100644 --- a/contracts/zero-ex/tests/utils/ForkUtils.sol +++ b/contracts/zero-ex/tests/utils/ForkUtils.sol @@ -93,7 +93,7 @@ struct LiquiditySources { address KyberElasticPool; address KyberElasticQuoter; address KyberElasticRouter; - address TraderJoeV2Pool; + address TraderJoeV2Quoter; address TraderJoeV2Router; address UniswapV2Router; address UniswapV3Router; @@ -103,35 +103,27 @@ 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; +interface ITraderJoeV2Quoter { + enum Version { + V1, + V2, + V2_1 } - function feeParameters() external view returns (FeeParameters memory); + struct Quote { + address[] route; + address[] pairs; + uint256[] binSteps; + Version[] versions; + uint128[] amounts; + uint128[] virtualAmountsWithoutSlippage; + uint128[] fees; + } - function tokenY() external view returns (address); -} - -interface ITraderJoeV2Router { - function getSwapOut( - address pool, - uint256 amountIn, - bool swapForY - ) external view returns (uint256 amountOut, uint256 feesIn); + function findBestPathFromAmountIn( + address[] calldata route, + uint128 amountIn + ) external view returns (Quote memory quote); } interface IKyberElasticQuoter { diff --git a/packages/contract-addresses/CHANGELOG.json b/packages/contract-addresses/CHANGELOG.json index ab5c3c845c..101d45123c 100644 --- a/packages/contract-addresses/CHANGELOG.json +++ b/packages/contract-addresses/CHANGELOG.json @@ -1,4 +1,12 @@ [ + { + "version": "8.6.0", + "changes": [ + { + "note": "Add Trader Joe V2.1 Router Support for MixIn" + } + ] + }, { "version": "8.5.0", "changes": [ diff --git a/packages/contract-addresses/addresses.json b/packages/contract-addresses/addresses.json index 25c7938ccf..64e4d05223 100644 --- a/packages/contract-addresses/addresses.json +++ b/packages/contract-addresses/addresses.json @@ -156,7 +156,7 @@ "wethTransformer": "0x9b8b52391071d71cd4ad1e61d7f273268fa34c6c", "payTakerTransformer": "0xb9a4c32547bc3cdc2ee2fb13cc1a0717dac9888f", "affiliateFeeTransformer": "0x105679f99d668001370b4621ad8648ac570c860f", - "fillQuoteTransformer": "0x540079df6023d39b2686fd9f6c06f1f8f66aca4a", + "fillQuoteTransformer": "0x886e4f97d7e06ab66dba574a7a861046dcf7ae4f", "positiveSlippageFeeTransformer": "0xadbfdc58a24b6dbc16f21541800f43dd6e282250" } },