Skip to content

Commit

Permalink
Add delta-neutral basis trading strategy extension contract (#27)
Browse files Browse the repository at this point in the history
* Add IUniswapV3Quoter interface

* Add WIP basis trading strategy contract

* Add WIP strategy contract unit tests

* Fix bugs in existing contracts and tests

* Refactor setter functions; Add unit tests for setter functions

* Fix getCurrentEtherIncentive; Add tests for withdrawEtherBalance and getCurrentEtherIncentive

* Refactor shouldRebalance; Add tests for shouldRebalance and shouldRebalanceWithBounds

* Refactor getChunkRebalanceNotional; Add tests for getChunkRebalanceNotional

* Fix disengage; Add unit tests for disengage

* Add unit tests for ripcord

* Fix reinvest; Add unit tests for reinvest

* Remove console logs from contract; Fix comments

* Rebase to master

* Add javadocs and missing test cases

* Fix reinvest; Add appropriate revert logic

* Fix coverage; Remove unncessary if block

* Fix compilation

* Fix imports and failing tests

* Fix bug: Update reinvest to onlyAllowedCaller from onlyOperator

* Updated version to 0.0.7-basis.0

* Add suggested changes

* Remove pending todos

* Fix to support all leverages <1, =1, >1; Refactor tests

* Add logic to return reinvest only when reinvestable amount > 0; Add unit tests

* Fix failing tests; Fix unit calculation

* Updated version to 0.0.7-basis.1

* Refactor reinvest

* Verify bytes parameters set in exchange settings

* Update package to 0.0.7-basis.2

* Update version to 0.0.8; Pull latest set-protocol-v2

* Add check for booleans in exchange params

* Add suggested changes

* Deposit unused USDC to Perp after rebalance; Add unit tests for all flows

* Add check for exchange name

* Fix failing tests
  • Loading branch information
Sachin authored Apr 15, 2022
1 parent f4e1292 commit a98789a
Show file tree
Hide file tree
Showing 13 changed files with 5,986 additions and 135 deletions.
1,554 changes: 1,554 additions & 0 deletions contracts/extensions/DeltaNeutralBasisTradingStrategyExtension.sol

Large diffs are not rendered by default.

15 changes: 7 additions & 8 deletions contracts/extensions/PerpV2LeverageStrategyExtension.sol
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import { SafeMath } from "@openzeppelin/contracts/math/SafeMath.sol";
import { SignedSafeMath } from "@openzeppelin/contracts/math/SignedSafeMath.sol";

import { IAccountBalance } from "@setprotocol/set-protocol-v2/contracts/interfaces/external/perp-v2/IAccountBalance.sol";
import { IPerpV2LeverageModule } from "@setprotocol/set-protocol-v2/contracts/interfaces/IPerpV2LeverageModule.sol";
import { IPerpV2LeverageModuleV2 } from "@setprotocol/set-protocol-v2/contracts/interfaces/IPerpV2LeverageModuleV2.sol";
import { ISetToken } from "@setprotocol/set-protocol-v2/contracts/interfaces/ISetToken.sol";
import { IVault } from "@setprotocol/set-protocol-v2/contracts/interfaces/external/perp-v2/IVault.sol";
import { PreciseUnitMath } from "@setprotocol/set-protocol-v2/contracts/lib/PreciseUnitMath.sol";
Expand Down Expand Up @@ -71,7 +71,7 @@ contract PerpV2LeverageStrategyExtension is BaseExtension {
struct ActionInfo {
int256 baseBalance; // Balance of virtual base asset from Perp in precise units (10e18). E.g. vWBTC = 10e18
int256 quoteBalance; // Balance of virtual quote asset from Perp in precise units (10e18). E.g. vUSD = 10e18
IPerpV2LeverageModule.AccountInfo accountInfo; // Info on perpetual account including, collateral balance, owedRealizedPnl and pendingFunding
IPerpV2LeverageModuleV2.AccountInfo accountInfo; // Info on perpetual account including, collateral balance, owedRealizedPnl and pendingFunding
int256 basePositionValue; // Valuation in USD adjusted for decimals in precise units (10e18)
int256 quoteValue; // Valuation in USD adjusted for decimals in precise units (10e18)
int256 basePrice; // Price of base asset in precise units (10e18) from PerpV2 Oracle
Expand All @@ -88,7 +88,7 @@ contract PerpV2LeverageStrategyExtension is BaseExtension {

struct ContractSettings {
ISetToken setToken; // Instance of leverage token
IPerpV2LeverageModule perpV2LeverageModule; // Instance of Perp V2 leverage module
IPerpV2LeverageModuleV2 perpV2LeverageModule; // Instance of Perp V2 leverage module
IAccountBalance perpV2AccountBalance; // Instance of Perp V2 AccountBalance contract used to fetch position balances
IPriceFeed baseUSDPriceOracle; // PerpV2 oracle that returns TWAP price for base asset in USD. IPriceFeed is a PerpV2 specific interface
// to interact with differnt oracle providers, e.g. Band Protocol and Chainlink, for different assets
Expand Down Expand Up @@ -399,7 +399,7 @@ contract PerpV2LeverageStrategyExtension is BaseExtension {
*/
function deposit(uint256 _collateralUnits) external onlyOperator {
bytes memory depositCalldata = abi.encodeWithSelector(
IPerpV2LeverageModule.deposit.selector,
IPerpV2LeverageModuleV2.deposit.selector,
address(strategy.setToken),
_collateralUnits
);
Expand All @@ -414,7 +414,7 @@ contract PerpV2LeverageStrategyExtension is BaseExtension {
*/
function withdraw(uint256 _collateralUnits) external onlyOperator {
bytes memory withdrawCalldata = abi.encodeWithSelector(
IPerpV2LeverageModule.withdraw.selector,
IPerpV2LeverageModuleV2.withdraw.selector,
address(strategy.setToken),
_collateralUnits
);
Expand Down Expand Up @@ -599,7 +599,7 @@ contract PerpV2LeverageStrategyExtension is BaseExtension {
function getCurrentEtherIncentive() external view returns(uint256) {
int256 currentLeverageRatio = getCurrentLeverageRatio();

if (currentLeverageRatio >= incentive.incentivizedLeverageRatio) {
if (currentLeverageRatio.abs() >= incentive.incentivizedLeverageRatio.abs()) {
// If ETH reward is below the balance on this contract, then return ETH balance on contract instead
return incentive.etherReward < address(this).balance ? incentive.etherReward : address(this).balance;
} else {
Expand Down Expand Up @@ -673,7 +673,7 @@ contract PerpV2LeverageStrategyExtension is BaseExtension {
uint256 oppositeBoundUnits = _calculateOppositeBoundUnits(baseRebalanceUnits, _leverageInfo.action, _leverageInfo.slippageTolerance);

bytes memory tradeCallData = abi.encodeWithSelector(
IPerpV2LeverageModule.trade.selector,
IPerpV2LeverageModuleV2.trade.selector,
address(strategy.setToken),
strategy.virtualBaseAddress,
baseRebalanceUnits,
Expand Down Expand Up @@ -754,7 +754,6 @@ contract PerpV2LeverageStrategyExtension is BaseExtension {

// Fetch base token prices from PerpV2 oracles and adjust them to 18 decimal places.
int256 rawBasePrice = strategy.baseUSDPriceOracle.getPrice(strategy.twapInterval).toInt256();
uint256 decimals = strategy.baseUSDPriceOracle.decimals();
rebalanceInfo.basePrice = rawBasePrice.mul((10 ** strategy.basePriceDecimalAdjustment).toInt256());

// vUSD price is fixed to 1$
Expand Down
51 changes: 51 additions & 0 deletions contracts/interfaces/IUniswapV3Quoter.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity 0.6.10;
pragma experimental ABIEncoderV2;

/// @title Quoter Interface
/// @notice Supports quoting the calculated amounts from exact input or exact output swaps
/// @dev These functions are not marked view because they rely on calling non-view functions and reverting
/// to compute the result. They are also not gas efficient and should not be called on-chain.
interface IUniswapV3Quoter {
/// @notice Returns the amount out received for a given exact input swap without executing the swap
/// @param path The path of the swap, i.e. each token pair and the pool fee
/// @param amountIn The amount of the first token to swap
/// @return amountOut The amount of the last token that would be received
function quoteExactInput(bytes memory path, uint256 amountIn) external returns (uint256 amountOut);

/// @notice Returns the amount out received for a given exact input but for a swap of a single pool
/// @param tokenIn The token being swapped in
/// @param tokenOut The token being swapped out
/// @param fee The fee of the token pool to consider for the pair
/// @param amountIn The desired input amount
/// @param sqrtPriceLimitX96 The price limit of the pool that cannot be exceeded by the swap
/// @return amountOut The amount of `tokenOut` that would be received
function quoteExactInputSingle(
address tokenIn,
address tokenOut,
uint24 fee,
uint256 amountIn,
uint160 sqrtPriceLimitX96
) external returns (uint256 amountOut);

/// @notice Returns the amount in required for a given exact output swap without executing the swap
/// @param path The path of the swap, i.e. each token pair and the pool fee. Path must be provided in reverse order
/// @param amountOut The amount of the last token to receive
/// @return amountIn The amount of first token required to be paid
function quoteExactOutput(bytes memory path, uint256 amountOut) external returns (uint256 amountIn);

/// @notice Returns the amount in required to receive the given exact output amount but for a swap of a single pool
/// @param tokenIn The token being swapped in
/// @param tokenOut The token being swapped out
/// @param fee The fee of the token pool to consider for the pair
/// @param amountOut The desired output amount
/// @param sqrtPriceLimitX96 The price limit of the pool that cannot be exceeded by the swap
/// @return amountIn The amount required as the input for the swap in order to receive `amountOut`
function quoteExactOutputSingle(
address tokenIn,
address tokenOut,
uint24 fee,
uint256 amountOut,
uint160 sqrtPriceLimitX96
) external returns (uint256 amountIn);
}
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@setprotocol/set-v2-strategies",
"version": "0.0.7",
"version": "0.0.8",
"description": "",
"main": "dist",
"types": "dist/types",
Expand Down Expand Up @@ -90,7 +90,7 @@
"web3": "^1.2.9"
},
"dependencies": {
"@setprotocol/set-protocol-v2": "^0.4.0-hhat.1",
"@setprotocol/set-protocol-v2": "0.10.0-hhat.1",
"@uniswap/v3-sdk": "^3.5.1",
"ethers": "5.5.2",
"fs-extra": "^5.0.0",
Expand Down
Loading

0 comments on commit a98789a

Please sign in to comment.