Skip to content

Conversation

@thal0x
Copy link
Member

@thal0x thal0x commented Jun 10, 2025

Implements the VelodromeAdapter for swapping on Velodrome (and Aerodrome) pools.

There are 3 types of pools supported by Velodrome: Volatile, Stable, and Concentrated Liquidity. Volatile and Stable use the same interface, however Concentrated Liquidity does not.

Quotes:
The Quoter contract supports all pool types

Swapping:
For swapping all Concentrated Liquidity swaps go through their SwapRouter contract, while Volatile and Stable swaps are executed directly on the pools.

Things to note

Don't require getAmountOut and getAmountIn to be view methods

The Velodrome Quoter contract works by actually executing a swap and then gracefully reverting while still returning the output amount. It is intended to be called by using the CallContract RPC method to execute a contract call without submitting a transaction.

Note: Multicall contracts work similarly

Because of this, the IAdapter interface had to be updated to not expect getAmountOut and getAmountIn to also be view methods.

The API will need to be updated to execute the queries this way.

No getAmountIn support

The Velodrome Quoter also does not support exact out quotes. There are CosmWasm dexes with the same problem and for those we just revert when calling GetExactOut, so the same behavior is used here.

@thal0x thal0x self-assigned this Jun 10, 2025
@thal0x thal0x changed the title Jw/velodrome adapter [API-4314] Velodrome swap adapter Jun 10, 2025
@linear
Copy link

linear bot commented Jun 10, 2025

@thal0x thal0x marked this pull request as ready for review June 10, 2025 17:41
}

function _tickSpacing(PoolType poolType, address pool) internal view returns (int24) {
if (poolType == PoolType.V2) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

v2 pool types are just concentrated liquidity pools with fixed tick spacing? also are these tick spacing numbers likely to change?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No V2 pools are like standard xyk pools, they don't even have tick spacing it's just a constant used, there's a comment explaining a few lines down

function _swapV2Pool(address pool, address tokenIn, uint256 amountIn, uint256 amountOut) internal {
IERC20(tokenIn).safeTransfer(pool, amountIn);

bool zeroForOne = IPool(pool).token0() == tokenIn;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this checking how the pool orders the tokens?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The pools just have a token0 and token1, when swapping on a V2 pool you have to specify an amount0Out and amount1Out so you need to figure out whether you are receiving token0 or token1


bool zeroForOne = IPool(pool).token0() == tokenIn;

IPool(pool).swap(zeroForOne ? 0 : amountOut, zeroForOne ? amountOut : 0, address(this), "");
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i assume the second to last argument is the recipient of the swap, which will be swaprouter since this is invoked via delegate call?

what is the last argument?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct. And the last argument is a data parameter that gets passed to a swap callback. This is typically used in flash loans, so not relevant to us

revert("getAmountIn not supported for Velodrome");
}

function _swapV2Pool(address pool, address tokenIn, uint256 amountIn, uint256 amountOut) internal {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so high level -- this transfers the swap input to the velodrome pool contract and then the pool contract is called to execute the swap with the token balance it now has?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct

recipient: address(this),
deadline: block.timestamp,
amountIn: amountIn,
amountOutMinimum: 1,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we not need to specify some real amount in accordance to the slippage preference of the end user?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The slippage is checked at the end of all swaps in the entry point

@thal0x thal0x merged commit 010bcad into main Jun 16, 2025
1 check failed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants