Skip to content

Commit

Permalink
refactor some rebalance functionality into a separate function. Expos…
Browse files Browse the repository at this point in the history
…e WETH share as a public function
  • Loading branch information
sparrowDom committed Oct 7, 2024
1 parent a3e753b commit c7346e6
Show file tree
Hide file tree
Showing 2 changed files with 104 additions and 46 deletions.
111 changes: 67 additions & 44 deletions contracts/contracts/strategies/aerodrome/AerodromeAMOStrategy.sol
Original file line number Diff line number Diff line change
Expand Up @@ -369,7 +369,8 @@ contract AerodromeAMOStrategy is InitializableAbstractStrategy {

// if the pool price is not within the expected interval leave the WETH on the contract
// as to not break the mints
if (_checkForExpectedPoolPrice(true)) {
(bool _isExpectedRange, ) = _checkForExpectedPoolPrice(false);

Check warning on line 372 in contracts/contracts/strategies/aerodrome/AerodromeAMOStrategy.sol

View check run for this annotation

Codecov / codecov/patch

contracts/contracts/strategies/aerodrome/AerodromeAMOStrategy.sol#L372

Added line #L372 was not covered by tests
if (_isExpectedRange) {
// deposit funds into the underlying pool
_rebalance(0, false, 0);

Check warning on line 375 in contracts/contracts/strategies/aerodrome/AerodromeAMOStrategy.sol

View check run for this annotation

Codecov / codecov/patch

contracts/contracts/strategies/aerodrome/AerodromeAMOStrategy.sol#L375

Added line #L375 was not covered by tests
}
Expand Down Expand Up @@ -437,16 +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(false);
_checkForExpectedPoolPrice(true);

Check warning on line 441 in contracts/contracts/strategies/aerodrome/AerodromeAMOStrategy.sol

View check run for this annotation

Codecov / codecov/patch

contracts/contracts/strategies/aerodrome/AerodromeAMOStrategy.sol#L441

Added line #L441 was not covered by tests

_addLiquidity();

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

Check warning on line 447 in contracts/contracts/strategies/aerodrome/AerodromeAMOStrategy.sol

View check run for this annotation

Codecov / codecov/patch

contracts/contracts/strategies/aerodrome/AerodromeAMOStrategy.sol#L447

Added line #L447 was not covered by tests

// revert if protocol insolvent
_solvencyAssert();

emit PoolRebalanced(_wethSharePct);

Check warning on line 452 in contracts/contracts/strategies/aerodrome/AerodromeAMOStrategy.sol

View check run for this annotation

Codecov / codecov/patch

contracts/contracts/strategies/aerodrome/AerodromeAMOStrategy.sol#L452

Added line #L452 was not covered by tests
}

/**
Expand Down Expand Up @@ -698,12 +701,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.
* @param dryRun when this property is set to true the function doesn't throw
* an exception but returns a bool. `True` means price within expected
* range and `false` means outside that range.
* 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(bool dryRun) internal returns (bool) {
function _checkForExpectedPoolPrice(bool throwException)

Check warning on line 712 in contracts/contracts/strategies/aerodrome/AerodromeAMOStrategy.sol

View check run for this annotation

Codecov / codecov/patch

contracts/contracts/strategies/aerodrome/AerodromeAMOStrategy.sol#L712

Added line #L712 was not covered by tests
internal
returns (bool _isExpectedRange, uint256 _wethSharePct)
{
require(
allowedWethShareStart != 0 && allowedWethShareEnd != 0,
"Weth share interval not set"
Expand All @@ -721,53 +730,30 @@ contract AerodromeAMOStrategy is InitializableAbstractStrategy {
_currentPrice <= sqrtRatioX96TickLower ||
_currentPrice >= sqrtRatioX96TickHigher
) {
if (dryRun) {
return false;
if (throwException) {
revert OutsideExpectedTickRange(getCurrentTradingTick());

Check warning on line 734 in contracts/contracts/strategies/aerodrome/AerodromeAMOStrategy.sol

View check run for this annotation

Codecov / codecov/patch

contracts/contracts/strategies/aerodrome/AerodromeAMOStrategy.sol#L734

Added line #L734 was not covered by tests
}

revert OutsideExpectedTickRange(getCurrentTradingTick());
return (false, 0);

Check warning on line 736 in contracts/contracts/strategies/aerodrome/AerodromeAMOStrategy.sol

View check run for this annotation

Codecov / codecov/patch

contracts/contracts/strategies/aerodrome/AerodromeAMOStrategy.sol#L736

Added line #L736 was not covered by tests
}

/**
* 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);

Check warning on line 740 in contracts/contracts/strategies/aerodrome/AerodromeAMOStrategy.sol

View check run for this annotation

Codecov / codecov/patch

contracts/contracts/strategies/aerodrome/AerodromeAMOStrategy.sol#L740

Added line #L740 was not covered by tests

if (
_wethSharePct < allowedWethShareStart ||
_wethSharePct > allowedWethShareEnd
) {
if (dryRun) {
return false;
if (throwException) {
revert PoolRebalanceOutOfBounds(

Check warning on line 747 in contracts/contracts/strategies/aerodrome/AerodromeAMOStrategy.sol

View check run for this annotation

Codecov / codecov/patch

contracts/contracts/strategies/aerodrome/AerodromeAMOStrategy.sol#L747

Added line #L747 was not covered by tests
_wethSharePct,
allowedWethShareStart,
allowedWethShareEnd
);
}

revert PoolRebalanceOutOfBounds(
_wethSharePct,
allowedWethShareStart,
allowedWethShareEnd
);
return (false, _wethSharePct);

Check warning on line 753 in contracts/contracts/strategies/aerodrome/AerodromeAMOStrategy.sol

View check run for this annotation

Codecov / codecov/patch

contracts/contracts/strategies/aerodrome/AerodromeAMOStrategy.sol#L753

Added line #L753 was not covered by tests
}

// return before emitting the event is intentional
if (dryRun) {
return true;
}
emit PoolRebalanced(_wethSharePct);
return (true, _wethSharePct);

Check warning on line 756 in contracts/contracts/strategies/aerodrome/AerodromeAMOStrategy.sol

View check run for this annotation

Codecov / codecov/patch

contracts/contracts/strategies/aerodrome/AerodromeAMOStrategy.sol#L756

Added line #L756 was not covered by tests
}

/**
Expand Down Expand Up @@ -990,6 +976,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);

Check warning on line 986 in contracts/contracts/strategies/aerodrome/AerodromeAMOStrategy.sol

View check run for this annotation

Codecov / codecov/patch

contracts/contracts/strategies/aerodrome/AerodromeAMOStrategy.sol#L984-L986

Added lines #L984 - L986 were not covered by tests
}

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

function _getWethShare(uint160 _currentPrice)

Check warning on line 1001 in contracts/contracts/strategies/aerodrome/AerodromeAMOStrategy.sol

View check run for this annotation

Codecov / codecov/patch

contracts/contracts/strategies/aerodrome/AerodromeAMOStrategy.sol#L1001

Added line #L1001 was not covered by tests
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(

Check warning on line 1013 in contracts/contracts/strategies/aerodrome/AerodromeAMOStrategy.sol

View check run for this annotation

Codecov / codecov/patch

contracts/contracts/strategies/aerodrome/AerodromeAMOStrategy.sol#L1012-L1013

Added lines #L1012 - L1013 were not covered by tests
_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

Check warning on line 1022 in contracts/contracts/strategies/aerodrome/AerodromeAMOStrategy.sol

View check run for this annotation

Codecov / codecov/patch

contracts/contracts/strategies/aerodrome/AerodromeAMOStrategy.sol#L1022

Added line #L1022 was not covered by tests
_normalizedWethAmount.divPrecisely(
_normalizedWethAmount + _correspondingOethAmount
);
}

/***************************************
Hidden functions
****************************************/
Expand Down
39 changes: 37 additions & 2 deletions contracts/test/strategies/aerodrome-amo.base.fork-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -922,12 +922,47 @@ describe("ForkTest: Aerodrome AMO Strategy (Base)", async function () {
await assetLpStakedInGauge();
});

it("Should be able to rebalance the pool when price pushed to close to 1 OETHb costing 1.0001 WETH", async () => {
it("Should be able to rebalance the pool when price pushed to over the 1 OETHb costing 1.0001 WETH", async () => {
const priceAtTickLower =
await aerodromeAmoStrategy.sqrtRatioX96TickLower();
const priceAtTickHigher =
await aerodromeAmoStrategy.sqrtRatioX96TickHigher();
// 5% of the price diff within a single ticker
const fivePctTickerPrice = priceAtTickHigher
.sub(priceAtTickLower)
.div(20);

let { value: value0, direction: direction0 } =
await quoteAmountToSwapToReachPrice({
price: priceAtTickLower,
price: priceAtTickLower.add(fivePctTickerPrice),
});
await swap({
amount: value0,
swapWeth: direction0,
});

const { value, direction } = await quoteAmountToSwapBeforeRebalance({
lowValue: oethUnits("0"),
highValue: oethUnits("0"),
});
await rebalance(value, direction, value.mul("99").div("100"));

await assetLpStakedInGauge();
});

it("Should be able to rebalance the pool when price pushed to close to the 1 OETHb costing 1.0001 WETH", async () => {
const priceAtTickLower =
await aerodromeAmoStrategy.sqrtRatioX96TickLower();
const priceAtTickHigher =
await aerodromeAmoStrategy.sqrtRatioX96TickHigher();
// 5% of the price diff within a single ticker
const fivePctTickerPrice = priceAtTickHigher
.sub(priceAtTickLower)
.div(20);

let { value: value0, direction: direction0 } =
await quoteAmountToSwapToReachPrice({
price: priceAtTickLower.sub(fivePctTickerPrice),
});
await swap({
amount: value0,
Expand Down

0 comments on commit c7346e6

Please sign in to comment.