Skip to content

Commit 394ccbd

Browse files
authored
feat: Update Trader Joe V2 MixIn to support V2.1 router (#717)
* Add Trader Joe V2.1 Router MixIn * Update changelog and FQT address * lowercasing Avalanche FQT
1 parent 5a47f04 commit 394ccbd

7 files changed

Lines changed: 113 additions & 71 deletions

File tree

contracts/zero-ex/CHANGELOG.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,12 @@
11
[
2+
{
3+
"version": "0.43.0",
4+
"changes": [
5+
{
6+
"note": "Add Trader Joe V2.1 Router Support for MixIn"
7+
}
8+
]
9+
},
210
{
311
"timestamp": 1682334742,
412
"version": "0.42.1",

contracts/zero-ex/contracts/src/transformers/bridges/mixins/MixinTraderJoeV2.sol

Lines changed: 51 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -20,19 +20,43 @@ import "@0x/contracts-erc20/src/IERC20Token.sol";
2020
import "../IBridgeAdapter.sol";
2121

2222
interface ILBRouter {
23-
/// @notice Swaps exact tokens for tokens while performing safety checks
24-
/// @param amountIn The amount of token to send
25-
/// @param amountOutMin The min amount of token to receive
26-
/// @param pairBinSteps The bin step of the pairs (0: V1, other values will use V2)
27-
/// @param tokenPath The swap path using the binSteps following `_pairBinSteps`
28-
/// @param to The address of the recipient
29-
/// @param deadline The deadline of the tx
30-
/// @return amountOut Output amount of the swap
23+
/**
24+
* @dev This enum represents the version of the pair requested
25+
* - V1: Joe V1 pair
26+
* - V2: LB pair V2. Also called legacyPair
27+
* - V2_1: LB pair V2.1 (current version)
28+
*/
29+
enum Version {
30+
V1,
31+
V2,
32+
V2_1
33+
}
34+
35+
/**
36+
* @dev The path parameters, such as:
37+
* - pairBinSteps: The list of bin steps of the pairs to go through
38+
* - versions: The list of versions of the pairs to go through
39+
* - tokenPath: The list of tokens in the path to go through
40+
*/
41+
struct Path {
42+
uint256[] pairBinSteps;
43+
Version[] versions;
44+
IERC20Token[] tokenPath;
45+
}
46+
47+
/**
48+
* @notice Swaps exact tokens for tokens while performing safety checks
49+
* @param amountIn The amount of token to send
50+
* @param amountOutMin The min amount of token to receive
51+
* @param path The path of the swap
52+
* @param to The address of the recipient
53+
* @param deadline The deadline of the tx
54+
* @return amountOut Output amount of the swap
55+
*/
3156
function swapExactTokensForTokens(
3257
uint256 amountIn,
3358
uint256 amountOutMin,
34-
uint256[] memory pairBinSteps,
35-
IERC20Token[] memory tokenPath,
59+
Path memory path,
3660
address to,
3761
uint256 deadline
3862
) external returns (uint256 amountOut);
@@ -49,12 +73,16 @@ contract MixinTraderJoeV2 {
4973
ILBRouter router;
5074
IERC20Token[] memory tokenPath;
5175
uint256[] memory pairBinSteps;
76+
ILBRouter.Version[] memory versions;
5277
{
53-
address[] memory _path;
54-
(router, _path, pairBinSteps) = abi.decode(bridgeData, (ILBRouter, address[], uint256[]));
78+
address[] memory _tokenPath;
79+
(router, _tokenPath, pairBinSteps, versions) = abi.decode(
80+
bridgeData,
81+
(ILBRouter, address[], uint256[], ILBRouter.Version[])
82+
);
5583
// To get around `abi.decode()` not supporting interface array types.
5684
assembly {
57-
tokenPath := _path
85+
tokenPath := _tokenPath
5886
}
5987
}
6088

@@ -63,20 +91,22 @@ contract MixinTraderJoeV2 {
6391
tokenPath.length == pairBinSteps.length + 1,
6492
"MixinTraderJoeV2/PAIR_BIN_STEPS_LENGTH_MUST_BE_ONE_LESS_THAN_TOKEN_PATH_LENGTH"
6593
);
94+
require(
95+
versions.length == pairBinSteps.length,
96+
"MixinTraderJoeV2/VERSIONS_LENGTH_MUST_BE_EQUAL_TO_PAIR_BIN_STEPS_LENGTH"
97+
);
6698
require(
6799
tokenPath[tokenPath.length - 1] == buyToken,
68100
"MixinTraderJoeV2/LAST_ELEMENT_OF_PATH_MUST_MATCH_OUTPUT_TOKEN"
69101
);
70102
// Grant the Trader Joe V2 router an allowance to sell the first token.
71103
tokenPath[0].approveIfBelow(address(router), sellAmount);
72104

73-
boughtAmount = router.swapExactTokensForTokens(
74-
sellAmount,
75-
1,
76-
pairBinSteps,
77-
tokenPath,
78-
address(this),
79-
block.timestamp
80-
);
105+
ILBRouter.Path memory path = ILBRouter.Path({
106+
pairBinSteps: pairBinSteps,
107+
versions: versions,
108+
tokenPath: tokenPath
109+
});
110+
boughtAmount = router.swapExactTokensForTokens(sellAmount, 1, path, address(this), block.timestamp);
81111
}
82112
}

contracts/zero-ex/tests/addresses/SourceAddresses.json

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
"KyberElasticQuoter": "0x0d125c15d54ca1f8a813c74a81aee34ebb508c1f",
66
"KyberElasticRouter": "0xc1e7dfe73e1598e3910ef4c7845b68a9ab6f4c83",
77
"KyberElasticPool": "0x952ffc4c47d66b454a8181f5c68b6248e18b66ec",
8-
"TraderJoeV2Pool": "0x0000000000000000000000000000000000000000",
8+
"TraderJoeV2Quoter": "0x0000000000000000000000000000000000000000",
99
"TraderJoeV2Router": "0x0000000000000000000000000000000000000000"
1010
},
1111
"56": {
@@ -14,7 +14,7 @@
1414
"KyberElasticQuoter": "0x0d125c15d54ca1f8a813c74a81aee34ebb508c1f",
1515
"KyberElasticRouter": "0xc1e7dfe73e1598e3910ef4c7845b68a9ab6f4c83",
1616
"KyberElasticPool": "0xfbfab68ba077d099cd4b66fa76920572cc0b557c",
17-
"TraderJoeV2Pool": "0x0000000000000000000000000000000000000000",
17+
"TraderJoeV2Quoter": "0x0000000000000000000000000000000000000000",
1818
"TraderJoeV2Router": "0x0000000000000000000000000000000000000000"
1919
},
2020
"137": {
@@ -23,7 +23,7 @@
2323
"KyberElasticQuoter": "0x0d125c15d54ca1f8a813c74a81aee34ebb508c1f",
2424
"KyberElasticRouter": "0xc1e7dfe73e1598e3910ef4c7845b68a9ab6f4c83",
2525
"KyberElasticPool": "0xf9cc934753a127100585812181ac04d07158a4c2",
26-
"TraderJoeV2Pool": "0x0000000000000000000000000000000000000000",
26+
"TraderJoeV2Quoter": "0x0000000000000000000000000000000000000000",
2727
"TraderJoeV2Router": "0x0000000000000000000000000000000000000000"
2828
},
2929
"43114": {
@@ -32,16 +32,16 @@
3232
"KyberElasticQuoter": "0x0d125c15d54ca1f8a813c74a81aee34ebb508c1f",
3333
"KyberElasticRouter": "0xc1e7dfe73e1598e3910ef4c7845b68a9ab6f4c83",
3434
"KyberElasticPool": "0x6038373de7f64da99b2a31951628b7d778b2c3cf",
35-
"TraderJoeV2Pool": "0x1D7A1a79e2b4Ef88D2323f3845246D24a3c20F1d",
36-
"TraderJoeV2Router": "0xE3Ffc583dC176575eEA7FD9dF2A7c65F7E23f4C3"
35+
"TraderJoeV2Quoter": "0x3660268Ed43583a2cdd09e3fC7079ff07DBD4Caa",
36+
"TraderJoeV2Router": "0xb4315e873dBcf96Ffd0acd8EA43f689D8c20fB30"
3737
},
3838
"250": {
3939
"UniswapV2Router": "0x1b02da8cb0d097eb8d57a175b88c7d8b47997506",
4040
"UniswapV3Router": "0x0000000000000000000000000000000000000000",
4141
"KyberElasticQuoter": "0x0d125c15d54ca1f8a813c74a81aee34ebb508c1f",
4242
"KyberElasticRouter": "0xc1e7dfe73e1598e3910ef4c7845b68a9ab6f4c83",
4343
"KyberElasticPool": "0x8dcf5fed6ae6bf0befb5e4f0c9414c2cb9a4ed01",
44-
"TraderJoeV2Pool": "0x0000000000000000000000000000000000000000",
44+
"TraderJoeV2Quoter": "0x0000000000000000000000000000000000000000",
4545
"TraderJoeV2Router": "0x0000000000000000000000000000000000000000"
4646
},
4747
"10": {
@@ -50,7 +50,7 @@
5050
"KyberElasticQuoter": "0x0d125c15d54ca1f8a813c74a81aee34ebb508c1f",
5151
"KyberElasticRouter": "0xc1e7dfe73e1598e3910ef4c7845b68a9ab6f4c83",
5252
"KyberElasticPool": "0x7e29ccaa4bf2894aca02c77e6b99cafc1d24b2f5",
53-
"TraderJoeV2Pool": "0x0000000000000000000000000000000000000000",
53+
"TraderJoeV2Quoter": "0x0000000000000000000000000000000000000000",
5454
"TraderJoeV2Router": "0x0000000000000000000000000000000000000000"
5555
},
5656
"42161": {
@@ -59,7 +59,7 @@
5959
"KyberElasticQuoter": "0x0d125c15d54ca1f8a813c74a81aee34ebb508c1f",
6060
"KyberElasticRouter": "0xc1e7dfe73e1598e3910ef4c7845b68a9ab6f4c83",
6161
"KyberElasticPool": "0x087abaab9cd85025a8b3916948c69fe173c837ea",
62-
"TraderJoeV2Pool": "0x0000000000000000000000000000000000000000",
62+
"TraderJoeV2Quoter": "0x0000000000000000000000000000000000000000",
6363
"TraderJoeV2Router": "0x0000000000000000000000000000000000000000"
6464
}
6565
}

contracts/zero-ex/tests/forked/SwapERC20ForERC20Test.t.sol

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -81,8 +81,8 @@ contract SwapERC20ForERC20Test is Test, ForkUtils, TestUtils {
8181
emit log_string("TraderJoeV2Router not available on this chain");
8282
return;
8383
}
84-
if (sources.TraderJoeV2Pool == address(0)) {
85-
emit log_string("TraderJoeV2Pool not available on this chain");
84+
if (sources.TraderJoeV2Quoter == address(0)) {
85+
emit log_string("TraderJoeV2Quoter not available on this chain");
8686
return;
8787
}
8888

@@ -94,12 +94,11 @@ contract SwapERC20ForERC20Test is Test, ForkUtils, TestUtils {
9494
fqtData.fillSequence[0] = FillQuoteTransformer.OrderType.Bridge;
9595
fqtData.fillAmount = 1e6;
9696

97-
(uint256 amountOut, uint256 binStep) = sampleTraderJoeV2(
97+
(uint256 amountOut, uint256 binStep, uint256 version) = sampleTraderJoeV2(
9898
fqtData.fillAmount,
9999
address(fqtData.sellToken),
100100
address(fqtData.buyToken),
101-
sources.TraderJoeV2Router,
102-
sources.TraderJoeV2Pool
101+
sources.TraderJoeV2Quoter
103102
);
104103
log_named_uint("amountOut", amountOut);
105104

@@ -110,7 +109,9 @@ contract SwapERC20ForERC20Test is Test, ForkUtils, TestUtils {
110109
tokenPath[1] = address(fqtData.buyToken);
111110
uint256[] memory binSteps = new uint256[](1);
112111
binSteps[0] = binStep;
113-
order.bridgeData = abi.encode(address(sources.TraderJoeV2Router), tokenPath, binSteps);
112+
uint256[] memory versions = new uint256[](1);
113+
versions[0] = version;
114+
order.bridgeData = abi.encode(address(sources.TraderJoeV2Router), tokenPath, binSteps, versions);
114115
}
115116

116117
order.source = bytes32(uint256(BridgeProtocols.TRADERJOEV2) << 128);
@@ -198,20 +199,23 @@ contract SwapERC20ForERC20Test is Test, ForkUtils, TestUtils {
198199
uint256 amount,
199200
address takerToken,
200201
address makerToken,
201-
address router,
202-
address pool
203-
) private returns (uint256 makerTokenAmount, uint256 binStep) {
202+
address quoter
203+
) private returns (uint256 makerTokenAmount, uint256 binStep, uint256 version) {
204204
log_string("Sampling TraderJoeV2");
205205
log_named_address("takerToken", takerToken);
206206
log_named_address("makerToken", makerToken);
207-
log_named_address("router", router);
208-
log_named_address("pool", pool);
207+
log_named_address("quoter", quoter);
209208

210-
bool swapForY = ITraderJoeV2Pool(pool).tokenY() == makerToken;
209+
address[] memory tokenPath = new address[](2);
210+
tokenPath[0] = takerToken;
211+
tokenPath[1] = makerToken;
211212

212-
(makerTokenAmount, ) = ITraderJoeV2Router(router).getSwapOut(pool, amount, swapForY);
213+
ITraderJoeV2Quoter.Quote memory quote = ITraderJoeV2Quoter(quoter).findBestPathFromAmountIn(
214+
tokenPath,
215+
uint128(amount)
216+
);
213217

214-
binStep = ITraderJoeV2Pool(pool).feeParameters().binStep;
218+
return (quote.amounts[1], quote.binSteps[0], uint256(quote.versions[0]));
215219
}
216220

217221
function deployFQTAndGetDeploymentNonce(

contracts/zero-ex/tests/utils/ForkUtils.sol

Lines changed: 19 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ struct LiquiditySources {
9393
address KyberElasticPool;
9494
address KyberElasticQuoter;
9595
address KyberElasticRouter;
96-
address TraderJoeV2Pool;
96+
address TraderJoeV2Quoter;
9797
address TraderJoeV2Router;
9898
address UniswapV2Router;
9999
address UniswapV3Router;
@@ -103,35 +103,27 @@ interface IFQT {
103103
function bridgeAdapter() external returns (address);
104104
}
105105

106-
interface ITraderJoeV2Pool {
107-
struct FeeParameters {
108-
// 144 lowest bits in slot
109-
uint16 binStep;
110-
uint16 baseFactor;
111-
uint16 filterPeriod;
112-
uint16 decayPeriod;
113-
uint16 reductionFactor;
114-
uint24 variableFeeControl;
115-
uint16 protocolShare;
116-
uint24 maxVolatilityAccumulated;
117-
// 112 highest bits in slot
118-
uint24 volatilityAccumulated;
119-
uint24 volatilityReference;
120-
uint24 indexRef;
121-
uint40 time;
106+
interface ITraderJoeV2Quoter {
107+
enum Version {
108+
V1,
109+
V2,
110+
V2_1
122111
}
123112

124-
function feeParameters() external view returns (FeeParameters memory);
125-
126-
function tokenY() external view returns (address);
127-
}
113+
struct Quote {
114+
address[] route;
115+
address[] pairs;
116+
uint256[] binSteps;
117+
Version[] versions;
118+
uint128[] amounts;
119+
uint128[] virtualAmountsWithoutSlippage;
120+
uint128[] fees;
121+
}
128122

129-
interface ITraderJoeV2Router {
130-
function getSwapOut(
131-
address pool,
132-
uint256 amountIn,
133-
bool swapForY
134-
) external view returns (uint256 amountOut, uint256 feesIn);
123+
function findBestPathFromAmountIn(
124+
address[] calldata route,
125+
uint128 amountIn
126+
) external view returns (Quote memory quote);
135127
}
136128

137129
interface IKyberElasticQuoter {

packages/contract-addresses/CHANGELOG.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,12 @@
11
[
2+
{
3+
"version": "8.6.0",
4+
"changes": [
5+
{
6+
"note": "Add Trader Joe V2.1 Router Support for MixIn"
7+
}
8+
]
9+
},
210
{
311
"version": "8.5.0",
412
"changes": [

packages/contract-addresses/addresses.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@
156156
"wethTransformer": "0x9b8b52391071d71cd4ad1e61d7f273268fa34c6c",
157157
"payTakerTransformer": "0xb9a4c32547bc3cdc2ee2fb13cc1a0717dac9888f",
158158
"affiliateFeeTransformer": "0x105679f99d668001370b4621ad8648ac570c860f",
159-
"fillQuoteTransformer": "0x540079df6023d39b2686fd9f6c06f1f8f66aca4a",
159+
"fillQuoteTransformer": "0x886e4f97d7e06ab66dba574a7a861046dcf7ae4f",
160160
"positiveSlippageFeeTransformer": "0xadbfdc58a24b6dbc16f21541800f43dd6e282250"
161161
}
162162
},

0 commit comments

Comments
 (0)