Skip to content

Commit 4b74e00

Browse files
committed
move fee logic to base class so it's shared by all curves
1 parent 5e450bd commit 4b74e00

8 files changed

+108
-100
lines changed

src/MaglevBase.sol

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ abstract contract MaglevBase is EVCUtil, Ownable {
1414
address public immutable asset0;
1515
address public immutable asset1;
1616
address public immutable myAccount;
17+
uint112 public immutable debtLimit0;
18+
uint112 public immutable debtLimit1;
19+
uint256 public immutable feeMultiplier;
1720

1821
uint112 public reserve0;
1922
uint112 public reserve1;
@@ -25,6 +28,7 @@ abstract contract MaglevBase is EVCUtil, Ownable {
2528
error Reentrancy();
2629
error Overflow();
2730
error UnsupportedPair();
31+
error BadFee();
2832
error InsufficientReserves();
2933
error InsufficientCash();
3034

@@ -44,14 +48,22 @@ abstract contract MaglevBase is EVCUtil, Ownable {
4448
address vault0;
4549
address vault1;
4650
address myAccount;
51+
uint112 debtLimit0;
52+
uint112 debtLimit1;
53+
uint256 fee;
4754
}
4855

4956
constructor(BaseParams memory params) EVCUtil(params.evc) Ownable(msg.sender) {
57+
require(params.fee < 1e18, BadFee());
58+
5059
vault0 = params.vault0;
5160
vault1 = params.vault1;
5261
asset0 = IEVault(vault0).asset();
5362
asset1 = IEVault(vault1).asset();
5463
myAccount = params.myAccount;
64+
reserve0 = initialReserve0 = adjustReserve(params.debtLimit0, vault0);
65+
reserve1 = initialReserve1 = adjustReserve(params.debtLimit1, vault1);
66+
feeMultiplier = 1e18 - params.fee;
5567
}
5668

5769
// Owner functions
@@ -65,11 +77,6 @@ abstract contract MaglevBase is EVCUtil, Ownable {
6577
IEVC(evc).enableCollateral(myAccount, vault1);
6678
}
6779

68-
function setDebtLimit(uint112 debtLimit0, uint112 debtLimit1) external onlyOwner {
69-
reserve0 = initialReserve0 = adjustReserve(debtLimit0, vault0);
70-
reserve1 = initialReserve1 = adjustReserve(debtLimit1, vault1);
71-
}
72-
7380
// Swapper interface
7481

7582
function swap(uint256 amount0Out, uint256 amount1Out, address to, bytes calldata data)
@@ -86,12 +93,19 @@ abstract contract MaglevBase is EVCUtil, Ownable {
8693

8794
if (data.length > 0) IUniswapV2Callee(to).uniswapV2Call(msg.sender, amount0Out, amount1Out, data);
8895

89-
// Deposit all available funds
96+
// Deposit all available funds, adjust received amounts downward to collect fees
9097

9198
uint256 amount0In = IERC20(asset0).balanceOf(address(this));
99+
if (amount0In > 0) {
100+
depositAssets(vault0, amount0In);
101+
amount0In = amount0In * feeMultiplier / 1e18;
102+
}
103+
92104
uint256 amount1In = IERC20(asset1).balanceOf(address(this));
93-
if (amount0In > 0) depositAssets(vault0, amount0In);
94-
if (amount1In > 0) depositAssets(vault1, amount1In);
105+
if (amount1In > 0) {
106+
depositAssets(vault1, amount1In);
107+
amount1In = amount1In * feeMultiplier / 1e18;
108+
}
95109

96110
// Verify curve invariant is satisified
97111

@@ -176,6 +190,9 @@ abstract contract MaglevBase is EVCUtil, Ownable {
176190
view
177191
returns (uint256)
178192
{
193+
// exactIn: decrease received amountIn, rounding down
194+
if (exactIn) amount = amount * feeMultiplier / 1e18;
195+
179196
bool asset0IsInput;
180197
if (tokenIn == asset0 && tokenOut == asset1) asset0IsInput = true;
181198
else if (tokenIn == asset1 && tokenOut == asset0) asset0IsInput = false;
@@ -189,6 +206,9 @@ abstract contract MaglevBase is EVCUtil, Ownable {
189206
InsufficientCash()
190207
);
191208

209+
// exactOut: increase required amountIn, rounding up
210+
if (!exactIn) quote = (quote * 1e18 + (feeMultiplier - 1)) / feeMultiplier;
211+
192212
return quote;
193213
}
194214

src/MaglevConstantProduct.sol

Lines changed: 5 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4,34 +4,23 @@ pragma solidity ^0.8.27;
44
import {MaglevBase} from "./MaglevBase.sol";
55

66
contract MaglevConstantProduct is MaglevBase {
7-
uint64 public fee;
8-
97
error KNotSatisfied();
108

11-
struct ConstantProductParams {
12-
uint64 fee;
13-
}
14-
15-
constructor(BaseParams memory baseParams, ConstantProductParams memory params) MaglevBase(baseParams) {
16-
setConstantProductParams(params);
17-
}
18-
19-
function setConstantProductParams(ConstantProductParams memory params) public onlyOwner {
20-
fee = params.fee;
9+
constructor(BaseParams memory baseParams) MaglevBase(baseParams) {
2110
}
2211

2312
function k(uint256 r0, uint256 r1) public pure returns (uint256) {
2413
return r0 * r1;
2514
}
2615

27-
function verify(uint256 amount0In, uint256 amount1In, uint256 newReserve0, uint256 newReserve1)
16+
function verify(uint256, uint256, uint256 newReserve0, uint256 newReserve1)
2817
internal
2918
view
3019
virtual
3120
override
3221
{
3322
uint256 kBefore = k(reserve0, reserve1);
34-
uint256 kAfter = k(newReserve0 - (amount0In * fee / 1e18), newReserve1 - (amount1In * fee / 1e18));
23+
uint256 kAfter = k(newReserve0, newReserve1);
3524
require(kAfter >= kBefore, KNotSatisfied());
3625
}
3726

@@ -46,9 +35,9 @@ contract MaglevConstantProduct is MaglevBase {
4635
uint256 reserveOut = asset0IsInput ? reserve1 : reserve0;
4736

4837
if (exactIn) {
49-
return (reserveOut * amount) / (reserveIn + amount) * (1e18 - fee) / 1e18;
38+
return (reserveOut * amount) / (reserveIn + amount);
5039
} else {
51-
return (reserveIn * amount) / (reserveOut - amount) * 1e18 / (1e18 - fee);
40+
return (reserveIn * amount) / (reserveOut - amount);
5241
}
5342
}
5443
}

src/MaglevConstantSum.sol

Lines changed: 8 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -4,24 +4,17 @@ pragma solidity ^0.8.27;
44
import {MaglevBase} from "./MaglevBase.sol";
55

66
contract MaglevConstantSum is MaglevBase {
7-
uint64 public fee;
8-
uint96 public priceA;
9-
uint96 public priceB;
7+
uint256 public immutable priceA;
8+
uint256 public immutable priceB;
109

1110
error KNotSatisfied();
1211

1312
struct ConstantSumParams {
14-
uint64 fee;
15-
uint96 priceA;
16-
uint96 priceB;
13+
uint256 priceA;
14+
uint256 priceB;
1715
}
1816

1917
constructor(BaseParams memory baseParams, ConstantSumParams memory params) MaglevBase(baseParams) {
20-
setConstantSumParams(params);
21-
}
22-
23-
function setConstantSumParams(ConstantSumParams memory params) public onlyOwner {
24-
fee = params.fee;
2518
priceA = params.priceA;
2619
priceB = params.priceB;
2720
}
@@ -30,24 +23,20 @@ contract MaglevConstantSum is MaglevBase {
3023
return (r0 * priceA) + (r1 * priceB);
3124
}
3225

33-
function verify(uint256 amount0In, uint256 amount1In, uint256 newReserve0, uint256 newReserve1)
26+
function verify(uint256, uint256, uint256 newReserve0, uint256 newReserve1)
3427
internal
3528
view
3629
virtual
3730
override
3831
{
3932
uint256 kBefore = k(reserve0, reserve1);
40-
uint256 kAfter = k(newReserve0 - (amount0In * fee / 1e18), newReserve1 - (amount1In * fee / 1e18));
33+
uint256 kAfter = k(newReserve0, newReserve1);
4134
require(kAfter >= kBefore, KNotSatisfied());
4235
}
4336

4437
// FIXME: incorporate priceA and priceB
4538

46-
function computeQuote(uint256 amount, bool exactIn, bool) internal view virtual override returns (uint256) {
47-
if (exactIn) {
48-
return amount * (1e18 - fee) / 1e18;
49-
} else {
50-
return amount * 1e18 / (1e18 - fee);
51-
}
39+
function computeQuote(uint256 amount, bool, bool) internal view virtual override returns (uint256) {
40+
return amount;
5241
}
5342
}

src/MaglevEulerSwap.sol

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ contract MaglevEulerSwap is MaglevBase {
1010
uint256 public _py;
1111
uint256 public _cx;
1212
uint256 public _cy;
13-
uint256 public _fee;
1413

1514
error KNotSatisfied();
1615
error ReservesZero();
@@ -21,7 +20,6 @@ contract MaglevEulerSwap is MaglevBase {
2120
uint256 py;
2221
uint256 cx;
2322
uint256 cy;
24-
uint256 fee;
2523
}
2624

2725
constructor(BaseParams memory baseParams, EulerSwapParams memory params) MaglevBase(baseParams) {
@@ -33,8 +31,6 @@ contract MaglevEulerSwap is MaglevBase {
3331
_py = params.py;
3432
_cx = params.cx;
3533
_cy = params.cy;
36-
_fee = params.fee;
37-
//_fee = Math.max(params.fee, 0.0000000000001e18); // minimum fee required to compensate for rounding
3834
}
3935

4036
// FIXME: how to charge fees?
@@ -51,6 +47,8 @@ contract MaglevEulerSwap is MaglevBase {
5147
require(delta >= 0, KNotSatisfied());
5248
}
5349

50+
uint256 private constant roundingCompensation = 1.0000000000001e18;
51+
5452
function computeQuote(uint256 amount, bool exactIn, bool asset0IsInput)
5553
internal
5654
view
@@ -89,11 +87,11 @@ contract MaglevEulerSwap is MaglevBase {
8987
if (exactIn) {
9088
if (asset0IsInput) output = uint256(-dy);
9189
else output = uint256(-dx);
92-
output = output * 1e18 / (1e18 + _fee);
90+
output = output * 1e18 / roundingCompensation;
9391
} else {
9492
if (asset0IsInput) output = uint256(dx);
9593
else output = uint256(dy);
96-
output = output * (1e18 + _fee) / 1e18;
94+
output = output * roundingCompensation / 1e18;
9795
}
9896
}
9997

test/ConstantProduct.t.sol

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,17 +16,34 @@ contract ConstantProductTest is MaglevTestBase {
1616
function setUp() public virtual override {
1717
super.setUp();
1818

19+
createMaglev(50e18, 50e18, 0);
20+
}
21+
22+
function createMaglev(uint112 debtLimit0, uint112 debtLimit1, uint256 fee) internal {
1923
vm.prank(owner);
20-
maglev = new Maglev(_getMaglevBaseParams(), Maglev.ConstantProductParams({fee: 0}));
24+
maglev = new Maglev(getMaglevBaseParams(debtLimit0, debtLimit1, fee));
2125

2226
vm.prank(holder);
2327
evc.setAccountOperator(holder, address(maglev), true);
2428

2529
vm.prank(owner);
2630
maglev.configure();
31+
}
2732

28-
vm.prank(owner);
29-
maglev.setDebtLimit(50e18, 50e18);
33+
function test_basicExactIn() public monotonicHolderNAV {
34+
uint256 amount = 1e18;
35+
36+
assetTST.mint(address(this), amount);
37+
38+
uint256 q = maglev.quoteExactInput(address(assetTST), address(assetTST2), amount);
39+
40+
assetTST.transfer(address(maglev), amount);
41+
42+
vm.expectRevert(Maglev.KNotSatisfied.selector);
43+
maglev.swap(0, q+1, address(this), "");
44+
45+
maglev.swap(0, q, address(this), "");
46+
assertEq(assetTST2.balanceOf(address(this)), q);
3047
}
3148

3249
function test_fee_exactIn(uint256 amount, bool dir) public monotonicHolderNAV {
@@ -39,15 +56,20 @@ contract ConstantProductTest is MaglevTestBase {
3956

4057
t1.mint(address(this), amount);
4158

42-
uint256 qOrig = maglev.quoteExactInput(address(assetTST), address(assetTST2), amount);
59+
uint256 qOrig = maglev.quoteExactInput(address(assetTST), address(assetTST2), amount * 0.998e18 / 1e18);
4360

44-
vm.prank(owner);
45-
maglev.setConstantProductParams(Maglev.ConstantProductParams({fee: 0.002e18}));
61+
createMaglev(50e18, 50e18, 0.002e18);
4662

4763
uint256 q = maglev.quoteExactInput(address(assetTST), address(assetTST2), amount);
48-
assertApproxEqAbs(1e18 * q / qOrig, 0.998e18, 0.000000000001e18);
64+
65+
assertEq(qOrig, q);
4966

5067
t1.transfer(address(maglev), amount);
68+
69+
vm.expectRevert(Maglev.KNotSatisfied.selector);
70+
if (dir) maglev.swap(0, q+1, address(this), "");
71+
else maglev.swap(q+1, 0, address(this), "");
72+
5173
if (dir) maglev.swap(0, q, address(this), "");
5274
else maglev.swap(q, 0, address(this), "");
5375
assertEq(t2.balanceOf(address(this)), q);
@@ -72,7 +94,6 @@ contract ConstantProductTest is MaglevTestBase {
7294

7395
t2.transfer(address(maglev), q);
7496
if (dir) maglev.swap(amount - 2, 0, address(this), ""); // - 2 due to rounding
75-
7697
else maglev.swap(0, amount - 2, address(this), "");
7798

7899
uint256 q2 = maglev.quoteExactInput(address(t1), address(t2), amount);

0 commit comments

Comments
 (0)