2024-07-04 10:24:06 +02:00
|
|
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
|
|
|
pragma solidity ^0.8.19;
|
|
|
|
|
|
|
|
|
|
import "forge-std/Test.sol";
|
|
|
|
|
import "@aperture/uni-v3-lib/TickMath.sol";
|
|
|
|
|
import {LiquidityAmounts} from "@aperture/uni-v3-lib/LiquidityAmounts.sol";
|
|
|
|
|
import {WETH} from "solmate/tokens/WETH.sol";
|
|
|
|
|
import {PoolAddress, PoolKey} from "@aperture/uni-v3-lib/PoolAddress.sol";
|
|
|
|
|
import "@uniswap-v3-core/interfaces/IUniswapV3Factory.sol";
|
|
|
|
|
import "@uniswap-v3-core/interfaces/IUniswapV3Pool.sol";
|
2024-11-07 15:33:40 +00:00
|
|
|
import "../src/interfaces/IWETH9.sol";
|
2024-07-18 07:35:39 +02:00
|
|
|
import {Harberg} from "../src/Harberg.sol";
|
2024-07-04 10:24:06 +02:00
|
|
|
|
|
|
|
|
import {Stake, ExceededAvailableStake} from "../src/Stake.sol";
|
2024-07-13 18:33:47 +02:00
|
|
|
import {LiquidityManager} from "../src/LiquidityManager.sol";
|
2024-11-07 15:33:40 +00:00
|
|
|
import "../src/helpers/UniswapHelpers.sol";
|
|
|
|
|
import {CSVHelper} from "./helpers/CSVHelper.sol";
|
|
|
|
|
import {CSVManager} from "./helpers/CSVManager.sol";
|
|
|
|
|
import {UniswapTestBase} from "./helpers/UniswapTestBase.sol";
|
|
|
|
|
import "../src/Sentimenter.sol";
|
|
|
|
|
import "../test/mocks/MockSentimenter.sol";
|
2024-07-04 10:24:06 +02:00
|
|
|
|
|
|
|
|
address constant TAX_POOL = address(2);
|
|
|
|
|
// default fee of 1%
|
|
|
|
|
uint24 constant FEE = uint24(10_000);
|
2024-07-09 18:00:39 +02:00
|
|
|
int24 constant TICK_SPACING = 200;
|
|
|
|
|
int24 constant ANCHOR_SPACING = 5 * TICK_SPACING;
|
2024-07-04 10:24:06 +02:00
|
|
|
|
|
|
|
|
// Dummy.sol
|
|
|
|
|
contract Dummy {
|
|
|
|
|
// This contract can be empty as it is only used to affect the nonce
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-07 15:33:40 +00:00
|
|
|
contract LiquidityManagerTest is UniswapTestBase, CSVManager {
|
|
|
|
|
using UniswapHelpers for IUniswapV3Pool;
|
|
|
|
|
using CSVHelper for *;
|
2024-07-09 18:00:39 +02:00
|
|
|
|
2024-07-04 10:24:06 +02:00
|
|
|
IUniswapV3Factory factory;
|
|
|
|
|
Stake stake;
|
2024-07-13 18:33:47 +02:00
|
|
|
LiquidityManager lm;
|
2024-07-04 10:24:06 +02:00
|
|
|
address feeDestination = makeAddr("fees");
|
2024-11-07 15:33:40 +00:00
|
|
|
|
|
|
|
|
struct Response {
|
|
|
|
|
uint256 ethFloor;
|
|
|
|
|
uint256 ethAnchor;
|
|
|
|
|
uint256 ethDiscovery;
|
|
|
|
|
uint256 harbergFloor;
|
|
|
|
|
uint256 harbergAnchor;
|
|
|
|
|
uint256 harbergDiscovery;
|
|
|
|
|
}
|
2024-07-04 10:24:06 +02:00
|
|
|
|
|
|
|
|
// Utility to deploy dummy contracts
|
|
|
|
|
function deployDummies(uint count) internal {
|
|
|
|
|
for (uint i = 0; i < count; i++) {
|
|
|
|
|
new Dummy(); // Just increment the nonce
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function setUpCustomToken0(bool token0shouldBeWeth) public {
|
2024-11-07 15:33:40 +00:00
|
|
|
factory = UniswapHelpers.deployUniswapFactory();
|
2024-07-04 10:24:06 +02:00
|
|
|
|
|
|
|
|
bool setupComplete = false;
|
|
|
|
|
uint retryCount = 0;
|
|
|
|
|
while (!setupComplete && retryCount < 5) {
|
|
|
|
|
// Clean slate if retrying
|
|
|
|
|
if (retryCount > 0) {
|
|
|
|
|
deployDummies(1); // Deploy a dummy contract to shift addresses
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
weth = IWETH9(address(new WETH()));
|
2025-01-23 13:21:49 +01:00
|
|
|
harberg = new Harberg("HARB", "HARB");
|
2024-07-04 10:24:06 +02:00
|
|
|
|
|
|
|
|
// Check if the setup meets the required condition
|
2024-07-18 07:35:39 +02:00
|
|
|
if (token0shouldBeWeth == address(weth) < address(harberg)) {
|
2024-07-04 10:24:06 +02:00
|
|
|
setupComplete = true;
|
|
|
|
|
} else {
|
|
|
|
|
// Clear current instances for re-deployment
|
|
|
|
|
delete weth;
|
2024-07-18 07:35:39 +02:00
|
|
|
delete harberg;
|
2024-07-04 10:24:06 +02:00
|
|
|
retryCount++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
require(setupComplete, "Setup failed to meet the condition after several retries");
|
|
|
|
|
|
2024-07-18 07:35:39 +02:00
|
|
|
pool = IUniswapV3Pool(factory.createPool(address(weth), address(harberg), FEE));
|
2024-07-04 10:24:06 +02:00
|
|
|
|
2024-07-18 07:35:39 +02:00
|
|
|
token0isWeth = address(weth) < address(harberg);
|
2024-11-07 15:33:40 +00:00
|
|
|
pool.initializePoolFor1Cent(token0isWeth);
|
2024-07-04 10:24:06 +02:00
|
|
|
|
2025-01-23 13:21:49 +01:00
|
|
|
stake = new Stake(address(harberg), feeDestination);
|
2024-07-18 07:35:39 +02:00
|
|
|
harberg.setStakingPool(address(stake));
|
2024-11-07 15:33:40 +00:00
|
|
|
Sentimenter senti = Sentimenter(address(new MockSentimenter()));
|
|
|
|
|
senti.initialize(address(harberg), address(stake));
|
|
|
|
|
lm = new LiquidityManager(address(factory), address(weth), address(harberg), address(senti));
|
2024-07-04 10:24:06 +02:00
|
|
|
lm.setFeeDestination(feeDestination);
|
2024-08-13 20:41:39 +02:00
|
|
|
vm.prank(feeDestination);
|
2024-07-18 07:35:39 +02:00
|
|
|
harberg.setLiquidityManager(address(lm));
|
2024-07-04 10:24:06 +02:00
|
|
|
vm.deal(address(lm), 10 ether);
|
2024-11-07 15:33:40 +00:00
|
|
|
initializePositionsCSV(); // Set up the CSV header
|
2024-07-04 10:24:06 +02:00
|
|
|
}
|
|
|
|
|
|
2024-11-07 15:33:40 +00:00
|
|
|
function recenter(bool last) internal {
|
2024-07-04 10:24:06 +02:00
|
|
|
// have some time pass to record prices in uni oracle
|
|
|
|
|
uint256 timeBefore = block.timestamp;
|
|
|
|
|
vm.warp(timeBefore + (60 * 60 * 5));
|
2024-07-06 19:25:09 +02:00
|
|
|
|
2024-11-07 15:33:40 +00:00
|
|
|
try lm.recenter() returns (bool isUp, uint256 sentiment) {
|
|
|
|
|
|
|
|
|
|
// Check liquidity positions after slide
|
|
|
|
|
Response memory rsp;
|
|
|
|
|
rsp = checkLiquidity(isUp ? "shift" : "slide", sentiment);
|
|
|
|
|
assertGt(rsp.ethFloor, rsp.ethAnchor, "slide - Floor should hold more ETH than Anchor");
|
|
|
|
|
assertGt(rsp.harbergDiscovery, rsp.harbergAnchor * 5, "slide - Discovery should hold more HARB than Anchor");
|
|
|
|
|
assertEq(rsp.harbergFloor, 0, "slide - Floor should have no HARB");
|
|
|
|
|
assertEq(rsp.ethDiscovery, 0, "slide - Discovery should have no ETH");
|
|
|
|
|
|
|
|
|
|
} catch Error(string memory reason) {
|
|
|
|
|
if (keccak256(abi.encodePacked(reason)) == keccak256(abi.encodePacked("amplitude not reached."))) {
|
|
|
|
|
console.log("slide failed on amplitude");
|
|
|
|
|
} else {
|
|
|
|
|
if (!last) {
|
|
|
|
|
revert(reason); // Rethrow the error if it's not the expected message
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-07-04 10:24:06 +02:00
|
|
|
}
|
|
|
|
|
|
2024-07-18 07:35:39 +02:00
|
|
|
function getBalancesPool(LiquidityManager.Stage s) internal view returns (int24 currentTick, int24 tickLower, int24 tickUpper, uint256 ethAmount, uint256 harbergAmount) {
|
2024-11-07 15:33:40 +00:00
|
|
|
(,tickLower, tickUpper) = lm.positions(s);
|
|
|
|
|
(uint128 liquidity, , , ,) = pool.positions(keccak256(abi.encodePacked(address(lm), tickLower, tickUpper)));
|
|
|
|
|
|
|
|
|
|
// Fetch the current price from the pool
|
|
|
|
|
uint160 sqrtPriceX96;
|
|
|
|
|
(sqrtPriceX96, currentTick, , , , , ) = pool.slot0();
|
|
|
|
|
uint160 sqrtPriceAX96 = TickMath.getSqrtRatioAtTick(tickLower);
|
|
|
|
|
uint160 sqrtPriceBX96 = TickMath.getSqrtRatioAtTick(tickUpper);
|
|
|
|
|
|
|
|
|
|
// Calculate amounts based on the current tick position relative to provided ticks
|
|
|
|
|
if (token0isWeth) {
|
|
|
|
|
if (currentTick < tickLower) {
|
|
|
|
|
// Current price is below the lower bound of the liquidity position
|
|
|
|
|
ethAmount = LiquidityAmounts.getAmount0ForLiquidity(sqrtPriceAX96, sqrtPriceBX96, liquidity);
|
|
|
|
|
harbergAmount = 0; // All liquidity is in token0 (ETH)
|
|
|
|
|
} else if (currentTick > tickUpper) {
|
|
|
|
|
// Current price is above the upper bound of the liquidity position
|
|
|
|
|
ethAmount = 0; // All liquidity is in token1 (HARB)
|
|
|
|
|
harbergAmount = LiquidityAmounts.getAmount1ForLiquidity(sqrtPriceAX96, sqrtPriceBX96, liquidity);
|
|
|
|
|
} else {
|
|
|
|
|
// Current price is within the bounds of the liquidity position
|
|
|
|
|
ethAmount = LiquidityAmounts.getAmount0ForLiquidity(sqrtPriceX96, sqrtPriceBX96, liquidity);
|
|
|
|
|
harbergAmount = LiquidityAmounts.getAmount1ForLiquidity(sqrtPriceAX96, sqrtPriceX96, liquidity);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
if (currentTick < tickLower) {
|
|
|
|
|
// Current price is below the lower bound of the liquidity position
|
|
|
|
|
harbergAmount = LiquidityAmounts.getAmount0ForLiquidity(sqrtPriceAX96, sqrtPriceBX96, liquidity);
|
|
|
|
|
ethAmount = 0; // All liquidity is in token1 (ETH)
|
|
|
|
|
} else if (currentTick > tickUpper) {
|
|
|
|
|
// Current price is above the upper bound of the liquidity position
|
|
|
|
|
harbergAmount = 0; // All liquidity is in token0 (HARB)
|
|
|
|
|
ethAmount = LiquidityAmounts.getAmount1ForLiquidity(sqrtPriceAX96, sqrtPriceBX96, liquidity);
|
|
|
|
|
} else {
|
|
|
|
|
// Current price is within the bounds of the liquidity position
|
|
|
|
|
harbergAmount = LiquidityAmounts.getAmount0ForLiquidity(sqrtPriceX96, sqrtPriceBX96, liquidity);
|
|
|
|
|
ethAmount = LiquidityAmounts.getAmount1ForLiquidity(sqrtPriceAX96, sqrtPriceX96, liquidity);
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-07-04 10:24:06 +02:00
|
|
|
}
|
|
|
|
|
|
2024-11-07 15:33:40 +00:00
|
|
|
function checkLiquidity(string memory eventName, uint256 sentiment) internal returns (Response memory) {
|
|
|
|
|
Response memory rsp;
|
|
|
|
|
int24 currentTick;
|
|
|
|
|
string memory floorData;
|
|
|
|
|
string memory anchorData;
|
|
|
|
|
string memory discoveryData;
|
|
|
|
|
{
|
|
|
|
|
int24 tickLower;
|
|
|
|
|
int24 tickUpper;
|
|
|
|
|
uint256 eth;
|
|
|
|
|
uint256 harb;
|
|
|
|
|
{
|
|
|
|
|
(currentTick, tickLower, tickUpper, eth, harb) = getBalancesPool(LiquidityManager.Stage.FLOOR);
|
|
|
|
|
floorData = string(abi.encodePacked(CSVHelper.intToStr(tickLower), ",", CSVHelper.intToStr(tickUpper), ",", CSVHelper.uintToStr(eth), ",", CSVHelper.uintToStr(harb), ","));
|
|
|
|
|
rsp.ethFloor = eth;
|
|
|
|
|
rsp.harbergFloor = harb;
|
|
|
|
|
}
|
|
|
|
|
{
|
|
|
|
|
(,tickLower, tickUpper, eth, harb) = getBalancesPool(LiquidityManager.Stage.ANCHOR);
|
|
|
|
|
anchorData = string(abi.encodePacked(CSVHelper.intToStr(tickLower), ",", CSVHelper.intToStr(tickUpper), ",", CSVHelper.uintToStr(eth), ",", CSVHelper.uintToStr(harb), ","));
|
|
|
|
|
rsp.ethAnchor = eth;
|
|
|
|
|
rsp.harbergAnchor = harb;
|
|
|
|
|
}
|
|
|
|
|
{
|
|
|
|
|
(,tickLower, tickUpper, eth, harb) = getBalancesPool(LiquidityManager.Stage.DISCOVERY);
|
|
|
|
|
discoveryData = string(abi.encodePacked(CSVHelper.intToStr(tickLower), ",", CSVHelper.intToStr(tickUpper), ",", CSVHelper.uintToStr(eth), ",", CSVHelper.uintToStr(harb), ","));
|
|
|
|
|
rsp.ethDiscovery = eth;
|
|
|
|
|
rsp.harbergDiscovery = harb;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
string memory newRow = string(abi.encodePacked(eventName, ",", CSVHelper.intToStr(currentTick), ",", CSVHelper.uintToStr(sentiment / 1e12), ",", floorData, anchorData, discoveryData));
|
|
|
|
|
appendCSVRow(newRow); // Append the new row to the CSV
|
|
|
|
|
return rsp;
|
2024-07-04 10:24:06 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function buy(uint256 amountEth) internal {
|
|
|
|
|
performSwap(amountEth, true);
|
2024-11-07 15:33:40 +00:00
|
|
|
checkLiquidity(string.concat("buy ", CSVHelper.uintToStr(amountEth)), 0);
|
2024-07-04 10:24:06 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function sell(uint256 amountHarb) internal {
|
|
|
|
|
performSwap(amountHarb, false);
|
2024-11-07 15:33:40 +00:00
|
|
|
checkLiquidity(string.concat("sell ", CSVHelper.uintToStr(amountHarb)), 0);
|
2024-07-04 10:24:06 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
receive() external payable {}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function writeCsv() public {
|
2024-11-07 15:33:40 +00:00
|
|
|
writeCSVToFile("./out/positions.csv"); // Write CSV to file
|
2024-07-04 10:24:06 +02:00
|
|
|
}
|
|
|
|
|
|
2024-07-18 07:35:39 +02:00
|
|
|
function testHandleCumulativeOverflow() public {
|
|
|
|
|
setUpCustomToken0(false);
|
|
|
|
|
vm.deal(account, 201 ether);
|
|
|
|
|
vm.prank(account);
|
|
|
|
|
weth.deposit{value: 201 ether}();
|
|
|
|
|
|
|
|
|
|
// Setup initial liquidity
|
2024-11-07 15:33:40 +00:00
|
|
|
recenter(false);
|
2024-07-18 07:35:39 +02:00
|
|
|
|
|
|
|
|
vm.store(
|
|
|
|
|
address(lm),
|
|
|
|
|
bytes32(uint256(0)),
|
|
|
|
|
bytes32(uint256(type(uint256).max - 10))
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
vm.store(
|
|
|
|
|
address(lm),
|
|
|
|
|
bytes32(uint256(1)),
|
|
|
|
|
bytes32(uint256((type(uint256).max - 10) / (3000 * 10**20)))
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
2024-08-12 19:07:07 +02:00
|
|
|
uint256 cumulativeVolumeWeightedPriceX96 = lm.cumulativeVolumeWeightedPriceX96();
|
2024-07-18 07:35:39 +02:00
|
|
|
uint256 beforeCumulativeVolume = lm.cumulativeVolume();
|
|
|
|
|
|
2024-08-12 19:07:07 +02:00
|
|
|
assertGt(cumulativeVolumeWeightedPriceX96, type(uint256).max / 2, "Initial cumulativeVolumeWeightedPrice is not near max uint256");
|
2024-07-18 07:35:39 +02:00
|
|
|
|
2024-08-15 15:17:44 +02:00
|
|
|
buy(25 ether);
|
2024-07-18 07:35:39 +02:00
|
|
|
|
2024-11-07 15:33:40 +00:00
|
|
|
recenter(false);
|
2024-07-18 07:35:39 +02:00
|
|
|
|
2024-08-12 19:07:07 +02:00
|
|
|
cumulativeVolumeWeightedPriceX96 = lm.cumulativeVolumeWeightedPriceX96();
|
2024-07-18 07:35:39 +02:00
|
|
|
uint256 cumulativeVolume = lm.cumulativeVolume();
|
|
|
|
|
|
|
|
|
|
// Assert that the values after wrap-around are valid and smaller than max uint256
|
|
|
|
|
assertGt(beforeCumulativeVolume, cumulativeVolume, "cumulativeVolume after wrap-around is smaller than before");
|
|
|
|
|
|
|
|
|
|
// Assert that the price is reasonable
|
2024-08-12 19:07:07 +02:00
|
|
|
uint256 calculatedPrice = cumulativeVolumeWeightedPriceX96 / cumulativeVolume;
|
2024-07-18 07:35:39 +02:00
|
|
|
assertTrue(calculatedPrice > 0 && calculatedPrice < 10**40, "Calculated price after wrap-around is not within a reasonable range");
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-06 18:36:13 +02:00
|
|
|
// function testScenarioBuyAll() public {
|
|
|
|
|
// setUpCustomToken0(false);
|
|
|
|
|
// vm.deal(account, 300 ether);
|
|
|
|
|
// vm.prank(account);
|
|
|
|
|
// weth.deposit{value: 300 ether}();
|
|
|
|
|
|
|
|
|
|
// uint256 traderBalanceBefore = weth.balanceOf(account);
|
|
|
|
|
|
|
|
|
|
// // Setup initial liquidity
|
2024-11-07 15:33:40 +00:00
|
|
|
// recenter(false);
|
2024-07-06 18:36:13 +02:00
|
|
|
|
2024-07-13 14:56:13 +02:00
|
|
|
// buy(200 ether);
|
2024-07-06 18:36:13 +02:00
|
|
|
|
2024-11-07 15:33:40 +00:00
|
|
|
// recenter(false);
|
2024-07-06 18:36:13 +02:00
|
|
|
|
2024-07-13 14:56:13 +02:00
|
|
|
// //revert();
|
|
|
|
|
|
2024-07-18 07:35:39 +02:00
|
|
|
// sell(harberg.balanceOf(account));
|
2024-07-06 18:36:13 +02:00
|
|
|
|
2024-11-07 15:33:40 +00:00
|
|
|
// recenter(true);
|
2024-07-06 18:36:13 +02:00
|
|
|
|
|
|
|
|
// writeCsv();
|
2024-07-09 18:00:39 +02:00
|
|
|
|
2024-07-06 18:36:13 +02:00
|
|
|
// uint256 traderBalanceAfter = weth.balanceOf(account);
|
2024-07-09 18:00:39 +02:00
|
|
|
|
2024-07-06 18:36:13 +02:00
|
|
|
// assertGt(traderBalanceBefore, traderBalanceAfter, "trader should not have made profit");
|
|
|
|
|
// }
|
|
|
|
|
|
2024-07-06 19:25:09 +02:00
|
|
|
// function testScenarioB() public {
|
|
|
|
|
// setUpCustomToken0(false);
|
2024-08-15 15:17:44 +02:00
|
|
|
// vm.deal(account, 501 ether);
|
2024-07-06 19:25:09 +02:00
|
|
|
// vm.prank(account);
|
2024-08-15 15:17:44 +02:00
|
|
|
// weth.deposit{value: 501 ether}();
|
2024-07-04 10:24:06 +02:00
|
|
|
|
2024-07-06 19:25:09 +02:00
|
|
|
// uint256 traderBalanceBefore = weth.balanceOf(account);
|
2024-07-04 10:24:06 +02:00
|
|
|
|
2024-07-06 19:25:09 +02:00
|
|
|
// // Setup initial liquidity
|
2024-11-07 15:33:40 +00:00
|
|
|
// recenter(false);
|
2024-07-04 10:24:06 +02:00
|
|
|
|
2024-08-15 15:17:44 +02:00
|
|
|
// buy(25 ether);
|
2024-07-16 19:47:39 +02:00
|
|
|
|
2024-11-07 15:33:40 +00:00
|
|
|
// recenter(false);
|
2024-07-16 19:47:39 +02:00
|
|
|
|
2024-08-15 15:17:44 +02:00
|
|
|
// buy(45 ether);
|
2024-07-06 18:36:13 +02:00
|
|
|
|
2024-11-07 15:33:40 +00:00
|
|
|
// recenter(false);
|
2024-07-06 18:36:13 +02:00
|
|
|
|
2024-08-15 15:17:44 +02:00
|
|
|
// buy(80 ether);
|
2024-07-06 18:36:13 +02:00
|
|
|
|
2024-11-07 15:33:40 +00:00
|
|
|
// recenter(false);
|
2024-07-06 18:36:13 +02:00
|
|
|
|
2024-08-15 15:17:44 +02:00
|
|
|
// buy(120 ether);
|
2024-07-04 10:24:06 +02:00
|
|
|
|
2024-11-07 15:33:40 +00:00
|
|
|
// recenter(false);
|
2024-07-04 10:24:06 +02:00
|
|
|
|
2024-08-12 19:07:07 +02:00
|
|
|
// sell(harberg.balanceOf(account) / 4);
|
|
|
|
|
|
2024-11-07 15:33:40 +00:00
|
|
|
// recenter(true);
|
2024-08-12 19:07:07 +02:00
|
|
|
|
|
|
|
|
// sell(harberg.balanceOf(account) / 4);
|
|
|
|
|
|
2024-11-07 15:33:40 +00:00
|
|
|
// recenter(true);
|
2024-08-12 19:07:07 +02:00
|
|
|
|
|
|
|
|
// sell(harberg.balanceOf(account) / 4);
|
|
|
|
|
|
2024-11-07 15:33:40 +00:00
|
|
|
// recenter(true);
|
2024-08-12 19:07:07 +02:00
|
|
|
|
2024-07-18 07:35:39 +02:00
|
|
|
// sell(harberg.balanceOf(account));
|
2024-07-04 10:24:06 +02:00
|
|
|
|
2024-11-07 15:33:40 +00:00
|
|
|
// recenter(true);
|
2024-07-04 10:24:06 +02:00
|
|
|
|
2024-07-06 19:25:09 +02:00
|
|
|
// writeCsv();
|
|
|
|
|
// uint256 traderBalanceAfter = weth.balanceOf(account);
|
|
|
|
|
// console.log(traderBalanceBefore);
|
|
|
|
|
// console.log(traderBalanceAfter);
|
|
|
|
|
// assertGt(traderBalanceBefore, traderBalanceAfter, "trader should not have made profit");
|
2024-08-12 19:07:07 +02:00
|
|
|
// revert();
|
2024-07-06 19:25:09 +02:00
|
|
|
// }
|
2024-07-04 10:24:06 +02:00
|
|
|
|
2024-07-06 19:25:09 +02:00
|
|
|
function testScenarioFuzz(uint8 numActions, uint8 frequency, uint8[] calldata amounts) public {
|
|
|
|
|
vm.assume(numActions > 5);
|
|
|
|
|
vm.assume(frequency > 0);
|
|
|
|
|
vm.assume(frequency < 20);
|
|
|
|
|
vm.assume(amounts.length >= numActions);
|
2024-07-04 10:24:06 +02:00
|
|
|
|
2024-07-13 14:56:13 +02:00
|
|
|
setUpCustomToken0(numActions % 2 == 0 ? true : false);
|
2024-11-07 15:33:40 +00:00
|
|
|
vm.deal(account, 20 ether);
|
2024-07-06 19:25:09 +02:00
|
|
|
vm.prank(account);
|
2024-11-07 15:33:40 +00:00
|
|
|
weth.deposit{value: 20 ether}();
|
2024-07-04 10:24:06 +02:00
|
|
|
|
|
|
|
|
|
2024-07-06 19:25:09 +02:00
|
|
|
// Setup initial liquidity
|
2024-11-07 15:33:40 +00:00
|
|
|
recenter(false);
|
2024-07-04 10:24:06 +02:00
|
|
|
|
2024-07-06 19:25:09 +02:00
|
|
|
uint256 traderBalanceBefore = weth.balanceOf(account);
|
|
|
|
|
uint8 f = 0;
|
|
|
|
|
for (uint i = 0; i < numActions; i++) {
|
|
|
|
|
uint256 amount = (uint256(amounts[i]) * 1 ether) + 1 ether;
|
2024-07-18 07:35:39 +02:00
|
|
|
uint256 harbergBal = harberg.balanceOf(account);
|
|
|
|
|
if (harbergBal == 0) {
|
2024-07-06 19:25:09 +02:00
|
|
|
amount = amount % (weth.balanceOf(account) / 2);
|
|
|
|
|
amount = amount == 0 ? weth.balanceOf(account) : amount;
|
|
|
|
|
buy(amount);
|
|
|
|
|
} else if (weth.balanceOf(account) == 0) {
|
2024-07-18 07:35:39 +02:00
|
|
|
sell(amount % harbergBal);
|
2024-07-06 19:25:09 +02:00
|
|
|
} else {
|
|
|
|
|
if (amount % 2 == 0) {
|
|
|
|
|
amount = amount % (weth.balanceOf(account) / 2);
|
|
|
|
|
amount = amount == 0 ? weth.balanceOf(account) : amount;
|
|
|
|
|
buy(amount);
|
|
|
|
|
} else {
|
2024-07-18 07:35:39 +02:00
|
|
|
sell(amount % harbergBal);
|
2024-07-06 19:25:09 +02:00
|
|
|
}
|
|
|
|
|
}
|
2024-07-04 10:24:06 +02:00
|
|
|
|
2024-11-07 15:33:40 +00:00
|
|
|
(, int24 currentTick, , , , , ) = pool.slot0();
|
|
|
|
|
if (currentTick < -887270) {
|
|
|
|
|
// buy(1000000000000000);
|
|
|
|
|
sell(100000000000000);
|
|
|
|
|
}
|
|
|
|
|
if (currentTick > 887270) {
|
|
|
|
|
buy(1000000000000000);
|
|
|
|
|
// sell(100000000000000);
|
|
|
|
|
}
|
2024-07-06 19:25:09 +02:00
|
|
|
if (f >= frequency) {
|
2024-11-07 15:33:40 +00:00
|
|
|
recenter(false);
|
2024-07-06 19:25:09 +02:00
|
|
|
f = 0;
|
|
|
|
|
} else {
|
|
|
|
|
f++;
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-07-04 10:24:06 +02:00
|
|
|
|
2024-07-06 19:25:09 +02:00
|
|
|
// Simulate large sell to push price down to floor
|
2024-07-18 07:35:39 +02:00
|
|
|
sell(harberg.balanceOf(account));
|
2024-07-04 10:24:06 +02:00
|
|
|
|
2024-11-07 15:33:40 +00:00
|
|
|
recenter(true);
|
2024-07-06 18:36:13 +02:00
|
|
|
|
2024-07-06 19:25:09 +02:00
|
|
|
uint256 traderBalanceAfter = weth.balanceOf(account);
|
|
|
|
|
|
|
|
|
|
if (traderBalanceAfter > traderBalanceBefore){
|
|
|
|
|
writeCsv();
|
|
|
|
|
}
|
2024-07-16 19:47:39 +02:00
|
|
|
// TODO: take 1% fee into account
|
2024-07-06 19:25:09 +02:00
|
|
|
assertGt(traderBalanceBefore, traderBalanceAfter, "trader should not have made profit");
|
|
|
|
|
}
|
2024-07-04 10:24:06 +02:00
|
|
|
|
2024-08-13 17:06:12 +03:00
|
|
|
}
|