@@ -36,6 +36,12 @@ abstract contract BaseTokenWrapperHook is BaseHook, DeltaResolver {
36
36
/// @dev Fee must be 0 as wrapper pools don't charge fees
37
37
error InvalidPoolFee ();
38
38
39
+ /// @notice Thrown when exact input swaps are not supported
40
+ error ExactInputNotSupported ();
41
+
42
+ /// @notice Thrown when exact output swaps are not supported
43
+ error ExactOutputNotSupported ();
44
+
39
45
/// @notice The wrapped token currency (e.g., WETH)
40
46
Currency public immutable wrapperCurrency;
41
47
@@ -118,27 +124,25 @@ abstract contract BaseTokenWrapperHook is BaseHook, DeltaResolver {
118
124
returns (bytes4 , BeforeSwapDelta swapDelta , uint24 )
119
125
{
120
126
bool isExactInput = params.amountSpecified < 0 ;
127
+ if (isExactInput && ! _supportsExactInput ()) revert ExactInputNotSupported ();
128
+ if (! isExactInput && ! _supportsExactOutput ()) revert ExactOutputNotSupported ();
121
129
122
130
if (wrapZeroForOne == params.zeroForOne) {
123
131
// we are wrapping
124
132
uint256 inputAmount =
125
133
isExactInput ? uint256 (- params.amountSpecified) : _getWrapInputRequired (uint256 (params.amountSpecified));
126
- _take (underlyingCurrency, address (this ), inputAmount);
127
- uint256 wrappedAmount = _deposit (inputAmount);
128
- _settle (wrapperCurrency, address (this ), wrappedAmount);
134
+ (uint256 actualUnderlyingAmount , uint256 wrappedAmount ) = _deposit (inputAmount);
129
135
int128 amountUnspecified =
130
- isExactInput ? - wrappedAmount.toInt256 ().toInt128 () : inputAmount .toInt256 ().toInt128 ();
136
+ isExactInput ? - wrappedAmount.toInt256 ().toInt128 () : actualUnderlyingAmount .toInt256 ().toInt128 ();
131
137
swapDelta = toBeforeSwapDelta (- params.amountSpecified.toInt128 (), amountUnspecified);
132
138
} else {
133
139
// we are unwrapping
134
140
uint256 inputAmount = isExactInput
135
141
? uint256 (- params.amountSpecified)
136
142
: _getUnwrapInputRequired (uint256 (params.amountSpecified));
137
- _take (wrapperCurrency, address (this ), inputAmount);
138
- uint256 unwrappedAmount = _withdraw (inputAmount);
139
- _settle (underlyingCurrency, address (this ), unwrappedAmount);
143
+ (uint256 actualWrappedAmount , uint256 unwrappedAmount ) = _withdraw (inputAmount);
140
144
int128 amountUnspecified =
141
- isExactInput ? - unwrappedAmount.toInt256 ().toInt128 () : inputAmount .toInt256 ().toInt128 ();
145
+ isExactInput ? - unwrappedAmount.toInt256 ().toInt128 () : actualWrappedAmount .toInt256 ().toInt128 ();
142
146
swapDelta = toBeforeSwapDelta (- params.amountSpecified.toInt128 (), amountUnspecified);
143
147
}
144
148
@@ -155,17 +159,29 @@ abstract contract BaseTokenWrapperHook is BaseHook, DeltaResolver {
155
159
156
160
/// @notice Deposits underlying tokens to receive wrapper tokens
157
161
/// @param underlyingAmount The amount of underlying tokens to deposit
162
+ /// @return actualUnderlyingAmount the actual number of underlying tokens used, i.e. to account for rebasing rounding errors
158
163
/// @return wrappedAmount The amount of wrapper tokens received
159
- /// @dev Implementing contracts should handle the wrapping operation
160
- /// The base contract will handle settling tokens with the pool manager
161
- function _deposit (uint256 underlyingAmount ) internal virtual returns (uint256 wrappedAmount );
164
+ /// @dev Implementing contracts should handle:
165
+ // - taking tokens from PoolManager
166
+ // - performing the wrapping operation
167
+ // - settling tokens on PoolManager
168
+ function _deposit (uint256 underlyingAmount )
169
+ internal
170
+ virtual
171
+ returns (uint256 actualUnderlyingAmount , uint256 wrappedAmount );
162
172
163
173
/// @notice Withdraws wrapper tokens to receive underlying tokens
164
174
/// @param wrappedAmount The amount of wrapper tokens to withdraw
175
+ /// @return actualWrappedAmount the actual number of wrapped tokens used, i.e. to account for rebasing rounding errors
165
176
/// @return underlyingAmount The amount of underlying tokens received
166
- /// @dev Implementing contracts should handle the unwrapping operation
167
- /// The base contract will handle settling tokens with the pool manager
168
- function _withdraw (uint256 wrappedAmount ) internal virtual returns (uint256 underlyingAmount );
177
+ /// @dev Implementing contracts should handle:
178
+ // - taking tokens from PoolManager
179
+ // - performing the unwrapping operation
180
+ // - settling tokens on PoolManager
181
+ function _withdraw (uint256 wrappedAmount )
182
+ internal
183
+ virtual
184
+ returns (uint256 actualWrappedAmount , uint256 underlyingAmount );
169
185
170
186
/// @notice Calculates underlying tokens needed to receive desired wrapper tokens
171
187
/// @param wrappedAmount The desired amount of wrapper tokens
@@ -184,4 +200,18 @@ abstract contract BaseTokenWrapperHook is BaseHook, DeltaResolver {
184
200
function _getUnwrapInputRequired (uint256 underlyingAmount ) internal view virtual returns (uint256 ) {
185
201
return underlyingAmount;
186
202
}
203
+
204
+ /// @notice Indicates whether the hook supports exact output swaps
205
+ /// @dev Default implementation returns true
206
+ /// @dev Override for wrappers that cannot support exact output swaps
207
+ function _supportsExactOutput () internal view virtual returns (bool ) {
208
+ return true ;
209
+ }
210
+
211
+ /// @notice Indicates whether the hook supports exact input swaps
212
+ /// @dev Default implementation returns true
213
+ /// @dev Override for wrappers that cannot support exact input swaps
214
+ function _supportsExactInput () internal view virtual returns (bool ) {
215
+ return true ;
216
+ }
187
217
}
0 commit comments