@@ -55,11 +55,16 @@ contract EulerSwapHook is EulerSwap, BaseHook {
55
55
params.zeroForOne ? (key.currency0, key.currency1) : (key.currency1, key.currency0);
56
56
bool isExactInput = params.amountSpecified < 0 ;
57
57
58
- // TODO: compute the open side of the trade, using computeQuote() ?
59
58
uint256 amountIn;
60
59
uint256 amountOut;
61
- // uint256 amountIn = isExactInput ? uint256(-params.amountSpecified) : computeQuote(..., false);
62
- // uint256 amountOut = isExactInput ? computeQuote(..., true) : uint256(params.amountSpecified);
60
+
61
+ if (isExactInput) {
62
+ amountIn = uint256 (- params.amountSpecified);
63
+ amountOut = computeQuote (params.zeroForOne, uint256 (- params.amountSpecified), true );
64
+ } else {
65
+ amountIn = computeQuote (params.zeroForOne, uint256 (params.amountSpecified), false );
66
+ amountOut = uint256 (params.amountSpecified);
67
+ }
63
68
64
69
// take the input token, from the PoolManager to the Euler vault
65
70
// the debt will be paid by the swapper via the swap router
@@ -100,4 +105,124 @@ contract EulerSwapHook is EulerSwap, BaseHook {
100
105
// TODO: fix salt mining & verification for the hook
101
106
function getHookPermissions () public pure override returns (Hooks.Permissions memory ) {}
102
107
function validateHookAddress (BaseHook) internal pure override {}
108
+
109
+ error SwapLimitExceeded ();
110
+ error OperatorNotInstalled ();
111
+
112
+ function computeQuote (bool asset0IsInput , uint256 amount , bool exactIn ) internal view returns (uint256 ) {
113
+ require (evc.isAccountOperatorAuthorized (eulerAccount, address (this )), OperatorNotInstalled ());
114
+ require (amount <= type (uint112 ).max, SwapLimitExceeded ());
115
+
116
+ // exactIn: decrease received amountIn, rounding down
117
+ if (exactIn) amount = amount * feeMultiplier / 1e18 ;
118
+
119
+ (uint256 inLimit , uint256 outLimit ) = calcLimits (asset0IsInput);
120
+
121
+ uint256 quote = binarySearch (amount, exactIn, asset0IsInput);
122
+
123
+ if (exactIn) {
124
+ // if `exactIn`, `quote` is the amount of assets to buy from the AMM
125
+ require (amount <= inLimit && quote <= outLimit, SwapLimitExceeded ());
126
+ } else {
127
+ // if `!exactIn`, `amount` is the amount of assets to buy from the AMM
128
+ require (amount <= outLimit && quote <= inLimit, SwapLimitExceeded ());
129
+ }
130
+
131
+ // exactOut: increase required quote(amountIn), rounding up
132
+ if (! exactIn) quote = (quote * 1e18 + (feeMultiplier - 1 )) / feeMultiplier;
133
+
134
+ return quote;
135
+ }
136
+
137
+ function binarySearch (uint256 amount , bool exactIn , bool asset0IsInput ) internal view returns (uint256 output ) {
138
+ int256 dx;
139
+ int256 dy;
140
+
141
+ if (exactIn) {
142
+ if (asset0IsInput) dx = int256 (amount);
143
+ else dy = int256 (amount);
144
+ } else {
145
+ if (asset0IsInput) dy = - int256 (amount);
146
+ else dx = - int256 (amount);
147
+ }
148
+
149
+ unchecked {
150
+ int256 reserve0New = int256 (uint256 (reserve0)) + dx;
151
+ int256 reserve1New = int256 (uint256 (reserve1)) + dy;
152
+ require (reserve0New > 0 && reserve1New > 0 , SwapLimitExceeded ());
153
+
154
+ uint256 low;
155
+ uint256 high = type (uint112 ).max;
156
+
157
+ while (low < high) {
158
+ uint256 mid = (low + high) / 2 ;
159
+ require (mid > 0 , SwapLimitExceeded ());
160
+ (uint256 a , uint256 b ) = dy == 0 ? (uint256 (reserve0New), mid) : (mid, uint256 (reserve1New));
161
+ if (verify (a, b)) {
162
+ high = mid;
163
+ } else {
164
+ low = mid + 1 ;
165
+ }
166
+ }
167
+
168
+ require (high < type (uint112 ).max, SwapLimitExceeded ()); // at least one point verified
169
+
170
+ if (dx != 0 ) dy = int256 (low) - reserve1New;
171
+ else dx = int256 (low) - reserve0New;
172
+ }
173
+
174
+ if (exactIn) {
175
+ if (asset0IsInput) output = uint256 (- dy);
176
+ else output = uint256 (- dx);
177
+ } else {
178
+ if (asset0IsInput) output = dx >= 0 ? uint256 (dx) : 0 ;
179
+ else output = dy >= 0 ? uint256 (dy) : 0 ;
180
+ }
181
+ }
182
+
183
+ function calcLimits (bool asset0IsInput ) internal view returns (uint256 , uint256 ) {
184
+ uint256 inLimit = type (uint112 ).max;
185
+ uint256 outLimit = type (uint112 ).max;
186
+
187
+ (IEVault vault0 , IEVault vault1 ) = (IEVault (vault0), IEVault (vault1));
188
+ // Supply caps on input
189
+ {
190
+ IEVault vault = (asset0IsInput ? vault0 : vault1);
191
+ uint256 maxDeposit = vault.debtOf (eulerAccount) + vault.maxDeposit (eulerAccount);
192
+ if (maxDeposit < inLimit) inLimit = maxDeposit;
193
+ }
194
+
195
+ // Remaining reserves of output
196
+ {
197
+ uint112 reserveLimit = asset0IsInput ? reserve1 : reserve0;
198
+ if (reserveLimit < outLimit) outLimit = reserveLimit;
199
+ }
200
+
201
+ // Remaining cash and borrow caps in output
202
+ {
203
+ IEVault vault = (asset0IsInput ? vault1 : vault0);
204
+
205
+ uint256 cash = vault.cash ();
206
+ if (cash < outLimit) outLimit = cash;
207
+
208
+ (, uint16 borrowCap ) = vault.caps ();
209
+ uint256 maxWithdraw = decodeCap (uint256 (borrowCap));
210
+ maxWithdraw = vault.totalBorrows () > maxWithdraw ? 0 : maxWithdraw - vault.totalBorrows ();
211
+ if (maxWithdraw > cash) maxWithdraw = cash;
212
+ maxWithdraw += vault.convertToAssets (vault.balanceOf (eulerAccount));
213
+ if (maxWithdraw < outLimit) outLimit = maxWithdraw;
214
+ }
215
+
216
+ return (inLimit, outLimit);
217
+ }
218
+
219
+ function decodeCap (uint256 amountCap ) internal pure returns (uint256 ) {
220
+ if (amountCap == 0 ) return type (uint256 ).max;
221
+
222
+ unchecked {
223
+ // Cannot overflow because this is less than 2**256:
224
+ // 10**(2**6 - 1) * (2**10 - 1) = 1.023e+66
225
+ return 10 ** (amountCap & 63 ) * (amountCap >> 6 ) / 100 ;
226
+ }
227
+ }
103
228
}
0 commit comments