diff --git a/evm/src/euler-swap/EulerSwapAdapter.sol b/evm/src/euler-swap/EulerSwapAdapter.sol new file mode 100644 index 00000000..2b885be5 --- /dev/null +++ b/evm/src/euler-swap/EulerSwapAdapter.sol @@ -0,0 +1,136 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later +pragma solidity ^0.8.13; + +import {ISwapAdapter} from "src/interfaces/ISwapAdapter.sol"; +import { + IERC20, + SafeERC20 +} from "openzeppelin-contracts/contracts/token/ERC20/utils/SafeERC20.sol"; + +contract EulerSwapAdapter is ISwapAdapter { + using SafeERC20 for IERC20; + + IMaglevEulerSwapFactory immutable factory; + + constructor(address factory_) { + factory = IMaglevEulerSwapFactory(factory_); + } + + /// @inheritdoc ISwapAdapter + function price( + bytes32 poolId, + address sellToken, + address buyToken, + uint256[] memory specifiedAmounts + ) external view override returns (Fraction[] memory prices) { + prices = new Fraction[](specifiedAmounts.length); + + IMaglevEulerSwap pool = IMaglevEulerSwap(address(bytes20(poolId))); + for (uint256 i = 0; i < specifiedAmounts.length; i++) { + prices[i] = priceSingle(pool, sellToken, buyToken, specifiedAmounts[i]); + } + } + + /// @inheritdoc ISwapAdapter + function getCapabilities(bytes32, address, address) + external + pure + override + returns (Capability[] memory capabilities) + { + capabilities = new Capability[](3); + capabilities[0] = Capability.SellOrder; + capabilities[1] = Capability.BuyOrder; + capabilities[2] = Capability.PriceFunction; + } + + /// @inheritdoc ISwapAdapter + function getTokens(bytes32 poolId) + external + view + override + returns (address[] memory tokens) + { + tokens = new address[](2); + IMaglevEulerSwap pool = IMaglevEulerSwap(address(bytes20(poolId))); + tokens[0] = address(pool.asset0()); + tokens[1] = address(pool.asset1()); + } + + /// @inheritdoc ISwapAdapter + function getPoolIds(uint256 offset, uint256 limit) + external + view + override + returns (bytes32[] memory ids) + { + uint256 endIdx = offset + limit; + if (endIdx > factory.allPoolsLength()) { + endIdx = factory.allPoolsLength(); + } + ids = new bytes32[](endIdx - offset); + for (uint256 i = 0; i < ids.length; i++) { + ids[i] = bytes20(factory.allPools(offset + i)); + } + } + + /// @notice Calculates pool prices for specified amounts + function priceSingle( + IMaglevEulerSwap pool, + address tokenIn, + address tokenOut, + uint256 amountIn + ) internal view returns (Fraction memory calculatedPrice) { + calculatedPrice = + Fraction(pool.quoteExactInput(tokenIn, tokenOut, amountIn), amountIn); + } +} + +interface IMaglevEulerSwapFactory { + event PoolDeployed(address indexed asset0, address indexed asset1, uint256 indexed feeMultiplier, address pool); + + function deployPool( + address vault0, + address vault1, + address holder, + uint112 debtLimit0, + uint112 debtLimit1, + uint256 fee, + uint256 priceX, + uint256 priceY, + uint256 concentrationX, + uint256 concentrationY + ) external returns (address); + + function evc() external view returns (address); + function allPools(uint256 index) external view returns (address); + function getPool(address assetA, address assetB, uint256 fee) external view returns (address); + function allPoolsLength() external view returns (uint256); + function getAllPoolsListSlice(uint256 start, uint256 end) external view returns (address[] memory); +} + +interface IMaglevEulerSwap { + // 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 getReserves() external view returns (uint112, uint112, uint32); + + // IMaglevEulerSwap + 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/evm/src/euler-swap/manifest.yaml b/evm/src/euler-swap/manifest.yaml new file mode 100644 index 00000000..886d3adb --- /dev/null +++ b/evm/src/euler-swap/manifest.yaml @@ -0,0 +1,24 @@ +# information about the author helps us reach out in case of issues. +author: + name: Haythem + email: haythem@euler.xyz + +# Protocol Constants +constants: + protocol_gas: 30000 + # minimum capabilities we can expect, individual pools may extend these + capabilities: + - SellSide + - BuySide + - PriceFunction + +# The file containing the adapter contract +contract: EulerSwapAdapter.sol + +# Deployment instances used to generate chain specific bytecode. +instances: + - chain: + name: mainnet + id: 1 + arguments: + - "0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f"