Skip to content

Commit

Permalink
Merge branch 'master' into shah/fix-typo
Browse files Browse the repository at this point in the history
  • Loading branch information
shahthepro committed Oct 11, 2024
2 parents 8cc8da5 + 6abd45b commit 284a730
Show file tree
Hide file tree
Showing 8 changed files with 485 additions and 86 deletions.
55 changes: 55 additions & 0 deletions brownie/runlogs/2024_10_strategist.py
Original file line number Diff line number Diff line change
Expand Up @@ -209,3 +209,58 @@ def main():
print("-----")
print("Profit", "{:.6f}".format(profit / 10**18), profit)
print("Vault Change", "{:.6f}".format(vault_change / 10**18), vault_change)

# -----------------------------------------------------
# Oct 9th 2024 - OETHb allocation & rebalance
# -----------------------------------------------------

from world_base import *

def main():
with TemporaryForkForOETHbReallocations() as txs:
# Before
txs.append(vault_core.rebase({ 'from': OETHB_STRATEGIST }))
txs.append(vault_value_checker.takeSnapshot({ 'from': OETHB_STRATEGIST }))

# Deposit all WETH
wethDepositAmount = weth.balanceOf(OETHB_VAULT_PROXY_ADDRESS)
txs.append(
vault_admin.depositToStrategy(
OETHB_AERODROME_AMO_STRATEGY,
[weth],
[wethDepositAmount],
{'from': OETHB_STRATEGIST}
)
)

amo_snapsnot()
swapWeth = True
swapAmount = 0
minAmount = swapAmount * 0.98
print("--------------------")
print("WETH Deposit ", c18(wethDepositAmount))
print("-----")
print("Swap amount ", c18(swapAmount))
print("Min amount ", c18(minAmount))
print("-----")

txs.append(
amo_strat.rebalance(
swapAmount,
swapWeth,
minAmount,
{'from': OETHB_STRATEGIST}
)
)

# After
vault_change = vault_core.totalValue() - vault_value_checker.snapshots(OETHB_STRATEGIST)[0]
supply_change = oethb.totalSupply() - vault_value_checker.snapshots(OETHB_STRATEGIST)[1]
profit = vault_change - supply_change

txs.append(vault_value_checker.checkDelta(profit, (1 * 10**18), vault_change, (1 * 10**18), {'from': OETHB_STRATEGIST}))

amo_snapsnot()
print("--------------------")
print("Profit ", c18(profit), profit)
print("Vault Change ", c18(vault_change), vault_change)
137 changes: 103 additions & 34 deletions contracts/contracts/strategies/aerodrome/AerodromeAMOStrategy.sol
Original file line number Diff line number Diff line change
Expand Up @@ -366,6 +366,14 @@ contract AerodromeAMOStrategy is InitializableAbstractStrategy {
require(_asset == WETH, "Unsupported asset");
require(_amount > 0, "Must deposit something");
emit Deposit(_asset, address(0), _amount);

// if the pool price is not within the expected interval leave the WETH on the contract
// as to not break the mints
(bool _isExpectedRange, ) = _checkForExpectedPoolPrice(false);
if (_isExpectedRange) {
// deposit funds into the underlying pool
_rebalance(0, false, 0);
}
}

/**
Expand Down Expand Up @@ -400,6 +408,14 @@ contract AerodromeAMOStrategy is InitializableAbstractStrategy {
bool _swapWeth,
uint256 _minTokenReceived
) external nonReentrant onlyGovernorOrStrategist {
_rebalance(_amountToSwap, _swapWeth, _minTokenReceived);
}

function _rebalance(
uint256 _amountToSwap,
bool _swapWeth,
uint256 _minTokenReceived
) internal {
/**
* Would be nice to check if there is any total liquidity in the pool before performing this swap
* but there is no easy way to do that in UniswapV3:
Expand All @@ -422,15 +438,18 @@ contract AerodromeAMOStrategy is InitializableAbstractStrategy {
}
// calling check liquidity early so we don't get unexpected errors when adding liquidity
// in the later stages of this function
_checkForExpectedPoolPrice();
_checkForExpectedPoolPrice(true);

_addLiquidity();

// this call shouldn't be necessary, since adding liquidity shouldn't affect the active
// trading price. It is a defensive programming measure.
_checkForExpectedPoolPrice();
(, uint256 _wethSharePct) = _checkForExpectedPoolPrice(true);

// revert if protocol insolvent
_solvencyAssert();

emit PoolRebalanced(_wethSharePct);
}

/**
Expand Down Expand Up @@ -534,6 +553,11 @@ contract AerodromeAMOStrategy is InitializableAbstractStrategy {
IVault(vaultAddress).mintForStrategy(mintForSwap);
}

// approve the specific amount of WETH required
if (_swapWeth) {
IERC20(WETH).approve(address(swapRouter), _amountToSwap);
}

// Swap it
swapRouter.exactInputSingle(
// sqrtPriceLimitX96 is just a rough sanity check that we are within 0 -> 1 tick
Expand Down Expand Up @@ -605,6 +629,9 @@ contract AerodromeAMOStrategy is InitializableAbstractStrategy {
);
}

// approve the specific amount of WETH required
IERC20(WETH).approve(address(positionManager), _wethBalance);

uint256 _wethAmountSupplied;
uint256 _oethbAmountSupplied;
if (tokenId == 0) {
Expand Down Expand Up @@ -672,9 +699,18 @@ contract AerodromeAMOStrategy is InitializableAbstractStrategy {
* @dev Check that the Aerodrome pool price is within the expected
* parameters.
* This function works whether the strategy contract has liquidity
* position in the pool or not.
* position in the pool or not. The function returns _wethSharePct
* as a gas optimization measure.
* @param throwException when set to true the function throws an exception
* when pool's price is not within expected range.
* @return _isExpectedRange Bool expressing price is within expected range
* @return _wethSharePct Share of WETH owned by this strategy contract in the
* configured ticker.
*/
function _checkForExpectedPoolPrice() internal {
function _checkForExpectedPoolPrice(bool throwException)
internal
returns (bool _isExpectedRange, uint256 _wethSharePct)
{
require(
allowedWethShareStart != 0 && allowedWethShareEnd != 0,
"Weth share interval not set"
Expand All @@ -692,40 +728,30 @@ contract AerodromeAMOStrategy is InitializableAbstractStrategy {
_currentPrice <= sqrtRatioX96TickLower ||
_currentPrice >= sqrtRatioX96TickHigher
) {
revert OutsideExpectedTickRange(getCurrentTradingTick());
if (throwException) {
revert OutsideExpectedTickRange(getCurrentTradingTick());
}
return (false, 0);
}

/**
* If estimateAmount1 call fails it could be due to _currentPrice being really
* close to a tick and amount1 too big to compute.
*
* If token addresses were reversed estimateAmount0 would be required here
*/
uint256 _normalizedWethAmount = 1 ether;
uint256 _correspondingOethAmount = helper.estimateAmount1(
_normalizedWethAmount,
address(0), // no need to pass pool address when current price is specified
_currentPrice,
lowerTick,
upperTick
);

// 18 decimal number expressed weth tick share
uint256 _wethSharePct = _normalizedWethAmount.divPrecisely(
_normalizedWethAmount + _correspondingOethAmount
);
// 18 decimal number expressed WETH tick share
_wethSharePct = _getWethShare(_currentPrice);

if (
_wethSharePct < allowedWethShareStart ||
_wethSharePct > allowedWethShareEnd
) {
revert PoolRebalanceOutOfBounds(
_wethSharePct,
allowedWethShareStart,
allowedWethShareEnd
);
if (throwException) {
revert PoolRebalanceOutOfBounds(
_wethSharePct,
allowedWethShareStart,
allowedWethShareEnd
);
}
return (false, _wethSharePct);
}
emit PoolRebalanced(_wethSharePct);

return (true, _wethSharePct);
}

/**
Expand Down Expand Up @@ -868,11 +894,17 @@ contract AerodromeAMOStrategy is InitializableAbstractStrategy {
nonReentrant
{
// to add liquidity to the clPool
IERC20(WETH).safeApprove(address(positionManager), type(uint256).max);
IERC20(OETHb).safeApprove(address(positionManager), type(uint256).max);
IERC20(OETHb).approve(address(positionManager), type(uint256).max);
// to be able to rebalance using the swapRouter
IERC20(WETH).safeApprove(address(swapRouter), type(uint256).max);
IERC20(OETHb).safeApprove(address(swapRouter), type(uint256).max);
IERC20(OETHb).approve(address(swapRouter), type(uint256).max);

/* the behaviour of this strategy has slightly changed and WETH could be
* present on the contract between the transactions. For that reason we are
* un-approving WETH to the swapRouter & positionManager and only approving
* the required amount before a transaction
*/
IERC20(WETH).approve(address(swapRouter), 0);
IERC20(WETH).approve(address(positionManager), 0);
}

/***************************************
Expand Down Expand Up @@ -940,6 +972,16 @@ contract AerodromeAMOStrategy is InitializableAbstractStrategy {
(, _currentTick, , , , ) = clPool.slot0();
}

/**
* @notice Returns the percentage of WETH liquidity in the configured ticker
* owned by this strategy contract.
* @return uint256 1e18 denominated percentage expressing the share
*/
function getWETHShare() external view returns (uint256) {
uint160 _currentPrice = getPoolX96Price();
return _getWethShare(_currentPrice);
}

/**
* @notice Returns the amount of liquidity in the contract's LP position
* @return _liquidity Amount of liquidity in the position
Expand All @@ -952,6 +994,33 @@ contract AerodromeAMOStrategy is InitializableAbstractStrategy {
(, , , , , , , _liquidity, , , , ) = positionManager.positions(tokenId);
}

function _getWethShare(uint160 _currentPrice)
internal
view
returns (uint256)
{
/**
* If estimateAmount1 call fails it could be due to _currentPrice being really
* close to a tick and amount1 too big to compute.
*
* If token addresses were reversed estimateAmount0 would be required here
*/
uint256 _normalizedWethAmount = 1 ether;
uint256 _correspondingOethAmount = helper.estimateAmount1(
_normalizedWethAmount,
address(0), // no need to pass pool address when current price is specified
_currentPrice,
lowerTick,
upperTick
);

// 18 decimal number expressed weth tick share
return
_normalizedWethAmount.divPrecisely(
_normalizedWethAmount + _correspondingOethAmount
);
}

/***************************************
Hidden functions
****************************************/
Expand Down
24 changes: 5 additions & 19 deletions contracts/deploy/base/006_base_amo_strategy.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ const {
} = require("../../utils/deploy");
const addresses = require("../../utils/addresses");
const { oethUnits } = require("../../test/helpers");
const {
deployBaseAerodromeAMOStrategyImplementation,
} = require("../deployActions");

//const aeroVoterAbi = require("../../test/abi/aerodromeVoter.json");
//const slipstreamPoolAbi = require("../../test/abi/aerodromeSlipstreamPool.json")
Expand Down Expand Up @@ -47,35 +50,18 @@ module.exports = deployOnBaseWithGuardian(
async ({ ethers }) => {
const { deployerAddr, governorAddr } = await getNamedAccounts();
const sDeployer = await ethers.provider.getSigner(deployerAddr);
const cOETHbProxy = await ethers.getContract("OETHBaseProxy");
const cOETHbVaultProxy = await ethers.getContract("OETHBaseVaultProxy");
const cOETHbVault = await ethers.getContractAt(
"IVault",
cOETHbVaultProxy.address
);

const cAMOStrategyImpl =
await deployBaseAerodromeAMOStrategyImplementation();
await deployWithConfirmation("AerodromeAMOStrategyProxy");
await deployWithConfirmation("AerodromeAMOStrategy", [
/* The pool address is not yet known. Might be created before we deploy the
* strategy or after.
*/
[addresses.zero, cOETHbVaultProxy.address], // platformAddress, VaultAddress
addresses.base.WETH, // weth address
cOETHbProxy.address, // OETHb address
addresses.base.swapRouter, // swapRouter
addresses.base.nonFungiblePositionManager, // nonfungiblePositionManager
addresses.base.aerodromeOETHbWETHClPool, // clOETHbWethPool
addresses.base.aerodromeOETHbWETHClGauge, // gauge address
addresses.base.sugarHelper, // sugarHelper
-1, // lowerBoundingTick
0, // upperBoundingTick
0, // tickClosestToParity
]);

const cAMOStrategyProxy = await ethers.getContract(
"AerodromeAMOStrategyProxy"
);
const cAMOStrategyImpl = await ethers.getContract("AerodromeAMOStrategy");
const cAMOStrategy = await ethers.getContractAt(
"AerodromeAMOStrategy",
cAMOStrategyProxy.address
Expand Down
Loading

0 comments on commit 284a730

Please sign in to comment.