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

SIP-399: Perps V3 Flash Loan Utility #2039

Closed
wants to merge 3 commits into from
Closed
Changes from all commits
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
104 changes: 104 additions & 0 deletions content/sips/sip-399.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
---
sip: 399
title: Perps V3 - Flash Loan Utility
network: Base
barrasso marked this conversation as resolved.
Show resolved Hide resolved
status: Draft
type: Governance
author: 'MEB (@barrasso)'
created: 2024-07-16
---

## Simple Summary

Deploy a Flash Loan utility contract to enable users to close their Perps V3 positions in a single transaction.

## Abstract

This SIP proposes to deploy a new contract, `PerpsFlashLoanUtil`, which will allow users to close their Perps V3 positions by utilizing flash loans. This contract interacts with the Synthetix proxies and Uniswap's SwapRouter to facilitate the necessary operations. The contract also includes a utility function to set Uniswap pool addresses dynamically based on the collateral or margin type.

## Motivation

The motivation behind this proposal comes from requests by Perps integrators and partners who seek a better user experience when closing Perps positions. The current process can be cumbersome, requiring multiple steps and transactions. This flash loan utility contract aims to streamline the process, allowing users to close positions atomically, thereby improving efficiency and user satisfaction.

## Specification

### Overview

The `PerpsFlashLoanUtil` contract will allow users to close their Perps positions using flash loans from the Aave protocol. The process involves wrapping USDC to snxUSD, repaying debt, modifying collateral, unwrapping to the original collateral type, swapping to USDC, and repaying the flash loan.

### Rationale

The proposed solution leverages existing Synthetix and Uniswap contracts to facilitate the flash loan process. By using a flash loan, users can close their Perps positions without needing to front the necessary collateral. The contract allows for dynamic setting of Uniswap pool addresses and supporting various collateral types.

### Technical Specification

#### Interfaces

```solidity
/**
* @title Interface for the PerpsFlashLoanUtil contract.
* @notice Provides functions for requesting flash loans to close perps V3 positions.
*/
interface IPerpsFlashLoanUtil {
/**
* @notice Requests a flash loan.
* @param _amount The amount of USDC to flash loan.
* @param _collateralType The address of the collateral type to use.
* @param _marketId The ID of the market for the flash loan.
* @param _accountId The account ID to use for the flash loan.
*/
function requestFlashLoan(
uint256 _amount,
address _collateralType,
uint128 _marketId,
uint128 _accountId
) external;

/**
* @notice Executes the flash loan operation.
* @param asset The address of the asset being flash loaned.
* @param amount The amount of the asset being flash loaned.
* @param premium The premium to be paid for the flash loan.
* @param initiator The address that initiated the flash loan.
* @param params Additional parameters for the flash loan operation.
* @return success True if the operation was successful, false otherwise.
*/
function executeOperation(
Copy link
Member

Choose a reason for hiding this comment

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

Can you please tell me which argument is given to ensure that sandwishes are constrained?

Copy link
Member Author

Choose a reason for hiding this comment

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

Can you please tell me which argument is given to ensure that sandwishes are constrained?

amountOutMinimum: https://github.com/Synthetixio/synthetix-v3/blob/b8f01cdaa829f7630c2064ed5717c86d7c3c3dc3/auxiliary/PerpsFlashLoanUtil/contracts/PerpsFlashLoanUtil.sol#L119

which is derived from uniswap's IQuoter.quoteExactInput:
https://github.com/Uniswap/v3-periphery/blob/main/contracts/interfaces/IQuoter.sol

Copy link
Member

Choose a reason for hiding this comment

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

ohhh this amountOutMinimum is included in calldata?

Copy link
Member Author

Choose a reason for hiding this comment

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

ohhh this amountOutMinimum is included in calldata?

No, we fetch it from the IQuoter at L119 in executeOperation:

            uint256 amountOutMinimum = quoter.quoteExactInput(
                abi.encodePacked(collateralType, poolAddress, USDC),
                unwrappedAmount
            );

And then we use it to do the swap:

            ISwapRouter.ExactInputParams memory swapParams = ISwapRouter.ExactInputParams({
               path: abi.encodePacked(collateralType, poolAddress, USDC),
               recipient: address(this),
               deadline: block.timestamp,
               amountIn: unwrappedAmount,
               amountOutMinimum: amountOutMinimum
           });
           unwrappedAmount = router.exactInput(swapParams);

Copy link
Member

@kaleb-keny kaleb-keny Jul 17, 2024

Choose a reason for hiding this comment

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

Maybe i'm not clear, but how does the front-end is able to specify a minimum amount of USDC that he expects to receive for selling his wETH/wBTC margin so that he doesn't get rugged with a sandwish... Like to protect against this, the front-end gives a user a interface to specify a maximum amount of slippage against a centralized exchange price, that he is willing to accept.. If the return on the uniswap trade yields fewer USDC against the ETH that was to be sold all of it reverts... The uni/flash quoter, which uses on-chain data, I think ensures that the transaction goes through, but does not protect the trader against a sandwish attack which involves pushing uniswap prices around.

Copy link
Member Author

@barrasso barrasso Jul 19, 2024

Choose a reason for hiding this comment

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

Maybe i'm not clear, but how does the front-end is able to specify a minimum amount of USDC that he expects to receive for selling his wETH/wBTC margin so that he doesn't get rugged with a sandwish

Ah, I see what you mean. Can we mitigate this by quoting the min amount of USDC in requestFlashLoan, and pass it to params for execution? The user can be shown the amount of USDC expected to close their position at the time of requesting.

This way, the user requests a flash loan with a quoted amount in the first block. And then if the price moves down by the following block when the execution is processed, it can compare the initially quoted amount to a new quote and revert if outside the slippage range.

Copy link
Member

@kaleb-keny kaleb-keny Jul 22, 2024

Choose a reason for hiding this comment

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

Like let's do an example:

  • User requests a 1000 usdc flash loan to repay usdc of debt, having 1 ETH margin on hand (3k$)
  • Users flash 1000 usdc,
  • Swaps ETH to 2000 usdc,
  • Repays debt
  • Repays flash

At no point in this set of transaction is the rate of swap ETH to USDC is specified. Uness we can configure the maximum amount of ETH margin is allowed to be spent somewhere, or the rate of ETH to USDC,

address asset,
uint256 amount,
uint256 premium,
address initiator,
bytes calldata params
) external returns (bool success);

/**
* @notice Sets the Uniswap pool address for a given collateral type.
* @param _collateralType The address of the collateral type.
* @param _poolAddress The pool address to be set for the collateral type.
*/
function setPoolAddress(address _collateralType, address _poolAddress) external;
}
```

#### Contract Summary

- `setPoolAddress`: Allows the contract owner to set the Uniswap pool address for different collateral types.
- `requestFlashLoan`: Requests a flash loan and encodes the necessary parameters for closing a Perps position.
- `executeOperation`: Handles the flash loan callback, performing the necessary operations to close the Perps position and repay the flash loan.

### Test Cases

#### Initial State Tests

- Verify the contract owner can set pool addresses using `setPoolAddress`.

#### Flash Loan Util Tests

- Test `requestFlashLoan` with various parameters to ensure the correct encoding and decoding of parameters.
- Test `executeOperation` for different collateral types and ensure the flash loan is repaid correctly.
- Ensure the contract can handle scenarios where the collateral type is already USDC and no swap is necessary.
- Test the Uniswap swap functionality by mocking the Quoter and SwapRouter to verify the amountOutMinimum calculation and the exact input swap execution.

## Copyright

Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/).
Loading