Skip to content

Commit

Permalink
Merge pull request #105 from stabilitydao/104-irmf-grmf-upgrade-trans…
Browse files Browse the repository at this point in the history
…mute-oretro

Retro strategies upgrade
  • Loading branch information
a17 authored Mar 9, 2024
2 parents 0fdb054 + 448ac01 commit bea3a47
Show file tree
Hide file tree
Showing 12 changed files with 783 additions and 238 deletions.
237 changes: 76 additions & 161 deletions chains/PolygonLib.sol

Large diffs are not rendered by default.

38 changes: 38 additions & 0 deletions script/DeployUpdateRetroStrategies.Polygon.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;

import "forge-std/Script.sol";
import "../src/strategies/IchiRetroMerklFarmStrategy.sol";
import "../src/strategies/GammaRetroMerklFarmStrategy.sol";

contract DeployUpdateRetroStrategiesPolygon is Script {
address public constant PLATFORM = 0xb2a0737ef27b5Cc474D24c779af612159b1c3e60;
address public constant POOL_RETRO_USDCe_CASH_100 = 0x619259F699839dD1498FFC22297044462483bD27;
address public constant TOKEN_CASH = 0x5D066D022EDE10eFa2717eD3D79f22F949F8C175;
address public constant TOKEN_USDCe = 0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174;

function run() external {
ISwapper swapper = ISwapper(IPlatform(PLATFORM).swapper());

// route
ISwapper.AddPoolData[] memory pools = new ISwapper.AddPoolData[](1);
pools[0] = ISwapper.AddPoolData({
pool: POOL_RETRO_USDCe_CASH_100,
ammAdapterId: AmmAdapterIdLib.UNISWAPV3,
tokenIn: TOKEN_CASH,
tokenOut: TOKEN_USDCe
});

uint deployerPrivateKey = vm.envUint("PRIVATE_KEY");
vm.startBroadcast(deployerPrivateKey);

swapper.addPools(pools, false);

new IchiRetroMerklFarmStrategy();
new GammaRetroMerklFarmStrategy();

vm.stopBroadcast();
}

function testDeployPolygon() external {}
}
10 changes: 10 additions & 0 deletions src/integrations/retro/IOToken.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;

interface IOToken {
function exercise(uint amount, uint maxPaymentAmount, address recipient) external returns (uint);

function getDiscountedPrice(uint amount_) external view returns (uint amount);

function discount() external view returns (uint);
}
50 changes: 50 additions & 0 deletions src/integrations/uniswapv3/IQuoter.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.23;

/// @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 IQuoter {
/// @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, uint amountIn) external returns (uint 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,
uint amountIn,
uint160 sqrtPriceLimitX96
) external returns (uint 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, uint amountOut) external returns (uint 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,
uint amountOut,
uint160 sqrtPriceLimitX96
) external returns (uint amountIn);
}
157 changes: 111 additions & 46 deletions src/strategies/GammaRetroMerklFarmStrategy.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,15 @@ import "./libs/StrategyIdLib.sol";
import "./libs/FarmMechanicsLib.sol";
import "./libs/UniswapV3MathLib.sol";
import "./libs/ALMPositionNameLib.sol";
import "./libs/GRMFLib.sol";
import "../integrations/gamma/IUniProxy.sol";
import "../integrations/gamma/IHypervisor.sol";
import "../integrations/uniswapv3/IUniswapV3Pool.sol";
import "../core/libs/CommonLib.sol";
import "../adapters/libs/AmmAdapterIdLib.sol";

/// @title Earning Merkl rewards on Retro by underlying Gamma Hypervisor
/// @dev 2.0.0: oRETRO transmutation through CASH flash loan
/// @author Alien Deployer (https://github.com/a17)
contract GammaRetroMerklFarmStrategy is LPStrategyBase, FarmingStrategyBase {
using SafeERC20 for IERC20;
Expand All @@ -23,7 +25,7 @@ contract GammaRetroMerklFarmStrategy is LPStrategyBase, FarmingStrategyBase {
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

/// @inheritdoc IControllable
string public constant VERSION = "1.0.0";
string public constant VERSION = "2.0.0";

uint internal constant _PRECISION = 1e36;

Expand All @@ -32,13 +34,11 @@ contract GammaRetroMerklFarmStrategy is LPStrategyBase, FarmingStrategyBase {
0x46595ab865e543d547ad8669c6b3d688cf90b51012c63b16ac16869cad017f00;

/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* STORAGE */
/* CUSTOM ERRORS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

/// @custom:storage-location erc7201:stability.GammaRetroFarmStrategy
struct GammaRetroFarmStrategyStorage {
IUniProxy uniProxy;
}
error NotFlashPool();
error PairReentered();

/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* INITIALIZATION */
Expand All @@ -51,10 +51,10 @@ contract GammaRetroMerklFarmStrategy is LPStrategyBase, FarmingStrategyBase {
}

IFactory.Farm memory farm = _getFarm(addresses[0], nums[0]);
if (farm.addresses.length != 2 || farm.nums.length != 1 || farm.ticks.length != 0) {
if (farm.addresses.length != 7 || farm.nums.length != 1 || farm.ticks.length != 0) {
revert IFarmingStrategy.BadFarm();
}
GammaRetroFarmStrategyStorage storage $ = _getGammaRetroStorage();
GRMFLib.GammaRetroFarmStrategyStorage storage $ = _getGammaRetroStorage();
$.uniProxy = IUniProxy(farm.addresses[0]);

__LPStrategyBase_init(
Expand All @@ -69,9 +69,105 @@ contract GammaRetroMerklFarmStrategy is LPStrategyBase, FarmingStrategyBase {

__FarmingStrategyBase_init(addresses[0], nums[0]);

$.paymentToken = farm.addresses[2];
$.flashPool = farm.addresses[3];
$.oPool = farm.addresses[4];
$.uToPaymentTokenPool = farm.addresses[5];
$.quoter = farm.addresses[6];

address[] memory _assets = assets();
address oToken = _getFarmingStrategyBaseStorage()._rewardAssets[0];
address uToken = GRMFLib.getOtherTokenFromPool(farm.addresses[4], oToken);
address swapper = IPlatform(addresses[0]).swapper();
IERC20(_assets[0]).forceApprove(farm.addresses[1], type(uint).max);
IERC20(_assets[1]).forceApprove(farm.addresses[1], type(uint).max);
IERC20(farm.addresses[2]).forceApprove(oToken, type(uint).max);
IERC20(uToken).forceApprove(swapper, type(uint).max);
IERC20(farm.addresses[2]).forceApprove(swapper, type(uint).max);
}

/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CALLBACKS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

// Call back function, called by the pair during our flashloan.
function uniswapV3FlashCallback(uint, uint fee1, bytes calldata) external {
GRMFLib.GammaRetroFarmStrategyStorage storage $ = _getGammaRetroStorage();
address flashPool = $.flashPool;
address paymentToken = $.paymentToken;
address oToken = _getFarmingStrategyBaseStorage()._rewardAssets[0];
address uToken = GRMFLib.getOtherTokenFromPool($.oPool, oToken);
address _platform = platform();

if (msg.sender != flashPool) {
revert NotFlashPool();
}
if (!$.flashOn) {
revert PairReentered();
}

// Exercise the oToken
uint paymentTokenAmount = IERC20(paymentToken).balanceOf(address(this));
uint oTokenAmt = IERC20(oToken).balanceOf(address(this));

//slither-disable-next-line unused-return
IOToken(oToken).exercise(oTokenAmt, paymentTokenAmount, address(this));

// Swap underlying to payment token
address swapper = IPlatform(_platform).swapper();

ISwapper.PoolData[] memory route = new ISwapper.PoolData[](1);
route[0].pool = $.uToPaymentTokenPool;
route[0].ammAdapter = IPlatform(_platform).ammAdapter(keccak256(bytes(ammAdapterId()))).proxy;
route[0].tokenIn = uToken;
route[0].tokenOut = paymentToken;
ISwapper(swapper).swapWithRoute(
route, GRMFLib.balance(uToken), LPStrategyLib.SWAP_ASSETS_PRICE_IMPACT_TOLERANCE
);

// Pay off our loan
uint pairDebt = paymentTokenAmount + fee1;
IERC20(paymentToken).safeTransfer(flashPool, pairDebt);

$.flashOn = false;
}

/// @dev Temporary actions
function upgradeStorageToVersion2(
address paymentToken,
address flashPool,
address oPool,
address uToPaymentTokenPool,
address quoter
) external onlyOperator {
GRMFLib.GammaRetroFarmStrategyStorage storage $ = _getGammaRetroStorage();

if ($.paymentToken != address(0)) {
revert AlreadyExist();
}

// CASH
$.paymentToken = paymentToken;

// USDCe-CASH 0.01%
$.flashPool = flashPool;

// RETRO-oRETRO 1%
$.oPool = oPool;

// CASH-RETRO 1%
$.uToPaymentTokenPool = uToPaymentTokenPool;

// UniswapV3 Quoter
$.quoter = quoter;

address oToken = _getFarmingStrategyBaseStorage()._rewardAssets[0];
address uToken = GRMFLib.getOtherTokenFromPool(oPool, oToken);
address swapper = IPlatform(platform()).swapper();

IERC20(paymentToken).forceApprove(oToken, type(uint).max);
IERC20(uToken).forceApprove(swapper, type(uint).max);
IERC20(paymentToken).forceApprove(swapper, type(uint).max);
}

/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
Expand Down Expand Up @@ -200,19 +296,9 @@ contract GammaRetroMerklFarmStrategy is LPStrategyBase, FarmingStrategyBase {
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

/// @inheritdoc StrategyBase
function _depositAssets(uint[] memory amounts, bool claimRevenue) internal override returns (uint value) {
GammaRetroFarmStrategyStorage storage $ = _getGammaRetroStorage();
FarmingStrategyBaseStorage storage _$ = _getFarmingStrategyBaseStorage();
function _depositAssets(uint[] memory amounts, bool) internal override returns (uint value) {
GRMFLib.GammaRetroFarmStrategyStorage storage $ = _getGammaRetroStorage();
StrategyBaseStorage storage __$ = _getStrategyBaseStorage();
if (claimRevenue) {
(,,, uint[] memory rewardAmounts) = _claimRevenue();
uint len = rewardAmounts.length;
// nosemgrep
for (uint i; i < len; ++i) {
// nosemgrep
_$._rewardsOnBalance[i] += rewardAmounts[i];
}
}
//slither-disable-next-line uninitialized-local
uint[4] memory minIn;
value = $.uniProxy.deposit(amounts[0], amounts[1], address(this), __$._underlying, minIn);
Expand Down Expand Up @@ -247,7 +333,6 @@ contract GammaRetroMerklFarmStrategy is LPStrategyBase, FarmingStrategyBase {
/// @inheritdoc StrategyBase
function _claimRevenue()
internal
view
override
returns (
address[] memory __assets,
Expand All @@ -256,16 +341,8 @@ contract GammaRetroMerklFarmStrategy is LPStrategyBase, FarmingStrategyBase {
uint[] memory __rewardAmounts
)
{
StrategyBaseStorage storage __$__ = _getStrategyBaseStorage();
FarmingStrategyBaseStorage storage _$_ = _getFarmingStrategyBaseStorage();
__assets = __$__._assets;
__rewardAssets = _$_._rewardAssets;
__amounts = new uint[](2);
uint rwLen = __rewardAssets.length;
__rewardAmounts = new uint[](rwLen);
for (uint i; i < rwLen; ++i) {
__rewardAmounts[i] = StrategyLib.balance(__rewardAssets[i]);
}
return
GRMFLib.claimRevenue(_getStrategyBaseStorage(), _getFarmingStrategyBaseStorage(), _getGammaRetroStorage());
}

/// @inheritdoc StrategyBase
Expand All @@ -289,7 +366,7 @@ contract GammaRetroMerklFarmStrategy is LPStrategyBase, FarmingStrategyBase {
returns (uint[] memory amountsConsumed, uint value)
{
// alternative calculation: beefy-contracts/contracts/BIFI/strategies/Gamma/StrategyQuickGamma.sol
GammaRetroFarmStrategyStorage storage $ = _getGammaRetroStorage();
GRMFLib.GammaRetroFarmStrategyStorage storage $ = _getGammaRetroStorage();
StrategyBaseStorage storage _$ = _getStrategyBaseStorage();
amountsConsumed = new uint[](2);
address[] memory _assets = assets();
Expand Down Expand Up @@ -368,22 +445,10 @@ contract GammaRetroMerklFarmStrategy is LPStrategyBase, FarmingStrategyBase {
IFactory.Farm memory farm,
IAmmAdapter _ammAdapter
) internal view returns (string memory) {
//slither-disable-next-line calls-loop
return string.concat(
"Earn ",
//slither-disable-next-line calls-loop
CommonLib.implode(CommonLib.getSymbols(farm.rewardAssets), ", "),
" on Retro by ",
//slither-disable-next-line calls-loop
CommonLib.implode(CommonLib.getSymbols(_ammAdapter.poolTokens(farm.pool)), "-"),
" Gamma ",
//slither-disable-next-line calls-loop
ALMPositionNameLib.getName(farm.nums[0]),
" LP"
);
return GRMFLib.generateDescription(farm, _ammAdapter);
}

function _getGammaRetroStorage() private pure returns (GammaRetroFarmStrategyStorage storage $) {
function _getGammaRetroStorage() private pure returns (GRMFLib.GammaRetroFarmStrategyStorage storage $) {
//slither-disable-next-line assembly
assembly {
$.slot := GAMMARETROFARMSTRATEGY_STORAGE_LOCATION
Expand Down
Loading

0 comments on commit bea3a47

Please sign in to comment.