Part 2

Zaros
PerpetualsDEXFoundrySolidity
70,000 USDC
View results
Submission Details
Severity: low
Valid

CurveAdapter uses non-existent exchange_with_best_rate() method, breaking fee conversion on Arbitrum.

Summary

The CurveAdapter contract calls the exchange_with_best_rate() method, however this method does not exist in the newer CurveRouterNG contract deployed on Arbitrum, causing all swap operations to revert and rendering the adapter non-functional.

Vulnerability Details

The issue exists in the CurveAdapter contract's swap execution logic. Specifically, the adapter attempts to call the non-existent exchange_with_best_rate() method.

The exchange_with_best_rate() method was part of Curve's outdated router, which is only deployed on Ethereum Mainnet. On Arbitrum (and other chains), the updated CurveRouterNG is deployed, which replaces this method with exchange(). This mismatch in method signatures and parameter structures will cause all calls to CurveAdapter.executeSwapExactInput() and CurveAdapter.executeSwapExactInputSingle() to revert on Arbitrum.

This issue is not observed during testing because the test suite uses a mock version of the Curve router (test/mocks/MockCurveStrategyRouter.sol).

Impact

All fee conversion operations via FeeDistributionBranch.convertAccumulatedFeesToWeth() that use Curve's pools will fail on Arbitrum. Disrupting core protocol functionality.

Tools Used

Manual Review.

Recommended Mitigation

Replace exchange_with_best_rate() with exchange() in the CurveAdapter contract. Align the parameters with the exchange() method's requirements. The new exchange() method requires different parameters, including a structured _route array and _swap_params parameters for pool routing, rather than the simplified _from/_to arguments from the old exchange_with_best_rate().

Example fixes:

diff --git a/CurveAdapter.sol b/CurveAdapter.mod.sol
index 09386b7..a0757b5 100644
--- a/CurveAdapter.sol
+++ b/CurveAdapter.mod.sol
@@ -89,9 +89,9 @@ contract CurveAdapter is BaseAdapter {
// Calculate the minimum acceptable output based on the slippage tolerance
uint256 amountOutMinimum = calculateAmountOutMin(expectedAmountOut);
- return ICurveSwapRouter(curveStrategyRouterCache).exchange_with_best_rate({
- _from: swapPayload.tokenIn,
- _to: swapPayload.tokenOut,
+ return ICurveSwapRouter(curveStrategyRouterCache).exchange({
+ _route: routeArray, // Structured swap route
+ _swap_params: swapParams, // Swap parameters
_amount: swapPayload.amountIn,
_expected: amountOutMinimum,
_receiver: swapPayload.recipient
@@ -128,9 +128,9 @@ contract CurveAdapter is BaseAdapter {
address receiver = (i == tokens.length - 2) ? swapPayload.recipient : address(th
is);
// make single exchange
- amountIn = ICurveSwapRouter(curveStrategyRouterCache).exchange_with_best_rat
e({
- _from: tokens[i],
- _to: tokens[i + 1],
+ amountIn = ICurveSwapRouter(curveStrategyRouterCache).exchange({
+ _route: routeArray, // Structured swap route
+ _swap_params: swapParams, // Swap parameters
_amount: amountIn,
_expected: amountOutMinimum,
_receiver: receiver
Updates

Lead Judging Commences

inallhonesty Lead Judge 5 months ago
Submission Judgement Published
Validated
Assigned finding tags:

Protocol uses CurveRegistryExchange which is outdated

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.