207 lines
8.1 KiB
Solidity
207 lines
8.1 KiB
Solidity
pragma solidity >=0.6.5;
|
|
pragma experimental ABIEncoderV2;
|
|
|
|
import "forge-std/Test.sol";
|
|
import "../src/AlgebraMultiQuoter.sol";
|
|
import "../src/AlgebraCommon.sol";
|
|
|
|
interface IAlgebraQuoter {
|
|
/// @notice Returns the amount out received for a given exact input swap without executing the swap
|
|
/// @param path The path of the swap, i.e. each token pair
|
|
/// @param amountIn The amount of the first token to swap
|
|
/// @return amountOut The amount of the last token that would be received
|
|
function quoteExactInput(
|
|
bytes memory path,
|
|
uint256 amountIn
|
|
) external returns (uint256 amountOut, uint16[] memory fees);
|
|
|
|
/// @notice Returns the amount in required for a given exact output swap without executing the swap
|
|
/// @param path The path of the swap, i.e. each token pair. Path must be provided in reverse order
|
|
/// @param amountOut The amount of the last token to receive
|
|
/// @return amountIn The amount of first token required to be paid
|
|
function quoteExactOutput(
|
|
bytes memory path,
|
|
uint256 amountOut
|
|
) external returns (uint256 amountIn, uint16[] memory fees);
|
|
}
|
|
|
|
contract TestAlgebraMultiQuoter is Test, AlgebraCommon {
|
|
/// @dev error threshold for comparison between MultiQuoter and UniswapV3's official QuoterV2.
|
|
/// MultiQuoter results in some rounding errors due to SqrtPriceMath library.
|
|
uint256 constant ERROR_THRESHOLD_10_BPS = 0.001e18;
|
|
|
|
address constant WMATIC = 0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270;
|
|
address constant WETH = 0x7ceB23fD6bC0adD59E62ac25578270cFf1b9f619;
|
|
address constant DAI = 0x8f3Cf7ad23Cd3CaDbD9735AFf958023239c6A063;
|
|
|
|
IAlgebraPool constant WMATIC_WETH_POOL = IAlgebraPool(0x479e1B71A702a595e19b6d5932CD5c863ab57ee0);
|
|
IAlgebraPool constant WMATIC_DAI_POOL = IAlgebraPool(0x6BaEad5Db7FeE6D5c9F0Ca07Bb5038C4cD279F5c);
|
|
|
|
IAlgebraFactory constant factory = IAlgebraFactory(0x411b0fAcC3489691f28ad58c47006AF5E3Ab3A28);
|
|
address constant poolDeployer = 0x2D98E2FA9da15aa6dC9581AB097Ced7af697CB92;
|
|
|
|
IAlgebraQuoter constant algebraQuoter = IAlgebraQuoter(0xa15F0D7377B2A0C0c10db057f641beD21028FC89);
|
|
|
|
AlgebraMultiQuoter multiQuoter;
|
|
uint256[][] testAmounts;
|
|
|
|
function setUp() public {
|
|
multiQuoter = new AlgebraMultiQuoter();
|
|
|
|
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 testSingleHopQuotesForLiquidPools() public {
|
|
address[] memory tokenPath = new address[](2);
|
|
tokenPath[0] = WMATIC;
|
|
tokenPath[1] = WETH;
|
|
|
|
testAllAmountsAndPathsForBuysAndSells(tokenPath);
|
|
}
|
|
|
|
function testSingleHopQuotesForIlliquidPools() public {
|
|
address[] memory tokenPath = new address[](2);
|
|
tokenPath[0] = WMATIC;
|
|
tokenPath[1] = DAI;
|
|
|
|
testAllAmountsAndPathsForBuysAndSells(tokenPath);
|
|
}
|
|
|
|
function testMultiHopQuotes() public {
|
|
address[] memory tokenPath = new address[](3);
|
|
tokenPath[0] = WETH;
|
|
tokenPath[1] = WMATIC;
|
|
tokenPath[2] = DAI;
|
|
|
|
testAllAmountsAndPathsForBuysAndSells(tokenPath);
|
|
}
|
|
|
|
function testAllAmountsAndPathsForBuysAndSells(address[] memory tokenPath) private {
|
|
uint256 algebraQuoterGasUsage;
|
|
uint256 multiQuoterGasUsage;
|
|
|
|
bytes memory path = toAlgebraPath(tokenPath);
|
|
bytes memory reversePath = toAlgebraPath(reverseAddressPath(tokenPath));
|
|
|
|
console.log("Quoter Gas Comparison ");
|
|
console.log("Token Path: ");
|
|
for (uint256 i = 0; i < tokenPath.length; ++i) {
|
|
console.logAddress(address(tokenPath[i]));
|
|
}
|
|
|
|
for (uint256 i = 0; i < testAmounts.length; ++i) {
|
|
(algebraQuoterGasUsage, multiQuoterGasUsage) = compareQuoterSells(path, testAmounts[i]);
|
|
console.log(
|
|
"Normal Path Sell: test=%d, algebraQuoterGasUsage=%d, multiQuoterGasUsage=%d",
|
|
i + 1,
|
|
algebraQuoterGasUsage,
|
|
multiQuoterGasUsage
|
|
);
|
|
(algebraQuoterGasUsage, multiQuoterGasUsage) = compareQuoterSells(reversePath, testAmounts[i]);
|
|
console.log(
|
|
"Reverse Path Sell: test=%d, algebraQuoterGasUsage=%d, multiQuoterGasUsage=%d",
|
|
i + 1,
|
|
algebraQuoterGasUsage,
|
|
multiQuoterGasUsage
|
|
);
|
|
(algebraQuoterGasUsage, multiQuoterGasUsage) = compareQuoterBuys(path, testAmounts[i]);
|
|
console.log(
|
|
"Normal Path Buy: test=%d, algebraQuoterGasUsage=%d, multiQuoterGasUsage=%d",
|
|
i + 1,
|
|
algebraQuoterGasUsage,
|
|
multiQuoterGasUsage
|
|
);
|
|
(algebraQuoterGasUsage, multiQuoterGasUsage) = compareQuoterBuys(reversePath, testAmounts[i]);
|
|
console.log(
|
|
"Reverse Path Buy: test=%d, algebraQuoterGasUsage=%d, multiQuoterGasUsage=%d",
|
|
i + 1,
|
|
algebraQuoterGasUsage,
|
|
multiQuoterGasUsage
|
|
);
|
|
}
|
|
}
|
|
|
|
function compareQuoterSells(
|
|
bytes memory path,
|
|
uint256[] memory amountsIn
|
|
) private returns (uint256 algebraQuoterGasUsage, uint256 multiQuoterGasUsage) {
|
|
uint256 gas0 = gasleft();
|
|
uint256[] memory multiQuoterAmountsOut;
|
|
try multiQuoter.quoteExactMultiInput(address(factory), path, amountsIn) {} catch (bytes memory reason) {
|
|
(, multiQuoterAmountsOut, ) = decodeMultiSwapRevert(reason);
|
|
}
|
|
uint256 gas1 = gasleft();
|
|
|
|
for (uint256 i = 0; i < amountsIn.length; ++i) {
|
|
try algebraQuoter.quoteExactInput(path, amountsIn[i]) returns (
|
|
uint256 algebraQuoterAmountOut,
|
|
uint16[] memory /* fees */
|
|
) {
|
|
assertApproxEqRel(multiQuoterAmountsOut[i], algebraQuoterAmountOut, ERROR_THRESHOLD_10_BPS);
|
|
} catch {
|
|
assertEq(
|
|
multiQuoterAmountsOut[i],
|
|
0,
|
|
"compareQuoterSells: MultiQuoter amount should be 0 when UniQuoter reverts"
|
|
);
|
|
}
|
|
}
|
|
return (gas1 - gasleft(), gas0 - gas1);
|
|
}
|
|
|
|
function compareQuoterBuys(
|
|
bytes memory path,
|
|
uint256[] memory amountsOut
|
|
) private returns (uint256 algebraQuoterGasUsage, uint256 multiQuoterGasUsage) {
|
|
uint256 gas0 = gasleft();
|
|
uint256[] memory multiQuoterAmountsIn;
|
|
try multiQuoter.quoteExactMultiOutput(address(factory), path, amountsOut) {} catch (bytes memory reason) {
|
|
(, multiQuoterAmountsIn, ) = decodeMultiSwapRevert(reason);
|
|
}
|
|
uint256 gas1 = gasleft();
|
|
|
|
for (uint256 i = 0; i < amountsOut.length; ++i) {
|
|
try algebraQuoter.quoteExactOutput(path, amountsOut[i]) returns (
|
|
uint256 algebraQuoterAmountIn,
|
|
uint16[] memory /* fees */
|
|
) {
|
|
assertApproxEqRel(multiQuoterAmountsIn[i], algebraQuoterAmountIn, ERROR_THRESHOLD_10_BPS);
|
|
} catch {
|
|
assertEq(
|
|
multiQuoterAmountsIn[i],
|
|
0,
|
|
"compareQuoterBuys: MultiQuoter amount should be 0 when UniQuoter reverts"
|
|
);
|
|
}
|
|
}
|
|
return (gas1 - gasleft(), gas0 - gas1);
|
|
}
|
|
}
|