diff --git a/contracts/contracts/interfaces/aerodrome/IAMOStrategy.sol b/contracts/contracts/interfaces/aerodrome/IAMOStrategy.sol index 89ac9a0e65..49260cf1e0 100644 --- a/contracts/contracts/interfaces/aerodrome/IAMOStrategy.sol +++ b/contracts/contracts/interfaces/aerodrome/IAMOStrategy.sol @@ -55,4 +55,8 @@ interface IAMOStrategy { function allowedWethShareStart() external view returns (uint256); function allowedWethShareEnd() external view returns (uint256); + + function claimGovernance() external; + + function transferGovernance(address _governor) external; } diff --git a/contracts/contracts/utils/AerodromeAMOQuoter.sol b/contracts/contracts/utils/AerodromeAMOQuoter.sol index a2bf2ff157..44b7768bea 100644 --- a/contracts/contracts/utils/AerodromeAMOQuoter.sol +++ b/contracts/contracts/utils/AerodromeAMOQuoter.sol @@ -51,6 +51,8 @@ contract QuoterHelper { IQuoterV2 public quoterV2; IAMOStrategy public strategy; + address public originalGovernor; + //////////////////////////////////////////////////////////////// /// --- ERRORS & EVENTS //////////////////////////////////////////////////////////////// @@ -321,7 +323,13 @@ contract QuoterHelper { uint160 currentPrice = strategy.getPoolX96Price(); uint160 ticker0Price = strategy.sqrtRatioX96TickLower(); uint160 ticker1Price = strategy.sqrtRatioX96TickHigher(); - uint160 targetPrice = (ticker0Price * 20 + ticker1Price * 80) / 100; + uint256 allowedWethShareStart = strategy.allowedWethShareStart(); + uint256 allowedWethShareEnd = strategy.allowedWethShareEnd(); + uint160 mid = uint160(allowedWethShareStart + allowedWethShareEnd) / 2; + uint160 targetPrice = (ticker0Price * + mid + + ticker1Price * + (1 ether - mid)) / 1 ether; return currentPrice > targetPrice; } @@ -415,6 +423,19 @@ contract QuoterHelper { uint160 currentPrice = strategy.getPoolX96Price(); return currentPrice > sqrtPriceTargetX96; } + + function claimGovernanceOnAMO() public { + originalGovernor = strategy.governor(); + strategy.claimGovernance(); + } + + function giveBackGovernanceOnAMO() public { + require( + originalGovernor != address(0), + "Quoter: Original governor not set" + ); + strategy.transferGovernance(originalGovernor); + } } /// @title AerodromeAMOQuoter @@ -552,4 +573,12 @@ contract AerodromeAMOQuoter { sqrtPriceAfterX96 ); } + + function claimGovernance() public { + quoterHelper.claimGovernanceOnAMO(); + } + + function giveBackGovernance() public { + quoterHelper.giveBackGovernanceOnAMO(); + } } diff --git a/contracts/test/strategies/aerodrome-amo.base.fork-test.js b/contracts/test/strategies/aerodrome-amo.base.fork-test.js index 341cd37440..8a9f071a88 100644 --- a/contracts/test/strategies/aerodrome-amo.base.fork-test.js +++ b/contracts/test/strategies/aerodrome-amo.base.fork-test.js @@ -613,7 +613,10 @@ describe("ForkTest: Aerodrome AMO Strategy (Base)", function () { it("Should be able to deposit to the pool & rebalance", async () => { await mintAndDepositToStrategy({ amount: oethUnits("5") }); - const { value, direction } = await quoteAmountToSwapBeforeRebalance(); + const { value, direction } = await quoteAmountToSwapBeforeRebalance({ + lowValue: oethUnits("0"), + highValue: oethUnits("0"), + }); const tx = await rebalance(value, direction, value.mul("99").div("100")); await expect(tx).to.emit(aerodromeAmoStrategy, "PoolRebalanced"); @@ -623,7 +626,10 @@ describe("ForkTest: Aerodrome AMO Strategy (Base)", function () { it("Should be able to deposit to the pool & rebalance multiple times", async () => { await mintAndDepositToStrategy({ amount: oethUnits("5") }); - const { value, direction } = await quoteAmountToSwapBeforeRebalance(); + const { value, direction } = await quoteAmountToSwapBeforeRebalance({ + lowValue: oethUnits("0"), + highValue: oethUnits("0"), + }); const tx = await rebalance(value, direction, value.mul("99").div("100")); await expect(tx).to.emit(aerodromeAmoStrategy, "PoolRebalanced"); @@ -690,11 +696,14 @@ describe("ForkTest: Aerodrome AMO Strategy (Base)", function () { }); it("Should revert when pool rebalance is off target", async () => { - const { value, direction } = await quoteAmountToSwapBeforeRebalance(); + const { value, direction } = await quoteAmountToSwapBeforeRebalance({ + lowValue: oethUnits("0.90"), + highValue: oethUnits("0.92"), + }); - await expect( - rebalance(value.mul("200").div("100"), direction, 0) - ).to.be.revertedWith("PoolRebalanceOutOfBounds"); + await expect(rebalance(value, direction, 0)).to.be.revertedWith( + "PoolRebalanceOutOfBounds" + ); }); it("Should be able to rebalance the pool when price pushed to 1:1", async () => { @@ -709,7 +718,10 @@ describe("ForkTest: Aerodrome AMO Strategy (Base)", function () { // supply some WETH for the rebalance await mintAndDepositToStrategy({ amount: oethUnits("1") }); - const { value, direction } = await quoteAmountToSwapBeforeRebalance(); + const { value, direction } = await quoteAmountToSwapBeforeRebalance({ + lowValue: oethUnits("0"), + highValue: oethUnits("0"), + }); await rebalance(value, direction, value.mul("99").div("100")); await assetLpStakedInGauge(); @@ -725,7 +737,10 @@ describe("ForkTest: Aerodrome AMO Strategy (Base)", function () { swapWeth: direction0, }); - const { value, direction } = await quoteAmountToSwapBeforeRebalance(); + const { value, direction } = await quoteAmountToSwapBeforeRebalance({ + lowValue: oethUnits("0"), + highValue: oethUnits("0"), + }); await rebalance(value, direction, value.mul("99").div("100")); await assetLpStakedInGauge(); @@ -878,7 +893,10 @@ describe("ForkTest: Aerodrome AMO Strategy (Base)", function () { const setup = async () => { await mintAndDepositToStrategy({ amount: oethUnits("5") }); - const { value, direction } = await quoteAmountToSwapBeforeRebalance(); + const { value, direction } = await quoteAmountToSwapBeforeRebalance({ + lowValue: oethUnits("0"), + highValue: oethUnits("0"), + }); // move the price to pre-configured 20% value await rebalance( @@ -955,26 +973,41 @@ describe("ForkTest: Aerodrome AMO Strategy (Base)", function () { }); }; - const quoteAmountToSwapBeforeRebalance = async () => { - // Get the strategist address - const strategist = oethbVault.strategistAddr(); - + const quoteAmountToSwapBeforeRebalance = async ({ lowValue, highValue }) => { // Set Quoter as strategist to pass the `onlyGovernorOrStrategist` requirement - await oethbVault - .connect(await impersonateAndFund(addresses.base.governor)) - .setStrategistAddr(await quoter.quoterHelper()); - + // Get governor + const gov = await aerodromeAmoStrategy.governor(); + // Set pending governance to quoter helper + await aerodromeAmoStrategy + .connect(await impersonateAndFund(gov)) + .transferGovernance(await quoter.quoterHelper()); + // Quoter claim governance) + await quoter.claimGovernance(); + + let txResponse; + if (lowValue == 0 && highValue == 0) { + txResponse = await quoter["quoteAmountToSwapBeforeRebalance()"](); + } else { + txResponse = await quoter[ + "quoteAmountToSwapBeforeRebalance(uint256,uint256)" + ](lowValue, highValue); + } // Get the quote - const txResponse = await quoter["quoteAmountToSwapBeforeRebalance()"](); const txReceipt = await txResponse.wait(); const [transferEvent] = txReceipt.events; const value = transferEvent.args.value; const direction = transferEvent.args.swapWETHForOETHB; // Set back the original strategist + /* await oethbVault .connect(await impersonateAndFund(addresses.base.governor)) .setStrategistAddr(strategist); + */ + quoter.giveBackGovernance(); + await aerodromeAmoStrategy + .connect(await impersonateAndFund(gov)) + .claimGovernance(); // Return the value and direction return { value, direction };