Skip to content

Commit 8ed00b2

Browse files
feat: swapExactIn() & swapExactOut()
1 parent cf2d9c0 commit 8ed00b2

File tree

6 files changed

+130
-8
lines changed

6 files changed

+130
-8
lines changed

src/EulerSwap.sol

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,12 @@ contract EulerSwap is IEulerSwap, EVCUtil {
150150
return (reserve0, reserve1, status);
151151
}
152152

153+
/// @notice Returns the address of the Ethereum Vault Connector (EVC) used by this contract.
154+
/// @return The address of the EVC contract.
155+
function EVC() external view override(EVCUtil, IEulerSwap) returns (address) {
156+
return address(evc);
157+
}
158+
153159
/// @inheritdoc IEulerSwap
154160
function activate() public {
155161
require(status != 2, Locked());

src/EulerSwapPeriphery.sol

Lines changed: 39 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,40 @@ pragma solidity ^0.8.27;
33

44
import {IEVC} from "evc/interfaces/IEthereumVaultConnector.sol";
55
import {IEVault} from "evk/EVault/IEVault.sol";
6-
import {IEulerSwap} from "./interfaces/IEulerSwap.sol";
76
import {IEulerSwapPeriphery} from "./interfaces/IEulerSwapPeriphery.sol";
7+
import {IERC20, IEulerSwap, SafeERC20} from "./EulerSwap.sol";
88

99
contract EulerSwapPeriphery is IEulerSwapPeriphery {
10-
address private immutable evc;
11-
12-
constructor(address evc_) {
13-
evc = evc_;
14-
}
10+
using SafeERC20 for IERC20;
1511

1612
error UnsupportedPair();
1713
error OperatorNotInstalled();
1814
error InsufficientReserves();
1915
error InsufficientCash();
16+
error AmountOutLessThanMin();
17+
error AmountInMoreThanMax();
18+
19+
/// @inheritdoc IEulerSwapPeriphery
20+
function swapExactIn(address eulerSwap, address tokenIn, address tokenOut, uint256 amountIn, uint256 amountOutMin)
21+
external
22+
{
23+
uint256 amountOut = computeQuote(IEulerSwap(eulerSwap), tokenIn, tokenOut, amountIn, true);
24+
25+
require(amountOut >= amountOutMin, AmountOutLessThanMin());
26+
27+
_swap(eulerSwap, tokenIn, tokenOut, amountIn, amountOut);
28+
}
29+
30+
/// @inheritdoc IEulerSwapPeriphery
31+
function swapExactOut(address eulerSwap, address tokenIn, address tokenOut, uint256 amountOut, uint256 amountInMax)
32+
external
33+
{
34+
uint256 amountIn = computeQuote(IEulerSwap(eulerSwap), tokenIn, tokenOut, amountOut, false);
35+
36+
require(amountIn <= amountInMax, AmountInMoreThanMax());
37+
38+
_swap(eulerSwap, tokenIn, tokenOut, amountIn, amountOut);
39+
}
2040

2141
/// @inheritdoc IEulerSwapPeriphery
2242
function quoteExactInput(address eulerSwap, address tokenIn, address tokenOut, uint256 amountIn)
@@ -36,6 +56,17 @@ contract EulerSwapPeriphery is IEulerSwapPeriphery {
3656
return computeQuote(IEulerSwap(eulerSwap), tokenIn, tokenOut, amountOut, false);
3757
}
3858

59+
function _swap(address eulerSwap, address tokenIn, address tokenOut, uint256 amountIn, uint256 amountOut)
60+
internal
61+
{
62+
IERC20(tokenIn).safeTransferFrom(msg.sender, eulerSwap, amountIn);
63+
64+
bool isAsset0In = tokenIn < tokenOut;
65+
(isAsset0In)
66+
? IEulerSwap(eulerSwap).swap(0, amountOut, msg.sender, "")
67+
: IEulerSwap(eulerSwap).swap(amountOut, 0, msg.sender, "");
68+
}
69+
3970
/// @dev High-level quoting function. It handles fees and performs
4071
/// state validation, for example that there is sufficient cash available.
4172
function computeQuote(IEulerSwap eulerSwap, address tokenIn, address tokenOut, uint256 amount, bool exactIn)
@@ -44,7 +75,8 @@ contract EulerSwapPeriphery is IEulerSwapPeriphery {
4475
returns (uint256)
4576
{
4677
require(
47-
IEVC(evc).isAccountOperatorAuthorized(eulerSwap.myAccount(), address(eulerSwap)), OperatorNotInstalled()
78+
IEVC(eulerSwap.EVC()).isAccountOperatorAuthorized(eulerSwap.myAccount(), address(eulerSwap)),
79+
OperatorNotInstalled()
4880
);
4981

5082
uint256 feeMultiplier = eulerSwap.feeMultiplier();

src/interfaces/IEulerSwap.sol

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,10 @@ interface IEulerSwap {
3434
/// of the swapping curve).
3535
function verify(uint256 newReserve0, uint256 newReserve1) external view returns (bool);
3636

37+
/// @notice Returns the address of the Ethereum Vault Connector (EVC) used by this contract.
38+
/// @return The address of the EVC contract.
39+
function EVC() external view returns (address);
40+
3741
// EulerSwap Accessors
3842

3943
function curve() external view returns (bytes32);

src/interfaces/IEulerSwapPeriphery.sol

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,14 @@
22
pragma solidity >=0.8.0;
33

44
interface IEulerSwapPeriphery {
5+
/// @notice Swap `amountIn` of `tokenIn` for `tokenOut`, with at least `amountOutMin` received.
6+
function swapExactIn(address eulerSwap, address tokenIn, address tokenOut, uint256 amountIn, uint256 amountOutMin)
7+
external;
8+
9+
/// @notice Swap `amountOut` of `tokenOut` for `tokenIn`, with at most `amountInMax` paid.
10+
function swapExactOut(address eulerSwap, address tokenIn, address tokenOut, uint256 amountOut, uint256 amountInMax)
11+
external;
12+
513
/// @notice How much `tokenOut` can I get for `amountIn` of `tokenIn`?
614
function quoteExactInput(address eulerSwap, address tokenIn, address tokenOut, uint256 amountIn)
715
external

test/EulerSwapPeriphery.t.sol

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
// SPDX-License-Identifier: GPL-2.0-or-later
2+
pragma solidity ^0.8.24;
3+
4+
import {EulerSwapTestBase, EulerSwap, EulerSwapPeriphery} from "./EulerSwapTestBase.t.sol";
5+
6+
contract EulerSwapPeripheryTest is EulerSwapTestBase {
7+
EulerSwap public eulerSwap;
8+
9+
function setUp() public virtual override {
10+
super.setUp();
11+
12+
eulerSwap = createEulerSwap(50e18, 50e18, 0, 1e18, 1e18, 0.4e18, 0.85e18);
13+
}
14+
15+
function test_SwapExactIn() public {
16+
uint256 amountIn = 1e18;
17+
uint256 amountOut =
18+
periphery.quoteExactInput(address(eulerSwap), address(assetTST), address(assetTST2), amountIn);
19+
20+
assetTST.mint(anyone, amountIn);
21+
22+
vm.startPrank(anyone);
23+
assetTST.approve(address(periphery), amountIn);
24+
periphery.swapExactIn(address(eulerSwap), address(assetTST), address(assetTST2), amountIn, amountOut);
25+
vm.stopPrank();
26+
27+
assertEq(assetTST2.balanceOf(anyone), amountOut);
28+
}
29+
30+
function test_SwapExactIn_AmountOutLessThanMin() public {
31+
uint256 amountIn = 1e18;
32+
uint256 amountOut =
33+
periphery.quoteExactInput(address(eulerSwap), address(assetTST), address(assetTST2), amountIn);
34+
35+
assetTST.mint(anyone, amountIn);
36+
37+
vm.startPrank(anyone);
38+
assetTST.approve(address(periphery), amountIn);
39+
vm.expectRevert(EulerSwapPeriphery.AmountOutLessThanMin.selector);
40+
periphery.swapExactIn(address(eulerSwap), address(assetTST), address(assetTST2), amountIn, amountOut + 1);
41+
vm.stopPrank();
42+
}
43+
44+
function test_SwapExactOut() public {
45+
uint256 amountOut = 1e18;
46+
uint256 amountIn =
47+
periphery.quoteExactOutput(address(eulerSwap), address(assetTST), address(assetTST2), amountOut);
48+
49+
assetTST.mint(anyone, amountIn);
50+
51+
vm.startPrank(anyone);
52+
assetTST.approve(address(periphery), amountIn);
53+
periphery.swapExactOut(address(eulerSwap), address(assetTST), address(assetTST2), amountOut, amountIn);
54+
vm.stopPrank();
55+
56+
assertEq(assetTST2.balanceOf(anyone), amountOut);
57+
}
58+
59+
function test_SwapExactOut_AmountInMoreThanMax() public {
60+
uint256 amountOut = 1e18;
61+
uint256 amountIn =
62+
periphery.quoteExactOutput(address(eulerSwap), address(assetTST), address(assetTST2), amountOut);
63+
64+
assetTST.mint(anyone, amountIn);
65+
66+
vm.startPrank(anyone);
67+
assetTST.approve(address(periphery), amountIn);
68+
vm.expectRevert(EulerSwapPeriphery.AmountInMoreThanMax.selector);
69+
periphery.swapExactOut(address(eulerSwap), address(assetTST), address(assetTST2), amountOut * 2, amountIn);
70+
vm.stopPrank();
71+
}
72+
}

test/EulerSwapTestBase.t.sol

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ contract EulerSwapTestBase is EVaultTestBase {
2525
function setUp() public virtual override {
2626
super.setUp();
2727

28-
periphery = new EulerSwapPeriphery(address(evc));
28+
periphery = new EulerSwapPeriphery();
2929

3030
// Vault config
3131

0 commit comments

Comments
 (0)