diff --git a/.gitignore b/.gitignore index 3269660..64c78d5 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,6 @@ out/ # Dotenv file .env + +# Coverage file +lcov.info \ No newline at end of file diff --git a/src/MaglevBase.sol b/src/MaglevBase.sol index 2629ddc..f799be8 100644 --- a/src/MaglevBase.sol +++ b/src/MaglevBase.sol @@ -5,8 +5,9 @@ import {EVCUtil} from "evc/utils/EVCUtil.sol"; import {IEVC} from "evc/interfaces/IEthereumVaultConnector.sol"; import {IEVault, IERC20, IBorrowing, IERC4626, IRiskManager} from "evk/EVault/IEVault.sol"; import {IUniswapV2Callee} from "./interfaces/IUniswapV2Callee.sol"; +import {IMaglevBase} from "./interfaces/IMaglevBase.sol"; -abstract contract MaglevBase is EVCUtil { +abstract contract MaglevBase is IMaglevBase, EVCUtil { address public immutable vault0; address public immutable vault1; address public immutable asset0; @@ -51,6 +52,9 @@ abstract contract MaglevBase is EVCUtil { vault1 = params.vault1; asset0 = IEVault(vault0).asset(); asset1 = IEVault(vault1).asset(); + + require(asset0 != asset1, UnsupportedPair()); + myAccount = params.myAccount; reserve0 = offsetReserve(params.debtLimit0, vault0); reserve1 = offsetReserve(params.debtLimit1, vault1); @@ -82,7 +86,7 @@ abstract contract MaglevBase is EVCUtil { // Invoke callback - if (data.length > 0) IUniswapV2Callee(to).uniswapV2Call(msg.sender, amount0Out, amount1Out, data); + if (data.length > 0) IUniswapV2Callee(to).uniswapV2Call(_msgSender(), amount0Out, amount1Out, data); // Deposit all available funds, adjust received amounts downward to collect fees diff --git a/src/MaglevEulerSwap.sol b/src/MaglevEulerSwap.sol index b56b72a..3d8aa04 100644 --- a/src/MaglevEulerSwap.sol +++ b/src/MaglevEulerSwap.sol @@ -3,8 +3,9 @@ pragma solidity ^0.8.27; import {Math} from "@openzeppelin/contracts/utils/math/Math.sol"; import {MaglevBase} from "./MaglevBase.sol"; +import {IMaglevEulerSwap} from "./interfaces/IMaglevEulerSwap.sol"; -contract MaglevEulerSwap is MaglevBase { +contract MaglevEulerSwap is IMaglevEulerSwap, MaglevBase { uint256 public immutable priceX; uint256 public immutable priceY; uint256 public immutable concentrationX; diff --git a/src/interfaces/IMaglevBase.sol b/src/interfaces/IMaglevBase.sol new file mode 100644 index 0000000..0bc4dcc --- /dev/null +++ b/src/interfaces/IMaglevBase.sol @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity >=0.8.0; + +interface IMaglevBase { + function configure() external; + function swap(uint256 amount0Out, uint256 amount1Out, address to, bytes calldata data) external; + function quoteExactInput(address tokenIn, address tokenOut, uint256 amountIn) external view returns (uint256); + function quoteExactOutput(address tokenIn, address tokenOut, uint256 amountOut) external view returns (uint256); + + function vault0() external view returns (address); + function vault1() external view returns (address); + function asset0() external view returns (address); + function asset1() external view returns (address); + function myAccount() external view returns (address); + function debtLimit0() external view returns (uint112); + function debtLimit1() external view returns (uint112); + function feeMultiplier() external view returns (uint256); + function reserve0() external view returns (uint112); + function reserve1() external view returns (uint112); +} diff --git a/src/interfaces/IMaglevEulerSwap.sol b/src/interfaces/IMaglevEulerSwap.sol new file mode 100644 index 0000000..3902449 --- /dev/null +++ b/src/interfaces/IMaglevEulerSwap.sol @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity >=0.8.0; + +import {IMaglevBase} from "./IMaglevBase.sol"; + +interface IMaglevEulerSwap is IMaglevBase { + function priceX() external view returns (uint256); + function priceY() external view returns (uint256); + function concentrationX() external view returns (uint256); + function concentrationY() external view returns (uint256); + function initialReserve0() external view returns (uint112); + function initialReserve1() external view returns (uint112); +} diff --git a/test/UniswapV2Call.t.sol b/test/UniswapV2Call.t.sol new file mode 100644 index 0000000..11516d3 --- /dev/null +++ b/test/UniswapV2Call.t.sol @@ -0,0 +1,83 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity ^0.8.24; + +import {IUniswapV2Callee} from "../src/interfaces/IUniswapV2Callee.sol"; +// import {Test, console} from "forge-std/Test.sol"; +import {MaglevTestBase} from "./MaglevTestBase.t.sol"; +import {MaglevEulerSwap as Maglev} from "../src/MaglevEulerSwap.sol"; + +contract UniswapV2CallTest is MaglevTestBase { + Maglev public maglev; + SwapCallbackTest swapCallback; + + function setUp() public virtual override { + super.setUp(); + + createMaglev(50e18, 50e18, 0, 1e18, 1e18, 0.4e18, 0.85e18); + + swapCallback = new SwapCallbackTest(); + } + + function createMaglev( + uint112 debtLimit0, + uint112 debtLimit1, + uint256 fee, + uint256 px, + uint256 py, + uint256 cx, + uint256 cy + ) internal { + vm.prank(creator); + maglev = new Maglev( + getMaglevBaseParams(debtLimit0, debtLimit1, fee), + Maglev.EulerSwapParams({priceX: px, priceY: py, concentrationX: cx, concentrationY: cy}) + ); + + vm.prank(holder); + evc.setAccountOperator(holder, address(maglev), true); + + vm.prank(anyone); + maglev.configure(); + } + + function test_callback() public { + uint256 amountIn = 1e18; + uint256 amountOut = maglev.quoteExactInput(address(assetTST), address(assetTST2), amountIn); + assertApproxEqAbs(amountOut, 0.9974e18, 0.0001e18); + + assetTST.mint(address(this), amountIn); + assetTST.transfer(address(maglev), amountIn); + + uint256 randomBalance = 3e18; + vm.prank(anyone); + swapCallback.executeSwap(maglev, 0, amountOut, abi.encode(randomBalance)); + assertEq(assetTST2.balanceOf(address(swapCallback)), amountOut); + assertEq(swapCallback.callbackSender(), address(swapCallback)); + assertEq(swapCallback.callbackAmount0(), 0); + assertEq(swapCallback.callbackAmount1(), amountOut); + assertEq(swapCallback.randomBalance(), randomBalance); + } +} + +contract SwapCallbackTest is IUniswapV2Callee { + address public callbackSender; + uint256 public callbackAmount0; + uint256 public callbackAmount1; + uint256 public randomBalance; + + function executeSwap(Maglev maglev, uint256 amountIn, uint256 amountOut, bytes calldata data) external { + maglev.swap(amountIn, amountOut, address(this), data); + } + + function uniswapV2Call(address sender, uint256 amount0, uint256 amount1, bytes calldata data) external { + randomBalance = abi.decode(data, (uint256)); + + callbackSender = sender; + callbackAmount0 = amount0; + callbackAmount1 = amount1; + } + + function test_avoid_coverage() public pure { + return; + } +}