cleanup
This commit is contained in:
parent
969d78247c
commit
ba298cfd50
4 changed files with 38 additions and 47 deletions
|
|
@ -20,7 +20,7 @@ import {Harb} from "./Harb.sol";
|
|||
* It maintains 3 positions:
|
||||
* - The floor position guarantees the capacity needed to maintain a minimum price of the HARB token It is a very tight liquidity range with enough reserve assets to buy back the circulating supply.
|
||||
* - The anchor range provides liquidity around the current market price, ensuring liquid trading conditions for the token, regardless of the market environment.
|
||||
* - The discovery range starts 500 ticks above the current market price and increases from there. It consists solely of unissued tokens, which are sold as the market price increases.
|
||||
* - The discovery range starts 1000 ticks above the current market price and increases from there. It consists solely of unissued tokens, which are sold as the market price increases.
|
||||
* The liquidity surplus obtained from selling tokens in the discovery range is directed back into the floor and anchor positions.
|
||||
*/
|
||||
contract BaseLineLP {
|
||||
|
|
@ -36,14 +36,12 @@ contract BaseLineLP {
|
|||
|
||||
enum Stage { FLOOR, ANCHOR, DISCOVERY }
|
||||
|
||||
uint256 constant LIQUIDITY_RATIO_DIVISOR = 100;
|
||||
|
||||
// the address of the Uniswap V3 factory
|
||||
address immutable factory;
|
||||
IWETH9 immutable weth;
|
||||
Harb immutable harb;
|
||||
IUniswapV3Pool immutable pool;
|
||||
bool immutable token0isWeth;
|
||||
address private immutable factory;
|
||||
IWETH9 private immutable weth;
|
||||
Harb private immutable harb;
|
||||
IUniswapV3Pool private immutable pool;
|
||||
bool private immutable token0isWeth;
|
||||
PoolKey private poolKey;
|
||||
|
||||
struct TokenPosition {
|
||||
|
|
@ -59,6 +57,9 @@ contract BaseLineLP {
|
|||
mapping(Stage => TokenPosition) public positions;
|
||||
address public feeDestination;
|
||||
|
||||
error ZeroAddressInSetter();
|
||||
error AddressAlreadySet();
|
||||
|
||||
// TODO: add events
|
||||
|
||||
constructor(address _factory, address _WETH9, address _harb) {
|
||||
|
|
@ -84,13 +85,9 @@ contract BaseLineLP {
|
|||
if (amount1Owed > 0) IERC20(poolKey.token1).transfer(msg.sender, amount1Owed);
|
||||
}
|
||||
|
||||
function liquidityPool() external view returns (address) {
|
||||
return address(pool);
|
||||
}
|
||||
|
||||
function setFeeDestination(address feeDestination_) external {
|
||||
// TODO: add trapdoor
|
||||
require(address(0) != feeDestination_, "zero addr");
|
||||
if (address(0) == feeDestination_) revert ZeroAddressInSetter();
|
||||
if (feeDestination != address(0)) revert AddressAlreadySet();
|
||||
feeDestination = feeDestination_;
|
||||
}
|
||||
|
||||
|
|
@ -99,10 +96,6 @@ contract BaseLineLP {
|
|||
|
||||
}
|
||||
|
||||
function outstanding() public view returns (uint256 _outstanding) {
|
||||
_outstanding = (harb.totalSupply() - harb.balanceOf(address(pool)) - harb.balanceOf(address(this)));
|
||||
}
|
||||
|
||||
function tickAtPrice(bool t0isWeth, uint256 tokenAmount, uint256 ethAmount) internal pure returns (int24 tick_) {
|
||||
require(ethAmount > 0, "ETH amount cannot be zero");
|
||||
uint160 sqrtPriceX96;
|
||||
|
|
@ -153,7 +146,7 @@ contract BaseLineLP {
|
|||
// ### set Floor position
|
||||
int24 vwapTick;
|
||||
{
|
||||
uint256 outstandingSupply = outstanding();
|
||||
uint256 outstandingSupply = harb.outstandingSupply();
|
||||
uint256 vwap = 0;
|
||||
uint256 requiredEthForBuyback = 0;
|
||||
if (cumulativeVolume > 0) {
|
||||
|
|
@ -174,6 +167,7 @@ contract BaseLineLP {
|
|||
} else {
|
||||
vwap = cumulativeVolumeWeightedPrice * CAPITAL_INEFFICIENCY / 100 / cumulativeVolume; // in harb/eth
|
||||
vwapTick = tickAtPrice(token0isWeth, token0isWeth ? vwap : 10**18, token0isWeth ? 10**18 : vwap);
|
||||
vwapTick = token0isWeth ? vwapTick : -vwapTick;
|
||||
if (requiredEthForBuyback < ethBalance) {
|
||||
// invest a majority of the ETH still in floor, even though not needed
|
||||
requiredEthForBuyback = (requiredEthForBuyback + (5 * ethBalance)) / 6;
|
||||
|
|
@ -282,7 +276,6 @@ contract BaseLineLP {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Transfer fees to the fee destination
|
||||
// and record transaction totals
|
||||
|
|
@ -324,17 +317,15 @@ contract BaseLineLP {
|
|||
averageTick = int24(tickCumulativeDiff / int56(int32(timeInterval)));
|
||||
// Process the data
|
||||
} catch {
|
||||
// Handle the error, possibly by trying with a different time interval or providing a default response
|
||||
// TODO: Handle the error, possibly by trying with a different time interval or providing a default response
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
return (currentTick >= averageTick - MAX_TICK_DEVIATION && currentTick <= averageTick + MAX_TICK_DEVIATION);
|
||||
}
|
||||
|
||||
// call this function when price has moved up 15%
|
||||
// call this function when price has moved up x%
|
||||
// TODO: write a bot that calls this function regularly
|
||||
event DEBUG(int24 a, int24 b, int24 c, int24 d);
|
||||
function shift() external {
|
||||
require(positions[Stage.ANCHOR].liquidity > 0, "Not initialized");
|
||||
// Fetch the current tick from the Uniswap V3 pool
|
||||
|
|
@ -349,7 +340,6 @@ contract BaseLineLP {
|
|||
int24 anchorTickUpper = positions[Stage.ANCHOR].tickUpper;
|
||||
|
||||
int24 centerTick = token0isWeth ? anchorTickLower + ANCHOR_SPACING : anchorTickUpper - ANCHOR_SPACING;
|
||||
emit DEBUG(anchorTickLower, anchorTickUpper, centerTick, currentTick);
|
||||
uint256 minAmplitude = uint256(uint24((anchorTickUpper - anchorTickLower) * 3 / 20));
|
||||
|
||||
// Determine the correct comparison direction based on token0isWeth
|
||||
|
|
|
|||
|
|
@ -130,6 +130,10 @@ contract Harb is ERC20, ERC20Permit {
|
|||
return twabController.totalSupply(address(this));
|
||||
}
|
||||
|
||||
function outstandingSupply() public view returns (uint256) {
|
||||
return totalSupply() - balanceOf(liquidityPool) - balanceOf(liquidityManager);
|
||||
}
|
||||
|
||||
/* ============ Internal ERC20 Overrides ============ */
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -208,7 +208,7 @@ contract Stake {
|
|||
require(taxRate > pos.taxRate, "tax too low to snatch");
|
||||
_payTax(positionId, pos, TAX_FLOOR_DURATION);
|
||||
pos.taxRate = taxRate;
|
||||
|
||||
emit PositionRateHiked(positionId, pos.owner, taxRate);
|
||||
}
|
||||
|
||||
function exitPosition(uint256 positionId) public {
|
||||
|
|
|
|||
|
|
@ -70,7 +70,7 @@ contract BaseLineLP2Test is Test {
|
|||
} else {
|
||||
// Token (valued at 1 USD cent) as token0, ETH as token1
|
||||
// We invert the logic to represent the price of 1 token in terms of ETH
|
||||
price = uint256(10**16) / 3700; // Adjust for 18 decimal places
|
||||
price = uint256(10**16) / 3000; // Adjust for 18 decimal places
|
||||
}
|
||||
|
||||
uint160 sqrtPriceX96 = uint160(sqrt(price) * 2**96 / 10**9); // Adjust sqrt value to 96-bit precision
|
||||
|
|
@ -131,7 +131,7 @@ contract BaseLineLP2Test is Test {
|
|||
createCSVHeader();
|
||||
}
|
||||
|
||||
function slide() internal {
|
||||
function slide(bool last) internal {
|
||||
// have some time pass to record prices in uni oracle
|
||||
uint256 timeBefore = block.timestamp;
|
||||
vm.warp(timeBefore + (60 * 60 * 5));
|
||||
|
|
@ -148,7 +148,9 @@ contract BaseLineLP2Test is Test {
|
|||
if (keccak256(abi.encodePacked(reason)) == keccak256(abi.encodePacked("amplitude not reached, diamond hands!"))) {
|
||||
console.log("slide failed on amplitude");
|
||||
} else {
|
||||
revert(reason); // Rethrow the error if it's not the expected message
|
||||
if (!last) {
|
||||
revert(reason); // Rethrow the error if it's not the expected message
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -158,7 +160,7 @@ contract BaseLineLP2Test is Test {
|
|||
uint256 timeBefore = block.timestamp;
|
||||
vm.warp(timeBefore + (60 * 60 * 5));
|
||||
|
||||
lm.shift();
|
||||
//lm.shift();
|
||||
try lm.shift() {
|
||||
// Check liquidity positions after shift
|
||||
(uint256 ethFloor, uint256 ethAnchor, uint256 ethDiscovery, uint256 harbFloor, uint256 harbAnchor, uint256 harbDiscovery) = checkLiquidityPositionsAfter("shift");
|
||||
|
|
@ -240,15 +242,12 @@ contract BaseLineLP2Test is Test {
|
|||
return x >= 0 ? uint(x) : uint(-x);
|
||||
}
|
||||
|
||||
event DEBUG(uint256 a, bool);
|
||||
function buy(uint256 amountEth) internal {
|
||||
emit DEBUG(amountEth, true);
|
||||
performSwap(amountEth, true);
|
||||
checkLiquidityPositionsAfter(string.concat("buy ", uintToStr(amountEth)));
|
||||
}
|
||||
|
||||
function sell(uint256 amountHarb) internal {
|
||||
emit DEBUG(amountHarb, false);
|
||||
performSwap(amountHarb, false);
|
||||
checkLiquidityPositionsAfter(string.concat("sell ", uintToStr(amountHarb)));
|
||||
}
|
||||
|
|
@ -434,15 +433,17 @@ contract BaseLineLP2Test is Test {
|
|||
// uint256 traderBalanceBefore = weth.balanceOf(account);
|
||||
|
||||
// // Setup initial liquidity
|
||||
// slide();
|
||||
// slide(false);
|
||||
|
||||
// buy(100 ether);
|
||||
// buy(200 ether);
|
||||
|
||||
// shift();
|
||||
|
||||
// //revert();
|
||||
|
||||
// sell(harb.balanceOf(account));
|
||||
|
||||
// slide();
|
||||
// slide(true);
|
||||
|
||||
// writeCsv();
|
||||
|
||||
|
|
@ -461,7 +462,7 @@ contract BaseLineLP2Test is Test {
|
|||
// uint256 traderBalanceBefore = weth.balanceOf(account);
|
||||
|
||||
// // Setup initial liquidity
|
||||
// slide();
|
||||
// slide(false);
|
||||
|
||||
// buy(2 ether);
|
||||
|
||||
|
|
@ -477,7 +478,7 @@ contract BaseLineLP2Test is Test {
|
|||
|
||||
// sell(harb.balanceOf(account));
|
||||
|
||||
// slide();
|
||||
// slide(true);
|
||||
|
||||
// writeCsv();
|
||||
// uint256 traderBalanceAfter = weth.balanceOf(account);
|
||||
|
|
@ -486,28 +487,26 @@ contract BaseLineLP2Test is Test {
|
|||
// assertGt(traderBalanceBefore, traderBalanceAfter, "trader should not have made profit");
|
||||
// }
|
||||
|
||||
event DEBUG2(int24 a, int24 b);
|
||||
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);
|
||||
|
||||
setUpCustomToken0(true);
|
||||
setUpCustomToken0(numActions % 2 == 0 ? true : false);
|
||||
vm.deal(account, 100 ether);
|
||||
vm.prank(account);
|
||||
weth.deposit{value: 100 ether}();
|
||||
|
||||
|
||||
// Setup initial liquidity
|
||||
slide();
|
||||
slide(false);
|
||||
|
||||
uint256 traderBalanceBefore = weth.balanceOf(account);
|
||||
uint8 f = 0;
|
||||
for (uint i = 0; i < numActions; i++) {
|
||||
uint256 amount = (uint256(amounts[i]) * 1 ether) + 1 ether;
|
||||
uint256 harbBal = harb.balanceOf(account);
|
||||
emit DEBUG(i, true);
|
||||
if (harbBal == 0) {
|
||||
amount = amount % (weth.balanceOf(account) / 2);
|
||||
amount = amount == 0 ? weth.balanceOf(account) : amount;
|
||||
|
|
@ -528,14 +527,12 @@ contract BaseLineLP2Test is Test {
|
|||
(, int24 currentTick, , , , , ) = pool.slot0();
|
||||
(, int24 tickLower, int24 tickUpper) = lm.positions(BaseLineLP.Stage.ANCHOR);
|
||||
int24 midTick = token0isWeth ? tickLower + ANCHOR_SPACING : tickUpper - ANCHOR_SPACING;
|
||||
emit DEBUG2(tickLower, tickUpper);
|
||||
emit DEBUG2(currentTick, midTick);
|
||||
if (currentTick < midTick) {
|
||||
// Current tick is below the midpoint, so call slide()
|
||||
token0isWeth ? shift(): slide();
|
||||
token0isWeth ? shift(): slide(false);
|
||||
} else if (currentTick > midTick) {
|
||||
// Current tick is above the midpoint, so call shift()
|
||||
token0isWeth ? slide(): shift();
|
||||
token0isWeth ? slide(false): shift();
|
||||
}
|
||||
f = 0;
|
||||
} else {
|
||||
|
|
@ -546,7 +543,7 @@ contract BaseLineLP2Test is Test {
|
|||
// Simulate large sell to push price down to floor
|
||||
sell(harb.balanceOf(account));
|
||||
|
||||
slide();
|
||||
slide(true);
|
||||
|
||||
uint256 traderBalanceAfter = weth.balanceOf(account);
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue