Files
protocol/apps-node/api/contracts/test/TraderJoeV2MultiQuoter.t.sol
2023-03-27 13:40:55 -04:00

204 lines
7.6 KiB
Solidity

pragma solidity >=0.6.5;
import "forge-std/Test.sol";
import "../src/TraderJoeV2MultiQuoter.sol";
import "../src/TraderJoeV2Common.sol";
import "@traderjoe-xyz/joe-v2/src/LBQuoterV2.sol";
interface ILBQuoter {
function findBestPathFromAmountIn(
address[] calldata _route,
uint256 _amountIn
) external view returns (LBQuoterV2.Quote memory quote);
}
contract TestTraderJoeV2MultiQuoter is Test, TraderJoeV2Common {
/// @dev error threshold for comparison between MultiQuoter and TraderJoe's LBQuoterV2.
uint256 constant ERROR_THRESHOLD_1_BPS = 0.0001e18;
address constant JOE = 0x6e84a6216eA6dACC71eE8E6b0a5B7322EEbC0fDd;
address constant WAVAX = 0xB31f66AA3C1e785363F0875A1B74E27b85FD66c7;
address constant WETHe = 0x49D5c2BdFfac6CE2BFdB6640F4F80f226bc10bAB;
address constant WAVAX_WETHe_POOL = 0x42Be75636374dfA0e57EB96fA7F68fE7FcdAD8a3;
address constant JOE_WAVAX_POOL = 0xc01961EdE437Bf0cC41D064B1a3F6F0ea6aa2a40;
address constant factory = 0x6E77932A92582f504FF6c4BdbCef7Da6c198aEEf;
address constant router = 0xE3Ffc583dC176575eEA7FD9dF2A7c65F7E23f4C3;
TraderJoeV2MultiQuoter multiQuoter;
LBQuoterV2 quoter;
uint256[][] testAmounts;
function setUp() public {
multiQuoter = new TraderJoeV2MultiQuoter();
quoter = new LBQuoterV2(router, factory);
testAmounts = new uint256[][](9);
testAmounts[0] = new uint256[](1);
testAmounts[0][0] = 1 ether;
testAmounts[1] = new uint256[](1);
testAmounts[1][0] = 1000 ether;
testAmounts[2] = new uint256[](1);
testAmounts[2][0] = 100000 ether;
testAmounts[3] = new uint256[](13);
testAmounts[4] = new uint256[](13);
testAmounts[5] = new uint256[](13);
for (uint256 i = 0; i < 13; ++i) {
testAmounts[3][i] = (i + 1) * 1000 ether;
testAmounts[4][i] = (i + 1) * 50000 ether;
testAmounts[5][i] = (i + 1) * 100000 ether;
}
testAmounts[6] = new uint256[](50);
testAmounts[7] = new uint256[](50);
testAmounts[8] = new uint256[](50);
for (uint256 i = 0; i < 50; ++i) {
testAmounts[6][i] = (i + 1) * 1000 ether;
testAmounts[7][i] = (i + 1) * 10000 ether;
testAmounts[8][i] = (i + 1) * 50000 ether;
}
}
function testSingleHopQuotes() public {
address[] memory tokenPath = new address[](2);
tokenPath[0] = WAVAX;
tokenPath[1] = WETHe;
address[] memory poolPath = new address[](1);
poolPath[0] = WAVAX_WETHe_POOL;
testAllAmountsAndPathsForBuysAndSells(tokenPath, poolPath);
}
function testMultiHopQuotes() public {
address[] memory tokenPath = new address[](3);
tokenPath[0] = JOE;
tokenPath[1] = WAVAX;
tokenPath[2] = WETHe;
address[] memory poolPath = new address[](2);
poolPath[0] = JOE_WAVAX_POOL;
poolPath[1] = WAVAX_WETHe_POOL;
testAllAmountsAndPathsForBuysAndSells(tokenPath, poolPath);
}
function testAllAmountsAndPathsForBuysAndSells(address[] memory tokenPath, address[] memory poolPath) private {
uint256 traderJoeQuoterGasUsage;
uint256 multiQuoterGasUsage;
bytes memory path = toTraderJoeV2Path(tokenPath, poolPath);
address[] memory reverseTokenPath = reverseAddressPath(tokenPath);
bytes memory reversePath = toTraderJoeV2Path(reverseTokenPath, reverseAddressPath(poolPath));
console.log("Quoter Gas Comparison: ");
console.log("Token Path: ");
for (uint256 i = 0; i < tokenPath.length; ++i) {
console.logAddress(tokenPath[i]);
}
for (uint256 i = 0; i < testAmounts.length; ++i) {
(traderJoeQuoterGasUsage, multiQuoterGasUsage) = compareQuoterSells(path, tokenPath, testAmounts[i]);
console.log(
"Normal Path Sell: test=%d, traderJoeQuoterGasUsage=%d, multiQuoterGasUsage=%d",
i + 1,
traderJoeQuoterGasUsage,
multiQuoterGasUsage
);
(traderJoeQuoterGasUsage, multiQuoterGasUsage) = compareQuoterSells(
reversePath,
reverseTokenPath,
testAmounts[i]
);
console.log(
"Reverse Path Sell: test=%d, traderJoeQuoterGasUsage=%d, multiQuoterGasUsage=%d",
i + 1,
traderJoeQuoterGasUsage,
multiQuoterGasUsage
);
(traderJoeQuoterGasUsage, multiQuoterGasUsage) = compareQuoterBuys(reversePath, tokenPath, testAmounts[i]);
console.log(
"Normal Path Buy: test=%d, traderJoeQuoterGasUsage=%d, multiQuoterGasUsage=%d",
i + 1,
traderJoeQuoterGasUsage,
multiQuoterGasUsage
);
(traderJoeQuoterGasUsage, multiQuoterGasUsage) = compareQuoterBuys(path, reverseTokenPath, testAmounts[i]);
console.log(
"Reverse Path Buy: test=%d, traderJoeQuoterGasUsage=%d, multiQuoterGasUsage=%d",
i + 1,
traderJoeQuoterGasUsage,
multiQuoterGasUsage
);
}
}
function compareQuoterSells(
bytes memory path,
address[] memory tokenPath,
uint256[] memory amountsIn
) private returns (uint256 traderJoeQuoterGasUsage, uint256 multiQuoterGasUsage) {
uint256 gas0 = gasleft();
uint256[] memory multiQuoterAmountsOut;
try multiQuoter.quoteExactMultiInput(factory, path, amountsIn) {} catch (bytes memory reason) {
bool success;
(success, multiQuoterAmountsOut, ) = decodeMultiSwapRevert(reason);
assertTrue(success);
}
uint256 gas1 = gasleft();
for (uint256 i = 0; i < amountsIn.length; ++i) {
try quoter.findBestPathFromAmountIn(tokenPath, amountsIn[i]) returns (LBQuoterV2.Quote memory quote) {
assertApproxEqRel(
multiQuoterAmountsOut[i],
quote.amounts[quote.amounts.length - 1],
ERROR_THRESHOLD_1_BPS
);
} catch {
assertEq(
multiQuoterAmountsOut[i],
0,
"compareQuoterSells: MultiQuoter amount should be 0 when TraderJoeV2 QuoterV2 reverts"
);
}
}
return (gas1 - gasleft(), gas0 - gas1);
}
function compareQuoterBuys(
bytes memory path,
address[] memory tokenPath,
uint256[] memory amountsOut
) private returns (uint256 traderJoeQuoterGasUsage, uint256 multiQuoterGasUsage) {
uint256 gas0 = gasleft();
uint256[] memory multiQuoterAmountsIn;
try multiQuoter.quoteExactMultiOutput(factory, path, amountsOut) {} catch (bytes memory reason) {
bool success;
(success, multiQuoterAmountsIn, ) = decodeMultiSwapRevert(reason);
assertTrue(success);
}
uint256 gas1 = gasleft();
for (uint256 i = 0; i < amountsOut.length; ++i) {
try quoter.findBestPathFromAmountOut(tokenPath, amountsOut[i]) returns (LBQuoterV2.Quote memory quote) {
assertApproxEqRel(multiQuoterAmountsIn[i], quote.amounts[0], ERROR_THRESHOLD_1_BPS);
} catch {
assertEq(
multiQuoterAmountsIn[i],
0,
"compareQuoterSells: MultiQuoter amount should be 0 when TraderJoeV2 QuoterV2 reverts"
);
}
}
return (gas1 - gasleft(), gas0 - gas1);
}
}