Skip to content

Commit

Permalink
fix bug when swap amount too small. fix bug when no liquidity in AMO …
Browse files Browse the repository at this point in the history
…pool to reach tick
  • Loading branch information
sparrowDom committed Sep 24, 2024
1 parent e8860c8 commit 3c85d1a
Show file tree
Hide file tree
Showing 4 changed files with 77 additions and 10 deletions.
9 changes: 6 additions & 3 deletions contracts/contracts/utils/AerodromeAMOQuoter.sol
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,11 @@ contract QuoterHelper {
////////////////////////////////////////////////////////////////
uint256 public constant BINARY_MIN_AMOUNT = 1 wei;
uint256 public constant BINARY_MAX_AMOUNT_FOR_REBALANCE = 3_000 ether;
uint256 public constant BINARY_MAX_AMOUNT_FOR_PUSH_PRICE = 50_000 ether;
uint256 public constant BINARY_MAX_AMOUNT_FOR_PUSH_PRICE = 5_000_000 ether;

uint256 public constant BINARY_MAX_ITERATIONS = 100;
uint256 public constant PERCENTAGE_BASE = 1e18; // 100%
uint256 public constant ALLOWED_VARIANCE_PERCENTAGE = 1e16; // 1%
uint256 public constant ALLOWED_VARIANCE_PERCENTAGE = 1e12; // 0.0001%

////////////////////////////////////////////////////////////////
/// --- VARIABLES STORAGE
Expand Down Expand Up @@ -386,10 +386,13 @@ contract QuoterHelper {
);

if (
low == high ||
isWithinAllowedVariance(sqrtPriceX96After, sqrtPriceTargetX96)
) {
return (mid, iterations, swapWETHForOETHB, sqrtPriceX96After);
} else if (low == high) {
// target swap amount not found.
// try increasing BINARY_MAX_AMOUNT_FOR_PUSH_PRICE
revert("SwapAmountNotFound");
} else if (
swapWETHForOETHB
? sqrtPriceX96After > sqrtPriceTargetX96
Expand Down
20 changes: 15 additions & 5 deletions contracts/test/_fixture-base.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ const log = require("../utils/logger")("test:fixtures-arb");
const aeroSwapRouterAbi = require("./abi/aerodromeSwapRouter.json");
const aeroNonfungiblePositionManagerAbi = require("./abi/aerodromeNonfungiblePositionManager.json");
const aerodromeClGaugeAbi = require("./abi/aerodromeClGauge.json");
const aerodromeSugarAbi = require("./abi/aerodromeSugarHelper.json");

const MINTER_ROLE =
"0x9f2df0fed2c77648de5860a4cc508cd0818c85b8b8a1ab4ceeef8d981c8956a6";
Expand All @@ -22,7 +23,7 @@ const BURNER_ROLE =

let snapshotId;
const defaultBaseFixture = deployments.createFixture(async () => {
let aerodromeAmoStrategy, quoter;
let aerodromeAmoStrategy, quoter, sugar;

if (!snapshotId && !isFork) {
snapshotId = await nodeSnapshot();
Expand Down Expand Up @@ -58,7 +59,10 @@ const defaultBaseFixture = deployments.createFixture(async () => {
const wOETHb = await ethers.getContractAt("WOETHBase", wOETHbProxy.address);

const dipperProxy = await ethers.getContract("OETHBaseDripperProxy");
const dripper = await ethers.getContractAt("OETHDripper", dipperProxy.address);
const dripper = await ethers.getContractAt(
"OETHDripper",
dipperProxy.address
);

// OETHb Vault
const oethbVaultProxy = await ethers.getContract("OETHBaseVaultProxy");
Expand All @@ -77,6 +81,11 @@ const defaultBaseFixture = deployments.createFixture(async () => {
aerodromeAmoStrategyProxy.address
);

sugar = await ethers.getContractAt(
aerodromeSugarAbi,
addresses.base.sugarHelper
);

await deployWithConfirmation("AerodromeAMOQuoter", [
aerodromeAmoStrategy.address,
addresses.base.aeroQuoterV2Address,
Expand Down Expand Up @@ -149,11 +158,11 @@ const defaultBaseFixture = deployments.createFixture(async () => {
for (const user of [rafael, nick]) {
// Mint some bridged WOETH
await woeth.connect(minter).mint(user.address, oethUnits("1"));
await hhHelpers.setBalance(user.address, oethUnits("10000000"));
await weth.connect(user).deposit({ value: oethUnits("100000") });
await hhHelpers.setBalance(user.address, oethUnits("100000000"));
await weth.connect(user).deposit({ value: oethUnits("10000000") });

// Set allowance on the vault
await weth.connect(user).approve(oethbVault.address, oethUnits("50"));
await weth.connect(user).approve(oethbVault.address, oethUnits("5000"));
}

await woeth.connect(minter).mint(governor.address, oethUnits("1"));
Expand Down Expand Up @@ -216,6 +225,7 @@ const defaultBaseFixture = deployments.createFixture(async () => {

// Helper
quoter,
sugar,
};
});

Expand Down
1 change: 1 addition & 0 deletions contracts/test/abi/aerodromeSugarHelper.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[{"inputs":[{"internalType":"uint256","name":"amount1","type":"uint256"},{"internalType":"address","name":"pool","type":"address"},{"internalType":"uint160","name":"sqrtRatioX96","type":"uint160"},{"internalType":"int24","name":"tickLow","type":"int24"},{"internalType":"int24","name":"tickHigh","type":"int24"}],"name":"estimateAmount0","outputs":[{"internalType":"uint256","name":"amount0","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount0","type":"uint256"},{"internalType":"address","name":"pool","type":"address"},{"internalType":"uint160","name":"sqrtRatioX96","type":"uint160"},{"internalType":"int24","name":"tickLow","type":"int24"},{"internalType":"int24","name":"tickHigh","type":"int24"}],"name":"estimateAmount1","outputs":[{"internalType":"uint256","name":"amount1","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract INonfungiblePositionManager","name":"positionManager","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"fees","outputs":[{"internalType":"uint256","name":"amount0","type":"uint256"},{"internalType":"uint256","name":"amount1","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint160","name":"sqrtRatioAX96","type":"uint160"},{"internalType":"uint160","name":"sqrtRatioBX96","type":"uint160"},{"internalType":"uint128","name":"liquidity","type":"uint128"},{"internalType":"bool","name":"roundUp","type":"bool"}],"name":"getAmount0Delta","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint160","name":"sqrtRatioAX96","type":"uint160"},{"internalType":"uint160","name":"sqrtRatioBX96","type":"uint160"},{"internalType":"int128","name":"liquidity","type":"int128"}],"name":"getAmount0Delta","outputs":[{"internalType":"int256","name":"","type":"int256"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint160","name":"sqrtRatioAX96","type":"uint160"},{"internalType":"uint160","name":"sqrtRatioBX96","type":"uint160"},{"internalType":"int128","name":"liquidity","type":"int128"}],"name":"getAmount1Delta","outputs":[{"internalType":"int256","name":"","type":"int256"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint160","name":"sqrtRatioAX96","type":"uint160"},{"internalType":"uint160","name":"sqrtRatioBX96","type":"uint160"},{"internalType":"uint128","name":"liquidity","type":"uint128"},{"internalType":"bool","name":"roundUp","type":"bool"}],"name":"getAmount1Delta","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint160","name":"sqrtRatioX96","type":"uint160"},{"internalType":"uint160","name":"sqrtRatioAX96","type":"uint160"},{"internalType":"uint160","name":"sqrtRatioBX96","type":"uint160"},{"internalType":"uint128","name":"liquidity","type":"uint128"}],"name":"getAmountsForLiquidity","outputs":[{"internalType":"uint256","name":"amount0","type":"uint256"},{"internalType":"uint256","name":"amount1","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount0","type":"uint256"},{"internalType":"uint256","name":"amount1","type":"uint256"},{"internalType":"uint160","name":"sqrtRatioX96","type":"uint160"},{"internalType":"uint160","name":"sqrtRatioAX96","type":"uint160"},{"internalType":"uint160","name":"sqrtRatioBX96","type":"uint160"}],"name":"getLiquidityForAmounts","outputs":[{"internalType":"uint256","name":"liquidity","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"pool","type":"address"},{"internalType":"int24","name":"startTick","type":"int24"}],"name":"getPopulatedTicks","outputs":[{"components":[{"internalType":"int24","name":"tick","type":"int24"},{"internalType":"uint160","name":"sqrtRatioX96","type":"uint160"},{"internalType":"int128","name":"liquidityNet","type":"int128"},{"internalType":"uint128","name":"liquidityGross","type":"uint128"}],"internalType":"struct ISugarHelper.PopulatedTick[]","name":"populatedTicks","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"int24","name":"tick","type":"int24"}],"name":"getSqrtRatioAtTick","outputs":[{"internalType":"uint160","name":"sqrtRatioX96","type":"uint160"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint160","name":"sqrtPriceX96","type":"uint160"}],"name":"getTickAtSqrtRatio","outputs":[{"internalType":"int24","name":"tick","type":"int24"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"pool","type":"address"},{"internalType":"uint128","name":"liquidity","type":"uint128"},{"internalType":"int24","name":"tickCurrent","type":"int24"},{"internalType":"int24","name":"tickLower","type":"int24"},{"internalType":"int24","name":"tickUpper","type":"int24"}],"name":"poolFees","outputs":[{"internalType":"uint256","name":"amount0","type":"uint256"},{"internalType":"uint256","name":"amount1","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract INonfungiblePositionManager","name":"positionManager","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint160","name":"sqrtRatioX96","type":"uint160"}],"name":"principal","outputs":[{"internalType":"uint256","name":"amount0","type":"uint256"},{"internalType":"uint256","name":"amount1","type":"uint256"}],"stateMutability":"view","type":"function"}]
57 changes: 55 additions & 2 deletions contracts/test/strategies/aerodrome-amo.base.fork-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ describe("ForkTest: Aerodrome AMO Strategy empty pool setup (Base)", function ()
rafael,
aeroSwapRouter,
aeroNftManager,
sugar,
quoter;

beforeEach(async () => {
Expand All @@ -38,6 +39,7 @@ describe("ForkTest: Aerodrome AMO Strategy empty pool setup (Base)", function ()
rafael = fixture.rafael;
aeroSwapRouter = fixture.aeroSwapRouter;
aeroNftManager = fixture.aeroNftManager;
sugar = fixture.sugar;
quoter = fixture.quoter;

await setupEmpty();
Expand All @@ -50,6 +52,54 @@ describe("ForkTest: Aerodrome AMO Strategy empty pool setup (Base)", function ()
.approve(aeroSwapRouter.address, oethUnits("1000000000"));
});

// tests need liquidity outside AMO ticks in order to test for fail states
const depositLiquidityToPool = async () => {
await weth
.connect(rafael)
.approve(aeroNftManager.address, oethUnits("1000000000"));
await oethb
.connect(rafael)
.approve(aeroNftManager.address, oethUnits("1000000000"));

let blockTimestamp = (await hre.ethers.provider.getBlock("latest"))
.timestamp;
await oethbVault
.connect(rafael)
.mint(weth.address, oethUnits("200"), oethUnits("19.999"));

// we need to supply liquidity in 2 separate transactions so liquidity position is populated
// outside the active tick.
await aeroNftManager.connect(rafael).mint({
token0: weth.address,
token1: oethb.address,
tickSpacing: BigNumber.from("1"),
tickLower: -3,
tickUpper: -1,
amount0Desired: oethUnits("100"),
amount1Desired: oethUnits("100"),
amount0Min: BigNumber.from("0"),
amount1Min: BigNumber.from("0"),
recipient: rafael.address,
deadline: blockTimestamp + 2000,
sqrtPriceX96: BigNumber.from("0"),
});

await aeroNftManager.connect(rafael).mint({
token0: weth.address,
token1: oethb.address,
tickSpacing: BigNumber.from("1"),
tickLower: 0,
tickUpper: 3,
amount0Desired: oethUnits("100"),
amount1Desired: oethUnits("100"),
amount0Min: BigNumber.from("0"),
amount1Min: BigNumber.from("0"),
recipient: rafael.address,
deadline: blockTimestamp + 2000,
sqrtPriceX96: BigNumber.from("0"),
});
};

// Haven't found away to test for this in the strategy contract yet
it.skip("Revert when there is no token id yet and no liquidity to perform the swap.", async () => {
const amount = oethUnits("5");
Expand All @@ -71,8 +121,10 @@ describe("ForkTest: Aerodrome AMO Strategy empty pool setup (Base)", function ()
});

it("Should be reverted trying to rebalance and we are not in the correct tick, below", async () => {
await depositLiquidityToPool();

// Push price to tick -2, which is OutisdeExpectedTickRange
const priceAtTickM2 = BigNumber.from("79220240490215316061937756561"); // tick -2
const priceAtTickM2 = await sugar.getSqrtRatioAtTick(-2);
const { value, direction } = await quoteAmountToSwapToReachPrice({
price: priceAtTickM2,
maxAmount: 0,
Expand All @@ -96,8 +148,9 @@ describe("ForkTest: Aerodrome AMO Strategy empty pool setup (Base)", function ()
});

it("Should be reverted trying to rebalance and we are not in the correct tick, above", async () => {
await depositLiquidityToPool();
// Push price to tick 1, which is OutisdeExpectedTickRange
const priceAtTick1 = BigNumber.from("79232123823359799118286999568"); // tick 1
const priceAtTick1 = await sugar.getSqrtRatioAtTick(1);
const { value, direction } = await quoteAmountToSwapToReachPrice({
price: priceAtTick1,
maxAmount: 0,
Expand Down

0 comments on commit 3c85d1a

Please sign in to comment.