This repository was archived by the owner on Jan 18, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 1
STIP-003 LeverageTokenExchangeIssuance #6
Open
ncitron
wants to merge
10
commits into
main
Choose a base branch
from
ncitron/debt-exchange-issuance
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
10 commits
Select commit
Hold shift + click to select a range
d7b1bc8
checkpoint 1
ncitron d4d4279
fix timeline formatting
ncitron 5f4e11c
review changes
ncitron c3ee250
checkpoint 2
ncitron ebd5788
fix math
ncitron 7414700
change name
ncitron 98bfb57
Update STIP-003.md
richardliang e79a544
checkpoint 3
ncitron 9a8075e
Merge branch 'ncitron/debt-exchange-issuance' of github.com:SetProtoc…
ncitron 12cd99a
clean up
ncitron File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,204 @@ | ||
# STIP-003 | ||
## Abstract | ||
The `DebtIssuance` module for issuing leveraged products has poor UX. For issuing, users initially pay a greater value of the collateral asset than the received leverage token value, and returns an additional refund denominated in the borrow asset. For redeeming, users must pay off all debt for the Set token by providing an additional amount of the borrow asset along with the leveraged token. | ||
|
||
## Motivation | ||
A new exchange issuance contract can significantly improve UX, with the downside of increased gas costs. In short we would allow users to issue Sets by sending the exact price of the amount they wish to issue. It would remove the need to send extra funds, and the user would not receive a refund at the end. For redemption, users would not have to manually pay off the debt of the leveraged token. | ||
|
||
This feature has two main uses. First, large whales may prefer the better UX, especially since the increased gas costs are negligible compared to their trade size. An additional use case for this feature is for leveraged products issued on Polygon. Since the gas price is so low on Polygon, we can perform issuance and redemption using this contract for regular users. This removes the need to fund large liquidity mining campaigns to incentivize liquidity provision. | ||
|
||
## Background Information | ||
Flash loan notes | ||
- Aave is one of the largest issuers of flash loans. | ||
- They currently provide this service on both Ethereum and Polygon. | ||
|
||
Leverage token notes: | ||
- For exact input issuances and exact output redemptions we need to call sync on `CompoundLeverageModule` before calculating the price | ||
- For Aave and Compound, we need different ways to fetch the values of the underlying asset. In Aave, it is a 1 to 1 relationship. In Compound, we need to call `balanceOfUnderlying` on the CToken contract. | ||
- Before fetching the required collateral and debt amounts for a leveraged token, we must call sync on the associated leverage module. | ||
|
||
|
||
Useful links: | ||
Aave flash loans: https://docs.aave.com/developers/guides/flash-loans | ||
DebtIssuanceModule: https://github.com/SetProtocol/set-protocol-v2/blob/master/contracts/protocol/modules/DebtIssuanceModule.sol | ||
ExchangeIssuance: https://github.com/SetProtocol/index-coop-smart-contracts/blob/master/contracts/exchangeIssuance/ExchangeIssuance.sol | ||
|
||
## Open Questions | ||
- How to we calculate the issuance amount for an exact input issuance. | ||
- How to we calculate the redemption amount for an exact output redemption | ||
ncitron marked this conversation as resolved.
Show resolved
Hide resolved
|
||
- Can we assume the leveraged tokens only have one debt position? | ||
ncitron marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
## Feasibility Analysis | ||
Solution 1: Aave flash loans with a Periphery smart contract | ||
In this solution we would write a periphery contract called to abstract away the interactions with `DebtIssuanceModule`. | ||
|
||
Issuance: | ||
- user sends an input token | ||
- entire amount of the input token is swapped into the collateral asset (if collateral asset is not equal to input asset) | ||
ncitron marked this conversation as resolved.
Show resolved
Hide resolved
|
||
- sync called on leverage module | ||
- the amount of additional collateral including Uniswap and Aave flash loan fees needed is calculated | ||
- the additional amount needed is flash loaned from Aave | ||
ncitron marked this conversation as resolved.
Show resolved
Hide resolved
|
||
- collateral is wrapped into CToken / AToken | ||
- Issuance is performed | ||
- Refund amount is swapped back to collateral asset | ||
- Aave flash loan is repaid | ||
|
||
Redemption: | ||
- user sends input leveraged token | ||
- sync called on leverage module | ||
- calculate amount of borrow asset required to redeem including flash loan and Uniswap fees | ||
- flash loan borrow asset amount from Aave | ||
- redeem leveraged token | ||
- unwrap CToken / AToken | ||
- sell some of returned collateral asset back to the borrow asset | ||
ncitron marked this conversation as resolved.
Show resolved
Hide resolved
|
||
- pay back Aave flash loan | ||
- swap the rest of the collateral asset into the output asset (if borrow asset is not equal to output asset) | ||
- send output asset to user | ||
|
||
Calculating the required amount to flash loan for an exact input issuance is quite complicated. For an overview of how to derive the required equations, take a look at [this pdf](../assets/STIP-003/debt_exchange_issuance_math.pdf) | ||
|
||
Solution 2: New issuance module | ||
In this solution, we would write a new `DebtIssuanceModule` that does much of this internal accounting itself. This removes the need to perform redundant syncs and swaps to pay off debt or to collect refunds. To do this, very careful accounting would be needed to ensure that these actions do not change the leverage ratios by too much. If the leverage ratios are changed by a certain unacceptable amount, then it would perform a correcting rebalance directly before the issuance or redemption. While this solution may require significantly less gas for most users, it comes at the cost of increased complexity and risk. For that reason, I do not recommend we go forward with this solution. | ||
|
||
## Timeline | ||
Spec: 7/23 | ||
Implementation: 7/28 | ||
ncitron marked this conversation as resolved.
Show resolved
Hide resolved
|
||
Internal audit: 8/6 | ||
Audit: 8/8 | ||
Staging deployment and tests: 8/12 | ||
Production deployment: 8/15 | ||
|
||
## Checkpoint 1 | ||
**Reviewer**: @richardliang | ||
|
||
## Proposed Architecture Changes | ||
A new contract will be built, called `LeverageTokenExchangeIssuance` which can be utilized to issue basic leveraged tokens that contain just one collateral asset and one debt position. This contract interfaces with the `DebtIssuanceModule`, `CompoundLeverageModule` and `AaveLeverageModule`. Since it is a periphery contract, it will not be a dependency of any core Set Protocol systems, nor will it have any privileged access. | ||
|
||
## Requirements | ||
- exact input issuance | ||
- exact output issuance | ||
- exact input redemption | ||
- users can use above functions with ERC20s | ||
- users can get quotes for the above trades | ||
- users can supply exchanges and trade paths for the input/out token -> collateral/debt trades | ||
- users can supply exchanges and trade paths for the debt -> collateral trades | ||
- trade paths can be up to three in length | ||
- works for set tokens with a single collateral and debt position (only basic leveraged tokens) | ||
- produces 0 dust | ||
|
||
## User Flows | ||
User wants to use 1000 DAI to purchase at last 20 ETH2xFLI | ||
- user approves at least 100 DAI to `LeverageTokenExchangeIssuance` | ||
- user calls `issueExactInput` on `LeverageTokenExchangeIssuance` | ||
- `LeverageTokenExchangeIssuance` returns as much ETH2xFLI as it can issue with the full 1000 DAI | ||
- if the purchase amount is below 20, revert | ||
|
||
User want to issue 20 ETH2xFLI for at max 0.7 WETH | ||
- user calls `issueExactOutputETH` on `LeverageTokenExchangeIssuance` with a msg.value of 0.7 | ||
- `LeverageTokenExchangeIssuance` issues 20 ETH2xFLI with the sent ETH | ||
- `LeverageTokenExchangeIssuance` returns 20 ETH2xFLI and refunds the unused ETH | ||
- if the issuance costs more than 0.7 ETH, revert | ||
|
||
User wants to redeem 10 BTC2xFLI and receive a minimum of 300 USDC | ||
- user calls `redeemExactInput` on `LeverageTokenExchangeIssuance` | ||
- `LeverageTokenExchangeIssuance` redeems 10 BTC2xFLI | ||
- `LeverageTokenExchangeIssuance` returns proceeds of the redemption in USDC | ||
- if USDC proceeds are less than 300, revert | ||
|
||
## Checkpoint 2 | ||
**Reviewer**: @richardliang | ||
|
||
Reviewer: [] | ||
## Specification | ||
### LeverageTokenExchangeIssuance | ||
#### Public Variables | ||
| Type | Name | Description | | ||
|------ |------ |------------- | | ||
|ILeverageModule|CompoundLeverageModule|leverage module address| | ||
|ILeverageModule|AaveLeverageModule|leverage module address| | ||
#### Functions | ||
| Name | Caller | Description | | ||
|------ |------ |------------- | | ||
|issueExactInput|trader|exact input issuance| | ||
|issueExactOutput|trader|exact output issuance| | ||
|redeemExactInput|trader|exact input redemption| | ||
|getIssueExactInput|trader|exact input issuance quote| | ||
|getIssueExactOutput|trader|exact output issuance quote| | ||
|getRedeemExactInput|trader|exact input redemption quote| | ||
#### Functions | ||
> issueExactInput(SetToken _setToken, uint256 _amountIn, uint256, _minOut, IERC20 _inputToken, IRouter _inputSwapRouter, address[] memory _inputSwapPath, IRouter _debtSwapRouter, address[] memory _debtSwapPath) external returns (uint256) | ||
- _setToken: set token to issue | ||
- _amountIn: input token amount | ||
- _minOut: minimum output amount | ||
- _inputToken: input token to be used by contract | ||
- _inputSwapRouter: Uniswap V2 router address to perform the swap from the input token to collateral token | ||
- _inputSwapPath: trade path for the swap from the input token to collateral token | ||
- _debtSwapRouter: Uniswap V2 router address to perform the swap from the debt token to collateral token | ||
- _debtSwapPath: trade path for the swap from the debt token to collateral token | ||
- returns: output amount | ||
|
||
> issueExactOutput(SetToken _setToken, uint256 _amountOut, uint256, _maxIn, IERC20 _inputToken, IRouter _inputSwapRouter, address[] memory _inputSwapPath, IRouter _debtSwapRouter, address[] memory _debtSwapPath) external returns (uint256) | ||
- _setToken: set token to issue | ||
- _amountOut: output set amount | ||
- _maxIn: maximum input amount | ||
- _inputToken: input token to be used by contract | ||
- _inputSwapRouter: Uniswap V2 router address to perform the swap from the input token to collateral token | ||
- _inputSwapPath: trade path for the swap from the input token to collateral token | ||
- _debtSwapRouter: Uniswap V2 router address to perform the swap from the debt token to collateral token | ||
- _debtSwapPath: trade path for the swap from the debt token to collateral token | ||
- returns: input amount | ||
|
||
> redeemExactInput(SetToken _setToken, uint256 _amountIn, uint256, _minOut, IERC20 _outputToken, IRouter _outputSwapRouter, address[] memory _outputSwapPath, IRouter _debtSwapRouter, address[] memory _debtSwapPath) external returns (uint256) | ||
- _setToken: set token to issue | ||
- _amountIn: input set amount | ||
- _minOut: minimum output amount | ||
- _outputToken: output token to be received by user | ||
- _outputSwapRouter: Uniswap V2 router address to perform the swap from the collateral token to the output token | ||
- _outputSwapPath: trade path for the swap from the collateral token to the output token | ||
- _debtSwapRouter: Uniswap V2 router address to perform the swap from the collateral token to the debt token | ||
- _debtSwapPath: trade path for the swap from the collateral token to the debt token | ||
- returns: output amount | ||
|
||
> getIssueExactInput(SetToken _setToken, uint256 _amountIn, IERC20 _inputToken, IRouter _inputSwapRouter, address[] memory _inputSwapPath, IRouter _debtSwapRouter, address[] memory _debtSwapPath) external view returns (uint256) | ||
- _setToken: set token to issue | ||
- _amountIn: input token amount | ||
- _inputToken: input token to be used by contract | ||
- _inputSwapRouter: Uniswap V2 router address to perform the swap from the input token to collateral token | ||
- _inputSwapPath: trade path for the swap from the input token to collateral token | ||
- _debtSwapRouter: Uniswap V2 router address to perform the swap from the debt token to collateral token | ||
- _debtSwapPath: trade path for the swap from the debt token to collateral token | ||
- returns: expected output amount | ||
|
||
> getIssueExactOutput(SetToken _setToken, uint256 _amountOut, IERC20 _inputToken, IRouter _inputSwapRouter, address[] memory _inputSwapPath, IRouter _debtSwapRouter, address[] memory _debtSwapPath) external view returns (uint256) | ||
- _setToken: set token to issue | ||
- _amountOut: output set amount | ||
- _inputToken: input token to be used by contract | ||
- _inputSwapRouter: Uniswap V2 router address to perform the swap from the input token to collateral token | ||
- _inputSwapPath: trade path for the swap from the input token to collateral token | ||
- _debtSwapRouter: Uniswap V2 router address to perform the swap from the debt token to collateral token | ||
- _debtSwapPath: trade path for the swap from the debt token to collateral token | ||
- returns: expected input amount | ||
|
||
> getRedeemExactInput(SetToken _setToken, uint256 _amountIn, IERC20 _outputToken, IRouter _outputSwapRouter, address[] memory _outputSwapPath, IRouter _debtSwapRouter, address[] memory _debtSwapPath) external view returns (uint256) | ||
- _setToken: set token to issue | ||
- _amountIn: input set amount | ||
- _outputToken: output token to be received by user | ||
- _outputSwapRouter: Uniswap V2 router address to perform the swap from the collateral token to the output token | ||
- _outputSwapPath: trade path for the swap from the collateral token to the output token | ||
- _debtSwapRouter: Uniswap V2 router address to perform the swap from the collateral token to the debt token | ||
- _debtSwapPath: trade path for the swap from the collateral token to the debt token | ||
- returns: expected output amount | ||
|
||
|
||
## Checkpoint 3 | ||
Before we move onto the implementation phase we want to make sure that we are aligned on the spec. All contracts should be specced out, their state and external function signatures should be defined. For more complex contracts, internal function definition is preferred in order to align on proper abstractions. Reviewer should take care to make sure that all stake holders (product, app engineering) have their needs met in this stage. | ||
|
||
**Reviewer**: | ||
|
||
## Implementation | ||
[Link to implementation PR]() | ||
## Documentation | ||
[Link to Documentation on feature]() | ||
## Deployment | ||
[Link to Deployment script PR]() | ||
[Link to Deploy outputs PR]() |
Binary file not shown.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.