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
This commit is contained in:
Savarn Dontamsetti (Sav) 2023-05-02 04:33:35 +09:00 committed by GitHub
parent 5a47f04ffc
commit 394ccbdc24
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 113 additions and 71 deletions

View File

@ -1,4 +1,12 @@
[ [
{
"version": "0.43.0",
"changes": [
{
"note": "Add Trader Joe V2.1 Router Support for MixIn"
}
]
},
{ {
"timestamp": 1682334742, "timestamp": 1682334742,
"version": "0.42.1", "version": "0.42.1",

View File

@ -20,19 +20,43 @@ import "@0x/contracts-erc20/src/IERC20Token.sol";
import "../IBridgeAdapter.sol"; import "../IBridgeAdapter.sol";
interface ILBRouter { interface ILBRouter {
/// @notice Swaps exact tokens for tokens while performing safety checks /**
/// @param amountIn The amount of token to send * @dev This enum represents the version of the pair requested
/// @param amountOutMin The min amount of token to receive * - V1: Joe V1 pair
/// @param pairBinSteps The bin step of the pairs (0: V1, other values will use V2) * - V2: LB pair V2. Also called legacyPair
/// @param tokenPath The swap path using the binSteps following `_pairBinSteps` * - V2_1: LB pair V2.1 (current version)
/// @param to The address of the recipient */
/// @param deadline The deadline of the tx enum Version {
/// @return amountOut Output amount of the swap 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( function swapExactTokensForTokens(
uint256 amountIn, uint256 amountIn,
uint256 amountOutMin, uint256 amountOutMin,
uint256[] memory pairBinSteps, Path memory path,
IERC20Token[] memory tokenPath,
address to, address to,
uint256 deadline uint256 deadline
) external returns (uint256 amountOut); ) external returns (uint256 amountOut);
@ -49,12 +73,16 @@ contract MixinTraderJoeV2 {
ILBRouter router; ILBRouter router;
IERC20Token[] memory tokenPath; IERC20Token[] memory tokenPath;
uint256[] memory pairBinSteps; uint256[] memory pairBinSteps;
ILBRouter.Version[] memory versions;
{ {
address[] memory _path; address[] memory _tokenPath;
(router, _path, pairBinSteps) = abi.decode(bridgeData, (ILBRouter, address[], uint256[])); (router, _tokenPath, pairBinSteps, versions) = abi.decode(
bridgeData,
(ILBRouter, address[], uint256[], ILBRouter.Version[])
);
// To get around `abi.decode()` not supporting interface array types. // To get around `abi.decode()` not supporting interface array types.
assembly { assembly {
tokenPath := _path tokenPath := _tokenPath
} }
} }
@ -63,6 +91,10 @@ contract MixinTraderJoeV2 {
tokenPath.length == pairBinSteps.length + 1, tokenPath.length == pairBinSteps.length + 1,
"MixinTraderJoeV2/PAIR_BIN_STEPS_LENGTH_MUST_BE_ONE_LESS_THAN_TOKEN_PATH_LENGTH" "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( require(
tokenPath[tokenPath.length - 1] == buyToken, tokenPath[tokenPath.length - 1] == buyToken,
"MixinTraderJoeV2/LAST_ELEMENT_OF_PATH_MUST_MATCH_OUTPUT_TOKEN" "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. // Grant the Trader Joe V2 router an allowance to sell the first token.
tokenPath[0].approveIfBelow(address(router), sellAmount); tokenPath[0].approveIfBelow(address(router), sellAmount);
boughtAmount = router.swapExactTokensForTokens( ILBRouter.Path memory path = ILBRouter.Path({
sellAmount, pairBinSteps: pairBinSteps,
1, versions: versions,
pairBinSteps, tokenPath: tokenPath
tokenPath, });
address(this), boughtAmount = router.swapExactTokensForTokens(sellAmount, 1, path, address(this), block.timestamp);
block.timestamp
);
} }
} }

View File

@ -5,7 +5,7 @@
"KyberElasticQuoter": "0x0d125c15d54ca1f8a813c74a81aee34ebb508c1f", "KyberElasticQuoter": "0x0d125c15d54ca1f8a813c74a81aee34ebb508c1f",
"KyberElasticRouter": "0xc1e7dfe73e1598e3910ef4c7845b68a9ab6f4c83", "KyberElasticRouter": "0xc1e7dfe73e1598e3910ef4c7845b68a9ab6f4c83",
"KyberElasticPool": "0x952ffc4c47d66b454a8181f5c68b6248e18b66ec", "KyberElasticPool": "0x952ffc4c47d66b454a8181f5c68b6248e18b66ec",
"TraderJoeV2Pool": "0x0000000000000000000000000000000000000000", "TraderJoeV2Quoter": "0x0000000000000000000000000000000000000000",
"TraderJoeV2Router": "0x0000000000000000000000000000000000000000" "TraderJoeV2Router": "0x0000000000000000000000000000000000000000"
}, },
"56": { "56": {
@ -14,7 +14,7 @@
"KyberElasticQuoter": "0x0d125c15d54ca1f8a813c74a81aee34ebb508c1f", "KyberElasticQuoter": "0x0d125c15d54ca1f8a813c74a81aee34ebb508c1f",
"KyberElasticRouter": "0xc1e7dfe73e1598e3910ef4c7845b68a9ab6f4c83", "KyberElasticRouter": "0xc1e7dfe73e1598e3910ef4c7845b68a9ab6f4c83",
"KyberElasticPool": "0xfbfab68ba077d099cd4b66fa76920572cc0b557c", "KyberElasticPool": "0xfbfab68ba077d099cd4b66fa76920572cc0b557c",
"TraderJoeV2Pool": "0x0000000000000000000000000000000000000000", "TraderJoeV2Quoter": "0x0000000000000000000000000000000000000000",
"TraderJoeV2Router": "0x0000000000000000000000000000000000000000" "TraderJoeV2Router": "0x0000000000000000000000000000000000000000"
}, },
"137": { "137": {
@ -23,7 +23,7 @@
"KyberElasticQuoter": "0x0d125c15d54ca1f8a813c74a81aee34ebb508c1f", "KyberElasticQuoter": "0x0d125c15d54ca1f8a813c74a81aee34ebb508c1f",
"KyberElasticRouter": "0xc1e7dfe73e1598e3910ef4c7845b68a9ab6f4c83", "KyberElasticRouter": "0xc1e7dfe73e1598e3910ef4c7845b68a9ab6f4c83",
"KyberElasticPool": "0xf9cc934753a127100585812181ac04d07158a4c2", "KyberElasticPool": "0xf9cc934753a127100585812181ac04d07158a4c2",
"TraderJoeV2Pool": "0x0000000000000000000000000000000000000000", "TraderJoeV2Quoter": "0x0000000000000000000000000000000000000000",
"TraderJoeV2Router": "0x0000000000000000000000000000000000000000" "TraderJoeV2Router": "0x0000000000000000000000000000000000000000"
}, },
"43114": { "43114": {
@ -32,8 +32,8 @@
"KyberElasticQuoter": "0x0d125c15d54ca1f8a813c74a81aee34ebb508c1f", "KyberElasticQuoter": "0x0d125c15d54ca1f8a813c74a81aee34ebb508c1f",
"KyberElasticRouter": "0xc1e7dfe73e1598e3910ef4c7845b68a9ab6f4c83", "KyberElasticRouter": "0xc1e7dfe73e1598e3910ef4c7845b68a9ab6f4c83",
"KyberElasticPool": "0x6038373de7f64da99b2a31951628b7d778b2c3cf", "KyberElasticPool": "0x6038373de7f64da99b2a31951628b7d778b2c3cf",
"TraderJoeV2Pool": "0x1D7A1a79e2b4Ef88D2323f3845246D24a3c20F1d", "TraderJoeV2Quoter": "0x3660268Ed43583a2cdd09e3fC7079ff07DBD4Caa",
"TraderJoeV2Router": "0xE3Ffc583dC176575eEA7FD9dF2A7c65F7E23f4C3" "TraderJoeV2Router": "0xb4315e873dBcf96Ffd0acd8EA43f689D8c20fB30"
}, },
"250": { "250": {
"UniswapV2Router": "0x1b02da8cb0d097eb8d57a175b88c7d8b47997506", "UniswapV2Router": "0x1b02da8cb0d097eb8d57a175b88c7d8b47997506",
@ -41,7 +41,7 @@
"KyberElasticQuoter": "0x0d125c15d54ca1f8a813c74a81aee34ebb508c1f", "KyberElasticQuoter": "0x0d125c15d54ca1f8a813c74a81aee34ebb508c1f",
"KyberElasticRouter": "0xc1e7dfe73e1598e3910ef4c7845b68a9ab6f4c83", "KyberElasticRouter": "0xc1e7dfe73e1598e3910ef4c7845b68a9ab6f4c83",
"KyberElasticPool": "0x8dcf5fed6ae6bf0befb5e4f0c9414c2cb9a4ed01", "KyberElasticPool": "0x8dcf5fed6ae6bf0befb5e4f0c9414c2cb9a4ed01",
"TraderJoeV2Pool": "0x0000000000000000000000000000000000000000", "TraderJoeV2Quoter": "0x0000000000000000000000000000000000000000",
"TraderJoeV2Router": "0x0000000000000000000000000000000000000000" "TraderJoeV2Router": "0x0000000000000000000000000000000000000000"
}, },
"10": { "10": {
@ -50,7 +50,7 @@
"KyberElasticQuoter": "0x0d125c15d54ca1f8a813c74a81aee34ebb508c1f", "KyberElasticQuoter": "0x0d125c15d54ca1f8a813c74a81aee34ebb508c1f",
"KyberElasticRouter": "0xc1e7dfe73e1598e3910ef4c7845b68a9ab6f4c83", "KyberElasticRouter": "0xc1e7dfe73e1598e3910ef4c7845b68a9ab6f4c83",
"KyberElasticPool": "0x7e29ccaa4bf2894aca02c77e6b99cafc1d24b2f5", "KyberElasticPool": "0x7e29ccaa4bf2894aca02c77e6b99cafc1d24b2f5",
"TraderJoeV2Pool": "0x0000000000000000000000000000000000000000", "TraderJoeV2Quoter": "0x0000000000000000000000000000000000000000",
"TraderJoeV2Router": "0x0000000000000000000000000000000000000000" "TraderJoeV2Router": "0x0000000000000000000000000000000000000000"
}, },
"42161": { "42161": {
@ -59,7 +59,7 @@
"KyberElasticQuoter": "0x0d125c15d54ca1f8a813c74a81aee34ebb508c1f", "KyberElasticQuoter": "0x0d125c15d54ca1f8a813c74a81aee34ebb508c1f",
"KyberElasticRouter": "0xc1e7dfe73e1598e3910ef4c7845b68a9ab6f4c83", "KyberElasticRouter": "0xc1e7dfe73e1598e3910ef4c7845b68a9ab6f4c83",
"KyberElasticPool": "0x087abaab9cd85025a8b3916948c69fe173c837ea", "KyberElasticPool": "0x087abaab9cd85025a8b3916948c69fe173c837ea",
"TraderJoeV2Pool": "0x0000000000000000000000000000000000000000", "TraderJoeV2Quoter": "0x0000000000000000000000000000000000000000",
"TraderJoeV2Router": "0x0000000000000000000000000000000000000000" "TraderJoeV2Router": "0x0000000000000000000000000000000000000000"
} }
} }

View File

@ -81,8 +81,8 @@ contract SwapERC20ForERC20Test is Test, ForkUtils, TestUtils {
emit log_string("TraderJoeV2Router not available on this chain"); emit log_string("TraderJoeV2Router not available on this chain");
return; return;
} }
if (sources.TraderJoeV2Pool == address(0)) { if (sources.TraderJoeV2Quoter == address(0)) {
emit log_string("TraderJoeV2Pool not available on this chain"); emit log_string("TraderJoeV2Quoter not available on this chain");
return; return;
} }
@ -94,12 +94,11 @@ contract SwapERC20ForERC20Test is Test, ForkUtils, TestUtils {
fqtData.fillSequence[0] = FillQuoteTransformer.OrderType.Bridge; fqtData.fillSequence[0] = FillQuoteTransformer.OrderType.Bridge;
fqtData.fillAmount = 1e6; fqtData.fillAmount = 1e6;
(uint256 amountOut, uint256 binStep) = sampleTraderJoeV2( (uint256 amountOut, uint256 binStep, uint256 version) = sampleTraderJoeV2(
fqtData.fillAmount, fqtData.fillAmount,
address(fqtData.sellToken), address(fqtData.sellToken),
address(fqtData.buyToken), address(fqtData.buyToken),
sources.TraderJoeV2Router, sources.TraderJoeV2Quoter
sources.TraderJoeV2Pool
); );
log_named_uint("amountOut", amountOut); log_named_uint("amountOut", amountOut);
@ -110,7 +109,9 @@ contract SwapERC20ForERC20Test is Test, ForkUtils, TestUtils {
tokenPath[1] = address(fqtData.buyToken); tokenPath[1] = address(fqtData.buyToken);
uint256[] memory binSteps = new uint256[](1); uint256[] memory binSteps = new uint256[](1);
binSteps[0] = binStep; 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); order.source = bytes32(uint256(BridgeProtocols.TRADERJOEV2) << 128);
@ -198,20 +199,23 @@ contract SwapERC20ForERC20Test is Test, ForkUtils, TestUtils {
uint256 amount, uint256 amount,
address takerToken, address takerToken,
address makerToken, address makerToken,
address router, address quoter
address pool ) private returns (uint256 makerTokenAmount, uint256 binStep, uint256 version) {
) private returns (uint256 makerTokenAmount, uint256 binStep) {
log_string("Sampling TraderJoeV2"); log_string("Sampling TraderJoeV2");
log_named_address("takerToken", takerToken); log_named_address("takerToken", takerToken);
log_named_address("makerToken", makerToken); log_named_address("makerToken", makerToken);
log_named_address("router", router); log_named_address("quoter", quoter);
log_named_address("pool", pool);
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( function deployFQTAndGetDeploymentNonce(

View File

@ -93,7 +93,7 @@ struct LiquiditySources {
address KyberElasticPool; address KyberElasticPool;
address KyberElasticQuoter; address KyberElasticQuoter;
address KyberElasticRouter; address KyberElasticRouter;
address TraderJoeV2Pool; address TraderJoeV2Quoter;
address TraderJoeV2Router; address TraderJoeV2Router;
address UniswapV2Router; address UniswapV2Router;
address UniswapV3Router; address UniswapV3Router;
@ -103,35 +103,27 @@ interface IFQT {
function bridgeAdapter() external returns (address); function bridgeAdapter() external returns (address);
} }
interface ITraderJoeV2Pool { interface ITraderJoeV2Quoter {
struct FeeParameters { enum Version {
// 144 lowest bits in slot V1,
uint16 binStep; V2,
uint16 baseFactor; V2_1
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); struct Quote {
address[] route;
address[] pairs;
uint256[] binSteps;
Version[] versions;
uint128[] amounts;
uint128[] virtualAmountsWithoutSlippage;
uint128[] fees;
}
function tokenY() external view returns (address); function findBestPathFromAmountIn(
} address[] calldata route,
uint128 amountIn
interface ITraderJoeV2Router { ) external view returns (Quote memory quote);
function getSwapOut(
address pool,
uint256 amountIn,
bool swapForY
) external view returns (uint256 amountOut, uint256 feesIn);
} }
interface IKyberElasticQuoter { interface IKyberElasticQuoter {

View File

@ -1,4 +1,12 @@
[ [
{
"version": "8.6.0",
"changes": [
{
"note": "Add Trader Joe V2.1 Router Support for MixIn"
}
]
},
{ {
"version": "8.5.0", "version": "8.5.0",
"changes": [ "changes": [

View File

@ -156,7 +156,7 @@
"wethTransformer": "0x9b8b52391071d71cd4ad1e61d7f273268fa34c6c", "wethTransformer": "0x9b8b52391071d71cd4ad1e61d7f273268fa34c6c",
"payTakerTransformer": "0xb9a4c32547bc3cdc2ee2fb13cc1a0717dac9888f", "payTakerTransformer": "0xb9a4c32547bc3cdc2ee2fb13cc1a0717dac9888f",
"affiliateFeeTransformer": "0x105679f99d668001370b4621ad8648ac570c860f", "affiliateFeeTransformer": "0x105679f99d668001370b4621ad8648ac570c860f",
"fillQuoteTransformer": "0x540079df6023d39b2686fd9f6c06f1f8f66aca4a", "fillQuoteTransformer": "0x886e4f97d7e06ab66dba574a7a861046dcf7ae4f",
"positiveSlippageFeeTransformer": "0xadbfdc58a24b6dbc16f21541800f43dd6e282250" "positiveSlippageFeeTransformer": "0xadbfdc58a24b6dbc16f21541800f43dd6e282250"
} }
}, },