Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: (WIP) Simplified PropellerRouter Sketch #127

Draft
wants to merge 6 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
refactor: no more mention of "checked" - check by default
- All methods are now checked by default. Merge checked and unchecked internal methods for simplicity.
- Also remove the remaining _executeAction which was only used for batch execution, as well as the ActionType.
  • Loading branch information
TAMARA LIPOWSKI committed Dec 20, 2024
commit 4e75f98113d2555e193412d579f870d5b4570157
3 changes: 3 additions & 0 deletions evm/src/propeller-router/CallbackVerificationDispatcher.sol
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@ contract CallbackVerificationDispatcher is PropellerRouterStructs {

/**
* @dev Calls a callback verifier. This should revert if the callback verification fails.
* TODO check if we even still need the GenericCallbackHeader if we are hard-coding
* the callbacks for USV3 (which we can do since we don't care about gas and are
* prioritizing simplicity)
* This function returns the offset of the GenericCallbackHeader in data.
* This offset depends on the protocol and is required to parse the callback data in the fallback function.
*/
Expand Down
20 changes: 10 additions & 10 deletions evm/src/propeller-router/PropellerRouter.sol
Original file line number Diff line number Diff line change
Expand Up @@ -66,27 +66,27 @@ contract PropellerRouter is
);
}

function singleExactInChecked(uint256 givenAmount, bytes calldata swap)
function singleExactIn(uint256 givenAmount, bytes calldata swap)
external
override
onlyRole(EXECUTOR_ROLE)
withSwapContext
returns (uint256 calculatedAmount)
{
calculatedAmount = _singleExactInChecked(givenAmount, swap);
calculatedAmount = _singleExactIn(givenAmount, swap);
}

function singleExactOutChecked(uint256 givenAmount, bytes calldata swap)
function singleExactOut(uint256 givenAmount, bytes calldata swap)
external
override
onlyRole(EXECUTOR_ROLE)
withSwapContext
returns (uint256 calculatedAmount)
{
calculatedAmount = _singleExactOutChecked(givenAmount, swap);
calculatedAmount = _singleExactOut(givenAmount, swap);
}

function sequentialExactInChecked(
function sequentialExactIn(
uint256 givenAmount,
uint256 minUserAmount,
bytes calldata swaps
Expand All @@ -98,10 +98,10 @@ contract PropellerRouter is
returns (uint256 calculatedAmount)
{
calculatedAmount =
_sequentialExactInChecked(givenAmount, minUserAmount, swaps);
_sequentialExactIn(givenAmount, minUserAmount, swaps);
}

function sequentialExactOutChecked(
function sequentialExactOut(
uint256 givenAmount,
uint256 maxUserAmount,
bytes[] calldata swaps
Expand All @@ -113,10 +113,10 @@ contract PropellerRouter is
returns (uint256 calculatedAmount)
{
calculatedAmount =
_sequentialExactOutChecked(givenAmount, maxUserAmount, swaps);
_sequentialExactOut(givenAmount, maxUserAmount, swaps);
}

function splitExactInChecked(
function splitExactIn(
uint256 amount,
uint256 minUserAmount,
SplitSwapExactInParameters calldata parameters
Expand All @@ -127,7 +127,7 @@ contract PropellerRouter is
withSwapContext
returns (uint256 amountOut)
{
amountOut = _splitExactInChecked(
amountOut = _splitExactIn(
amount, minUserAmount, parameters.nTokens, parameters.swaps
);
}
Expand Down
118 changes: 23 additions & 95 deletions evm/src/propeller-router/PropellerRouterInternal.sol
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ abstract contract PropellerRouterInternal is PropellerRouterStructs {
_executeSwap(swap.exchange(), amount, swap.protocolData());
}

function _singleExactOutChecked(uint256 amount, bytes calldata data)
function _singleExactOut(uint256 amount, bytes calldata data)
internal
returns (uint256 calculatedAmount)
{
Expand All @@ -75,7 +75,7 @@ abstract contract PropellerRouterInternal is PropellerRouterStructs {
}
}

function _singleExactInChecked(uint256 amount, bytes calldata data)
function _singleExactIn(uint256 amount, bytes calldata data)
internal
returns (uint256 calculatedAmount)
{
Expand All @@ -96,9 +96,14 @@ abstract contract PropellerRouterInternal is PropellerRouterStructs {
}

/**
* @dev Executes a sequence of exact in swaps
* @dev Executes a sequence of exact in swaps, checking that the user gets more
* than minUserAmount of buyToken.
*/
function _sequentialSwapExactIn(uint256 givenAmount, bytes calldata swaps)
function _sequentialSwapExactIn(
uint256 givenAmount,
uint256 minUserAmount,
bytes calldata swaps
)
internal
returns (uint256 calculatedAmount)
{
Expand All @@ -112,20 +117,25 @@ abstract contract PropellerRouterInternal is PropellerRouterStructs {
calculatedAmount =
_executeSwap(exchange, calculatedAmount, swap.protocolData());
}
if (calculatedAmount < minUserAmount) {
revert NegativeSlippage(calculatedAmount, minUserAmount);
}
}

/**
* @dev Executes a sequence of exact out swaps, by first quoting
* backwards and then executing with corrected amounts a
* sequential exactIn swap
*
* This method checks that the user spends no more than maxUserAmount of sellToken
* Note: All used executors must implement ISwapQuoter, for this
* method to work correctly.
*/
function _sequentialSwapExactOut(
Copy link
Contributor

Choose a reason for hiding this comment

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

is this the best way to do this?? I have no idea how to do exact outs. but this seems rather hacky 😕

Copy link
Author

Choose a reason for hiding this comment

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

hmmm we can consider redesigning this in january - I don't think this way is so bad though

uint256 givenAmount,
uint256 maxUserAmount,
bytes[] calldata swaps
) internal returns (uint256) {
) internal returns (uint256 calculatedAmount) {
// Idea: On v2, reserve 14 bytes for calculatedAmount and replace them here
// to save some quotes, if these 14 bytes are all zero the swap call won't
// recalculate the quote else, it will simply execute with the calculatedAmount
Expand All @@ -147,67 +157,26 @@ abstract contract PropellerRouterInternal is PropellerRouterStructs {
swap = swaps[i];
_executeSwap(swap.exchange(), amounts[i + 1], swap.protocolData());
}
return amounts[0];
}
calculatedAmount = amounts[0];

/**
* @dev Same as sequentialExactOut but checks that the user
* spends less than maxUserAmount of sellToken.
*/
function _sequentialExactOutChecked(
uint256 givenAmount,
uint256 maxUserAmount,
bytes[] calldata swaps
) internal returns (uint256 calculatedAmount) {
calculatedAmount = _sequentialSwapExactOut(givenAmount, swaps);
if (calculatedAmount > maxUserAmount) {
revert NegativeSlippage(calculatedAmount, maxUserAmount);
}
}

/**
* @dev same as sequentialExactIn but checks that the user gets
* more than minUserAmount of buyToken.
*/
function _sequentialExactInChecked(
uint256 givenAmount,
uint256 minUserAmount,
bytes calldata swaps
) internal returns (uint256 calculatedAmount) {
calculatedAmount = _sequentialSwapExactIn(givenAmount, swaps);
if (calculatedAmount < minUserAmount) {
revert NegativeSlippage(calculatedAmount, minUserAmount);
}
}

/**
* @dev same as _splitSwapWithDataInclToken but checks that the user gets
* more than minUserAmount of buyToken.
*/
function _splitExactInChecked(
uint256 amountIn,
uint256 minUserAmount,
uint256 nTokens,
bytes calldata swaps_
) internal returns (uint256 calculatedAmount) {
calculatedAmount =
_splitSwapExactIn(amountIn, nTokens, swaps_);
if (calculatedAmount < minUserAmount) {
revert NegativeSlippage(calculatedAmount, minUserAmount);
}
}
/**
* @dev Executes a swap graph with internal splits token amount
* splits.
* splits, checking that the user gets more than minUserAmount of buyToken.
*
* Assumes the swaps in swaps_ already contain any required token
* addresses.
*/
function _splitSwapExactIn(
uint256 amountIn,
uint256 minUserAmount,
uint256 nTokens,
bytes calldata swaps_
) internal returns (uint256) {
) internal returns (uint256 calculatedAmount) {
uint256 currentAmountIn;
uint256 currentAmountOut;
uint8 tokenInIndex;
Expand Down Expand Up @@ -236,7 +205,10 @@ abstract contract PropellerRouterInternal is PropellerRouterStructs {
remainingAmounts[tokenOutIndex] += currentAmountOut;
remainingAmounts[tokenInIndex] -= currentAmountIn;
}
return amounts[tokenOutIndex];
calculatedAmount = amounts[tokenOutIndex];
if (calculatedAmount < minUserAmount) {
revert NegativeSlippage(calculatedAmount, minUserAmount);
}
}

/**
Expand Down Expand Up @@ -276,48 +248,4 @@ abstract contract PropellerRouterInternal is PropellerRouterStructs {
}
}

/**
* @dev Allows to route amount into any other action type supported
* by this contract. This allows for more flexibility during
* batchExecute or within callbacks.
* @param amount the amount to forward into the next action
* @param type_ what kind of action to take
* @param actionData data with the encoding for each action. See the
* individual methods for more information.
*/
function _executeAction(
uint256 amount,
ActionType type_,
bytes calldata actionData
) internal returns (uint256 calculatedAmount) {
if (type_ == ActionType.SINGLE_IN_CHECKED) {
calculatedAmount = _singleExactInChecked(amount, actionData);
} else if (type_ == ActionType.SINGLE_OUT_CHECKED) {
calculatedAmount = _singleExactOutChecked(amount, actionData);
} else if (type_ == ActionType.SEQUENTIAL_IN_CHECKED) {
(uint256 checkAmount, bytes calldata swaps) =
actionData.decodeAmountAndBytes();
calculatedAmount =
_sequentialExactInChecked(amount, checkAmount, swaps);
} else if (type_ == ActionType.SEQUENTIAL_OUT_CHECKED) {
(uint256 checkAmount, bytes[] calldata swaps) =
actionData.decodeAmountAndSwapArray();
calculatedAmount =
_sequentialExactOutChecked(amount, checkAmount, swaps);
} else if (type_ == ActionType.SPLIT_EXACT_IN_CHECKED) {
(
uint256 checkAmount,
IERC20[] calldata tokens,
bytes[] calldata swaps
) = actionData.decodeSplitSwapWithTokenArrayCheckedArgs();
calculatedAmount = _splitSwapWithoutTokensChecked(
amount, checkAmount, tokens, swaps
);
}
// No more action type = transfer. AFAIK this was used for USV3 callbacks
// and is unnecessary if we simplify the USV3 executor logic.
else {
revert UnsupportedBatchData(uint8(type_));
}
}
}
22 changes: 0 additions & 22 deletions evm/src/propeller-router/PropellerRouterStructs.sol
Original file line number Diff line number Diff line change
Expand Up @@ -62,26 +62,4 @@ interface PropellerRouterStructs {
address[] addresses;
uint256 allowance;
}

/**
* @dev header for generic callback
* `tokenOwed` the token we need to pay the pool with
* `supplyActionType` the type of action to do with amountOwed
* `forwardActionType` the type of action to do with amountReceived
*/
struct GenericCallbackHeader {
IERC20 tokenOwed;
ActionType supplyActionType;
ActionType forwardActionType;
}

enum ActionType {
SINGLE_IN_CHECKED,
SINGLE_OUT_CHECKED,
SEQUENTIAL_OUT_CHECKED,
SEQUENTIAL_IN_CHECKED,
SPLIT_EXACT_IN_CHECKED,
WRAP,
UNWRAP
}
}
Loading