Add Solidity linting with solhint, Foundry formatter, and pre-commit hooks (#51)
## Changes ### Configuration - Added .solhint.json with recommended rules + custom config - 160 char line length (warn) - Double quotes enforcement (error) - Explicit visibility required (error) - Console statements allowed (scripts/tests need them) - Gas optimization warnings enabled - Ignores test/helpers/, lib/, out/, cache/, broadcast/ - Added foundry.toml [fmt] section - 160 char line length - 4-space tabs - Double quotes - Thousands separators for numbers - Sort imports enabled - Added .lintstagedrc.json for pre-commit auto-fix - Runs solhint --fix on .sol files - Runs forge fmt on .sol files - Added husky pre-commit hook via lint-staged ### NPM Scripts - lint:sol - run solhint - lint:sol:fix - auto-fix solhint issues - format:sol - format with forge fmt - format:sol:check - check formatting - lint / lint:fix - combined commands ### Code Changes - Added explicit visibility modifiers (internal) to constants in scripts and tests - Fixed quote style in DeployLocal.sol - All Solidity files formatted with forge fmt ## Verification - ✅ forge fmt --check passes - ✅ No solhint errors (warnings only) - ✅ forge build succeeds - ✅ forge test passes (107/107) resolves #44 Co-authored-by: johba <johba@harb.eth> Reviewed-on: https://codeberg.org/johba/harb/pulls/51
This commit is contained in:
parent
f8927b426e
commit
d7c2184ccf
45 changed files with 2853 additions and 1225 deletions
|
|
@ -1,28 +1,29 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
pragma solidity ^0.8.19;
|
||||
|
||||
import "@uniswap-v3-periphery/libraries/PositionKey.sol";
|
||||
import "@uniswap-v3-core/interfaces/IUniswapV3Pool.sol";
|
||||
import "@aperture/uni-v3-lib/PoolAddress.sol";
|
||||
import "@aperture/uni-v3-lib/CallbackValidation.sol";
|
||||
import "@openzeppelin/token/ERC20/IERC20.sol";
|
||||
import "./interfaces/IWETH9.sol";
|
||||
import {Kraiken} from "./Kraiken.sol";
|
||||
import {Optimizer} from "./Optimizer.sol";
|
||||
import "./abstracts/ThreePositionStrategy.sol";
|
||||
import { Kraiken } from "./Kraiken.sol";
|
||||
import { Optimizer } from "./Optimizer.sol";
|
||||
|
||||
import "./abstracts/PriceOracle.sol";
|
||||
import "./abstracts/ThreePositionStrategy.sol";
|
||||
import "./interfaces/IWETH9.sol";
|
||||
import "@aperture/uni-v3-lib/CallbackValidation.sol";
|
||||
import "@aperture/uni-v3-lib/PoolAddress.sol";
|
||||
import "@openzeppelin/token/ERC20/IERC20.sol";
|
||||
import "@uniswap-v3-core/interfaces/IUniswapV3Pool.sol";
|
||||
import "@uniswap-v3-periphery/libraries/PositionKey.sol";
|
||||
|
||||
/**
|
||||
* @title LiquidityManager
|
||||
* @notice Manages liquidity provisioning on Uniswap V3 using the three-position anti-arbitrage strategy
|
||||
* @dev Inherits from modular contracts for better separation of concerns and testability
|
||||
*
|
||||
*
|
||||
* Key features:
|
||||
* - Three-position anti-arbitrage strategy (ANCHOR, DISCOVERY, FLOOR)
|
||||
* - Dynamic parameter adjustment via Optimizer contract
|
||||
* - Asymmetric slippage profile prevents profitable arbitrage
|
||||
* - Exclusive minting rights for KRAIKEN token
|
||||
*
|
||||
*
|
||||
* Price Validation:
|
||||
* - 5-minute TWAP with 50-tick tolerance
|
||||
* - Prevents oracle manipulation attacks
|
||||
|
|
@ -74,17 +75,17 @@ contract LiquidityManager is ThreePositionStrategy, PriceOracle {
|
|||
/// @param amount1Owed Amount of token1 owed for the liquidity provision
|
||||
function uniswapV3MintCallback(uint256 amount0Owed, uint256 amount1Owed, bytes calldata) external {
|
||||
CallbackValidation.verifyCallback(factory, poolKey);
|
||||
|
||||
|
||||
// Handle KRAIKEN minting
|
||||
uint256 kraikenPulled = token0isWeth ? amount1Owed : amount0Owed;
|
||||
kraiken.mint(kraikenPulled);
|
||||
|
||||
|
||||
// Handle WETH conversion
|
||||
uint256 ethOwed = token0isWeth ? amount0Owed : amount1Owed;
|
||||
if (weth.balanceOf(address(this)) < ethOwed) {
|
||||
weth.deposit{value: address(this).balance}();
|
||||
weth.deposit{ value: address(this).balance }();
|
||||
}
|
||||
|
||||
|
||||
// Transfer tokens to pool
|
||||
if (amount0Owed > 0) IERC20(poolKey.token0).transfer(msg.sender, amount0Owed);
|
||||
if (amount1Owed > 0) IERC20(poolKey.token1).transfer(msg.sender, amount1Owed);
|
||||
|
|
@ -135,19 +136,14 @@ contract LiquidityManager is ThreePositionStrategy, PriceOracle {
|
|||
|
||||
// Remove all existing positions and collect fees
|
||||
_scrapePositions();
|
||||
|
||||
|
||||
// Update total supply tracking if price moved up
|
||||
if (isUp) {
|
||||
kraiken.setPreviousTotalSupply(kraiken.totalSupply());
|
||||
}
|
||||
|
||||
// Get optimizer parameters and set new positions
|
||||
try optimizer.getLiquidityParams() returns (
|
||||
uint256 capitalInefficiency,
|
||||
uint256 anchorShare,
|
||||
uint24 anchorWidth,
|
||||
uint256 discoveryDepth
|
||||
) {
|
||||
try optimizer.getLiquidityParams() returns (uint256 capitalInefficiency, uint256 anchorShare, uint24 anchorWidth, uint256 discoveryDepth) {
|
||||
// Clamp parameters to valid ranges
|
||||
PositionParams memory params = PositionParams({
|
||||
capitalInefficiency: (capitalInefficiency > 10 ** 18) ? 10 ** 18 : capitalInefficiency,
|
||||
|
|
@ -155,17 +151,17 @@ contract LiquidityManager is ThreePositionStrategy, PriceOracle {
|
|||
anchorWidth: (anchorWidth > 100) ? 100 : anchorWidth,
|
||||
discoveryDepth: (discoveryDepth > 10 ** 18) ? 10 ** 18 : discoveryDepth
|
||||
});
|
||||
|
||||
|
||||
_setPositions(currentTick, params);
|
||||
} catch {
|
||||
// Fallback to default parameters if optimizer fails
|
||||
PositionParams memory defaultParams = PositionParams({
|
||||
capitalInefficiency: 5 * 10 ** 17, // 50%
|
||||
anchorShare: 5 * 10 ** 17, // 50%
|
||||
anchorWidth: 50, // 50%
|
||||
discoveryDepth: 5 * 10 ** 17 // 50%
|
||||
});
|
||||
|
||||
capitalInefficiency: 5 * 10 ** 17, // 50%
|
||||
anchorShare: 5 * 10 ** 17, // 50%
|
||||
anchorWidth: 50, // 50%
|
||||
discoveryDepth: 5 * 10 ** 17 // 50%
|
||||
});
|
||||
|
||||
_setPositions(currentTick, defaultParams);
|
||||
}
|
||||
}
|
||||
|
|
@ -175,29 +171,20 @@ contract LiquidityManager is ThreePositionStrategy, PriceOracle {
|
|||
uint256 fee0 = 0;
|
||||
uint256 fee1 = 0;
|
||||
uint256 currentPrice;
|
||||
|
||||
|
||||
for (uint256 i = uint256(Stage.FLOOR); i <= uint256(Stage.DISCOVERY); i++) {
|
||||
TokenPosition storage position = positions[Stage(i)];
|
||||
if (position.liquidity > 0) {
|
||||
// Burn liquidity and collect tokens + fees
|
||||
(uint256 amount0, uint256 amount1) = pool.burn(
|
||||
position.tickLower,
|
||||
position.tickUpper,
|
||||
position.liquidity
|
||||
);
|
||||
|
||||
(uint256 collected0, uint256 collected1) = pool.collect(
|
||||
address(this),
|
||||
position.tickLower,
|
||||
position.tickUpper,
|
||||
type(uint128).max,
|
||||
type(uint128).max
|
||||
);
|
||||
(uint256 amount0, uint256 amount1) = pool.burn(position.tickLower, position.tickUpper, position.liquidity);
|
||||
|
||||
(uint256 collected0, uint256 collected1) =
|
||||
pool.collect(address(this), position.tickLower, position.tickUpper, type(uint128).max, type(uint128).max);
|
||||
|
||||
// Calculate fees
|
||||
fee0 += collected0 - amount0;
|
||||
fee1 += collected1 - amount1;
|
||||
|
||||
|
||||
// Record price from anchor position for VWAP
|
||||
if (i == uint256(Stage.ANCHOR)) {
|
||||
int24 tick = position.tickLower + ((position.tickUpper - position.tickLower) / 2);
|
||||
|
|
@ -215,7 +202,7 @@ contract LiquidityManager is ThreePositionStrategy, PriceOracle {
|
|||
IERC20(address(kraiken)).transfer(feeDestination, fee0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (fee1 > 0) {
|
||||
if (token0isWeth) {
|
||||
IERC20(address(kraiken)).transfer(feeDestination, fee1);
|
||||
|
|
@ -224,13 +211,13 @@ contract LiquidityManager is ThreePositionStrategy, PriceOracle {
|
|||
_recordVolumeAndPrice(currentPrice, fee1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Burn any remaining KRAIKEN tokens
|
||||
kraiken.burn(kraiken.balanceOf(address(this)));
|
||||
}
|
||||
|
||||
/// @notice Allow contract to receive ETH
|
||||
receive() external payable {}
|
||||
receive() external payable { }
|
||||
|
||||
// ========================================
|
||||
// ABSTRACT FUNCTION IMPLEMENTATIONS
|
||||
|
|
@ -259,11 +246,7 @@ contract LiquidityManager is ThreePositionStrategy, PriceOracle {
|
|||
/// @notice Implementation of abstract function from ThreePositionStrategy
|
||||
function _mintPosition(Stage stage, int24 tickLower, int24 tickUpper, uint128 liquidity) internal override {
|
||||
pool.mint(address(this), tickLower, tickUpper, liquidity, abi.encode(poolKey));
|
||||
positions[stage] = TokenPosition({
|
||||
liquidity: liquidity,
|
||||
tickLower: tickLower,
|
||||
tickUpper: tickUpper
|
||||
});
|
||||
positions[stage] = TokenPosition({ liquidity: liquidity, tickLower: tickLower, tickUpper: tickUpper });
|
||||
}
|
||||
|
||||
/// @notice Implementation of abstract function from ThreePositionStrategy
|
||||
|
|
@ -275,4 +258,4 @@ contract LiquidityManager is ThreePositionStrategy, PriceOracle {
|
|||
function _getOutstandingSupply() internal view override returns (uint256) {
|
||||
return kraiken.outstandingSupply();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue