Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Inverted Quoter for AMO fork test. #2210

Merged
merged 31 commits into from
Sep 30, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
d29dc6f
feat: add inverted quoter for rebalancing.
clement-ux Aug 29, 2024
4e9d161
test: use quoter for AMO rebalancing.
clement-ux Aug 29, 2024
8810a77
chore: increase gas limit for test for AMO quoter.
clement-ux Aug 29, 2024
5e7df11
fix: remove arguments from quoter call.
clement-ux Aug 30, 2024
9b91a6a
fix: set strategist back to origin strategist for quoter.
clement-ux Aug 31, 2024
585b40a
feat: add AMO quoter for `amountToSwapToReachPrice`.
clement-ux Aug 31, 2024
4390da3
feat: add quoter for moving price in AMO tests and upgrade swap funct…
clement-ux Aug 31, 2024
aea2198
docs: add descriptions to functions.
clement-ux Sep 4, 2024
bf51ef9
feat: add `overrideWethShare` on `quoteAmountToSwapBeforeRebalance`.
clement-ux Sep 4, 2024
c502ed6
fix: use dictionnary syntax to call function that has same name as an…
clement-ux Sep 4, 2024
7383e79
fix: increase max iteration for quoter.
clement-ux Sep 9, 2024
ffa2d8e
fix: failling test.
clement-ux Sep 9, 2024
280f6b4
fix: move AMOQuoter to fixture.
clement-ux Sep 11, 2024
8abb412
feat: add more comments.
clement-ux Sep 11, 2024
3e076b9
fix: rethink variance calculation for AMO Quoter.
clement-ux Sep 11, 2024
a4d585b
fix: give more WETH at start and simplifies swap.
clement-ux Sep 11, 2024
ef2bf26
fix: remove unused import.
clement-ux Sep 11, 2024
2d663be
fix: _minTokenReceived is 99% of amountToSwap.
clement-ux Sep 11, 2024
80d115b
fix: refactor % comparison.
clement-ux Sep 11, 2024
6b72dc9
fix: change public into internal.
clement-ux Sep 16, 2024
944bb28
fix: adjust quoteAmountToSwapBeforeRebalance for custom shares.
clement-ux Sep 16, 2024
26035cc
fix: split boundaries for quoter.
clement-ux Sep 16, 2024
ea44472
fix: fetch governor before impersonning.
clement-ux Sep 16, 2024
d6d386a
fix: increase tolereance eth remaining in AMO after withdraw.
clement-ux Sep 16, 2024
aa3bb5b
fix: fix failing test and add description.
clement-ux Sep 16, 2024
29c18b5
fix: add doc for edge case in withdraw.
clement-ux Sep 17, 2024
f1c84b0
Merge branch 'master' into clement/add-quoter-for-AMO-fork-tests
clement-ux Sep 18, 2024
10e1ba8
fix: adjust test with latest values.
clement-ux Sep 18, 2024
920099b
Merge remote-tracking branch 'origin/master' into clement/add-quoter-…
sparrowDom Sep 24, 2024
8dce05e
Fix issues with fork testing (#2255)
sparrowDom Sep 30, 2024
d146df8
Merge remote-tracking branch 'origin/master' into clement/add-quoter-…
sparrowDom Sep 30, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 38 additions & 4 deletions contracts/contracts/utils/AerodromeAMOQuoter.sol
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ 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 = 1_000 ether;
uint256 public constant BINARY_MAX_AMOUNT_FOR_PUSH_PRICE = 50_000 ether;

uint256 public constant BINARY_MAX_ITERATIONS = 100;
uint256 public constant PERCENTAGE_BASE = 1e18; // 100%
Expand Down Expand Up @@ -338,12 +338,18 @@ contract QuoterHelper {

/// @notice Get the amount of tokens to swap to reach the target price.
/// @dev This act like a quoter, i.e. the transaction is not performed.
/// @dev Because the amount to swap can be largely overestimated, because CLAMM alow partial orders,
/// i.e. when we ask to swap a very large amount, with a close priceLimite, it will swap only a part of it,
/// and not revert. So if overestimated amount is to high, use a custom maxAmount to avoid this issue.
/// @param sqrtPriceTargetX96 The target price to reach.
/// @return amount The amount of tokens to swap.
/// @return iterations The number of iterations to find the amount.
/// @return swapWETHForOETHB True if we need to swap WETH for OETHb, false otherwise.
/// @return sqrtPriceX96After The price after the swap.
function getAmountToSwapToReachPrice(uint160 sqrtPriceTargetX96)
function getAmountToSwapToReachPrice(
uint160 sqrtPriceTargetX96,
uint256 maxAmount
)
public
returns (
uint256,
Expand All @@ -354,7 +360,9 @@ contract QuoterHelper {
{
uint256 iterations;
uint256 low = BINARY_MIN_AMOUNT;
uint256 high = BINARY_MAX_AMOUNT_FOR_PUSH_PRICE;
uint256 high = maxAmount == 0
? BINARY_MAX_AMOUNT_FOR_PUSH_PRICE
: maxAmount;
bool swapWETHForOETHB = getSwapDirection(sqrtPriceTargetX96);

while (low <= high && iterations < BINARY_MAX_ITERATIONS) {
Expand Down Expand Up @@ -557,6 +565,32 @@ contract AerodromeAMOQuoter {
}
}

/// @notice Use this to get the amount to swap to reach the target price after swap.
/// @dev This call will only revert, check the logs to get returned values.
/// @param sqrtPriceTargetX96 The target price to reach.
/// @param maxAmount The maximum amount to swap. (See QuoterHelper for more details)
function quoteAmountToSwapToReachPrice(
uint160 sqrtPriceTargetX96,
uint256 maxAmount
) public {
(
uint256 amount,
uint256 iterations,
bool swapWETHForOETHB,
uint160 sqrtPriceAfterX96
) = quoterHelper.getAmountToSwapToReachPrice(
sqrtPriceTargetX96,
maxAmount
);

emit ValueFoundBis(
amount,
iterations,
swapWETHForOETHB,
sqrtPriceAfterX96
);
}

/// @notice Use this to get the amount to swap to reach the target price after swap.
/// @dev This call will only revert, check the logs to get returned values.
/// @param sqrtPriceTargetX96 The target price to reach.
Expand All @@ -566,7 +600,7 @@ contract AerodromeAMOQuoter {
uint256 iterations,
bool swapWETHForOETHB,
uint160 sqrtPriceAfterX96
) = quoterHelper.getAmountToSwapToReachPrice(sqrtPriceTargetX96);
) = quoterHelper.getAmountToSwapToReachPrice(sqrtPriceTargetX96, 0);

emit ValueFoundBis(
amount,
Expand Down
3 changes: 2 additions & 1 deletion contracts/test/_fixture-base.js
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,8 @@ 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 weth.connect(user).deposit({ value: oethUnits("5000") });
await hhHelpers.setBalance(user.address, oethUnits("10000000"));
await weth.connect(user).deposit({ value: oethUnits("100000") });

// Set allowance on the vault
await weth.connect(user).approve(oethbVault.address, oethUnits("50"));
Expand Down
101 changes: 81 additions & 20 deletions contracts/test/strategies/aerodrome-amo.base.fork-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,10 @@ describe("ForkTest: Aerodrome AMO Strategy empty pool setup (Base)", function ()

await weth
.connect(rafael)
.approve(aeroSwapRouter.address, oethUnits("1000"));
.approve(aeroSwapRouter.address, oethUnits("1000000000"));
await oethb
.connect(rafael)
.approve(aeroSwapRouter.address, oethUnits("1000"));
.approve(aeroSwapRouter.address, oethUnits("1000000000"));
});

// Haven't found away to test for this in the strategy contract yet
Expand All @@ -70,17 +70,51 @@ describe("ForkTest: Aerodrome AMO Strategy empty pool setup (Base)", function ()
).to.be.revertedWith("Can not rebalance empty pool");
});

it.skip("Should be reverted trying to rebalance and we are not in the correct tick", async () => {
// Push price to tick 0, which is OutisdeExpectedTickRange
const { value, direction } = await quoteAmountToSwapToReachPrice(
await aerodromeAmoStrategy.sqrtRatioX96TickHigher()
it("Should be reverted trying to rebalance and we are not in the correct tick, below", async () => {
// Push price to tick -2, which is OutisdeExpectedTickRange
const priceAtTickM2 = BigNumber.from("79220240490215316061937756561"); // tick -2
const { value, direction } = await quoteAmountToSwapToReachPrice({
price: priceAtTickM2,
maxAmount: 0,
});
await swap({
amount: value,
swapWeth: direction,
priceLimit: priceAtTickM2,
});

// Ensure the price has been pushed enough
expect(await aerodromeAmoStrategy.getPoolX96Price()).to.be.eq(
priceAtTickM2
);
await swap({ amount: value, swapWeth: direction });

await expect(
aerodromeAmoStrategy
.connect(strategist)
.rebalance(oethUnits("0"), false, oethUnits("0"))
.rebalance(oethUnits("0"), direction, oethUnits("0"))
).to.be.revertedWith("OutsideExpectedTickRange");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: you can also check for the tick value here now that the error contains it

});

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

// Ensure the price has been pushed enough
expect(await aerodromeAmoStrategy.getPoolX96Price()).to.be.eq(priceAtTick1);

await expect(
aerodromeAmoStrategy
.connect(strategist)
.rebalance(oethUnits("0"), direction, oethUnits("0"))
).to.be.revertedWith("OutsideExpectedTickRange");
});

Expand Down Expand Up @@ -118,8 +152,17 @@ describe("ForkTest: Aerodrome AMO Strategy empty pool setup (Base)", function ()
});
};

const quoteAmountToSwapToReachPrice = async (price) => {
const txResponse = await quoter.quoteAmountToSwapToReachPrice(price);
const quoteAmountToSwapToReachPrice = async ({ price, maxAmount }) => {
let txResponse;
if (maxAmount == 0) {
txResponse = await quoter["quoteAmountToSwapToReachPrice(uint160)"](
price
);
} else {
txResponse = await quoter[
"quoteAmountToSwapToReachPrice(uint160,uint256)"
](price, maxAmount);
}
const txReceipt = await txResponse.wait();
const [transferEvent] = txReceipt.events;
const value = transferEvent.args.value;
Expand All @@ -128,7 +171,7 @@ describe("ForkTest: Aerodrome AMO Strategy empty pool setup (Base)", function ()
return { value, direction, priceReached };
};

const swap = async ({ amount, swapWeth }) => {
const swap = async ({ amount, swapWeth, priceLimit }) => {
// Check if rafael as enough token to perfom swap
// If not, mint some
const balanceOETHb = await oethb.balanceOf(rafael.address);
Expand All @@ -151,9 +194,12 @@ describe("ForkTest: Aerodrome AMO Strategy empty pool setup (Base)", function ()
deadline: 9999999999,
amountIn: amount,
amountOutMinimum: 0, // slippage check
sqrtPriceLimitX96: swapWeth
? sqrtRatioX96TickM1000
: sqrtRatioX96Tick1000,
sqrtPriceLimitX96:
priceLimit == 0
? swapWeth
? sqrtRatioX96TickM1000
: sqrtRatioX96Tick1000
: priceLimit,
});
};
});
Expand Down Expand Up @@ -193,10 +239,10 @@ describe("ForkTest: Aerodrome AMO Strategy (Base)", function () {
await setup();
await weth
.connect(rafael)
.approve(aeroSwapRouter.address, oethUnits("1000"));
.approve(aeroSwapRouter.address, oethUnits("1000000000"));
await oethb
.connect(rafael)
.approve(aeroSwapRouter.address, oethUnits("1000"));
.approve(aeroSwapRouter.address, oethUnits("1000000000"));
});

describe("ForkTest: Initial state (Base)", function () {
Expand Down Expand Up @@ -715,7 +761,10 @@ describe("ForkTest: Aerodrome AMO Strategy (Base)", function () {
it("Should be able to rebalance the pool when price pushed to 1:1", async () => {
const priceAtTick0 = await aerodromeAmoStrategy.sqrtRatioX96TickHigher();
let { value: value0, direction: direction0 } =
await quoteAmountToSwapToReachPrice(priceAtTick0);
await quoteAmountToSwapToReachPrice({
price: priceAtTick0,
maxAmount: oethUnits("2000"),
});
await swap({
amount: value0,
swapWeth: direction0,
Expand All @@ -737,7 +786,10 @@ describe("ForkTest: Aerodrome AMO Strategy (Base)", function () {
const priceAtTickLower =
await aerodromeAmoStrategy.sqrtRatioX96TickLower();
let { value: value0, direction: direction0 } =
await quoteAmountToSwapToReachPrice(priceAtTickLower);
await quoteAmountToSwapToReachPrice({
price: priceAtTickLower,
maxAmount: oethUnits("2000"),
});
await swap({
amount: value0,
swapWeth: direction0,
Expand Down Expand Up @@ -939,8 +991,17 @@ describe("ForkTest: Aerodrome AMO Strategy (Base)", function () {
// console.log("price of OETHb : ", displayedPoolPrice);
// };

const quoteAmountToSwapToReachPrice = async (price) => {
const txResponse = await quoter.quoteAmountToSwapToReachPrice(price);
const quoteAmountToSwapToReachPrice = async ({ price, maxAmount }) => {
let txResponse;
if (maxAmount == 0) {
txResponse = await quoter["quoteAmountToSwapToReachPrice(uint160)"](
price
);
} else {
txResponse = await quoter[
"quoteAmountToSwapToReachPrice(uint160,uint256)"
](price, maxAmount);
}
const txReceipt = await txResponse.wait();
const [transferEvent] = txReceipt.events;
const value = transferEvent.args.value;
Expand Down