From 9f03bd9f5d1ad0f63ebca1e8b0e5f2a3446721c2 Mon Sep 17 00:00:00 2001 From: JulesCrown Date: Tue, 16 Jul 2024 20:59:42 +0200 Subject: [PATCH] cleanup --- onchain/README.md | 10 +- onchain/src/LiquidityManager.sol | 17 +--- onchain/src/Stake.sol | 170 +++++++++++++++---------------- 3 files changed, 89 insertions(+), 108 deletions(-) diff --git a/onchain/README.md b/onchain/README.md index 5900fc0..c860d7b 100644 --- a/onchain/README.md +++ b/onchain/README.md @@ -143,24 +143,16 @@ open features: - coverage - overflows - reentry - - definition of severity and rewards - - Minor: Results in some unexpected or undesired behavior or disrupts a system function, causing at least minor loss of funds for user or Liquidity Manager. - reward 0.5% share of fees - - Major: Bug capable of creating a severe loss of funds for HARB holders or collapsing large parts of the system - reward: 2% share of fees - - Critical: Bug capable of triggering severe loss of funds for LiquidityManager contract or complete shutdown in Harb/Stake/LiquidityManager contracts - reward: 5% - - limitations: max 10% share of fees, no seat in multisig - HARB - mint - limit supply to 2^96? - Stake - - sharesToAssets - should the average total supply be used for this calculation? - what if someone calls payTax and exitPosition in the same transaction? - - LiquidityManager - - add events - what to do with stuck funds if slide/shift become inoperable? - _isPriceStable - // Handle try catch, possibly by trying with a different time interval or providing a default response + - make slide and shift one function - -- find a way to account harb/eth for attacker - NFT support of etherscan https://etherscan.io/nft/0xe12edaab53023c75473a5a011bdb729ee73545e8/4218 \ No newline at end of file diff --git a/onchain/src/LiquidityManager.sol b/onchain/src/LiquidityManager.sol index f39d144..082e8ec 100644 --- a/onchain/src/LiquidityManager.sol +++ b/onchain/src/LiquidityManager.sol @@ -211,7 +211,7 @@ contract LiquidityManager { /// @dev Recalculates and realigns all liquidity positions according to the latest market data and strategic requirements. function _set(int24 currentTick) internal { - // ### set Floor position + // set Floor position int24 vwapTick; { uint256 outstandingSupply = harb.outstandingSupply(); @@ -257,7 +257,6 @@ contract LiquidityManager { uint160 sqrtRatioAX96 = TickMath.getSqrtRatioAtTick(vwapTick); int24 floorTick = token0isWeth ? vwapTick + TICK_SPACING: vwapTick - TICK_SPACING; uint160 sqrtRatioBX96 = TickMath.getSqrtRatioAtTick(floorTick); - uint128 liquidity; if (token0isWeth) { liquidity = LiquidityAmounts.getLiquidityForAmount0( @@ -268,20 +267,10 @@ contract LiquidityManager { sqrtRatioAX96, sqrtRatioBX96, requiredEthForBuyback ); } - - // uint128 liquidity = LiquidityAmounts.getLiquidityForAmounts( - // sqrtPriceX96, - // sqrtRatioAX96, - // sqrtRatioBX96, - // token0isWeth ? requiredEthForBuyback : 0, - // token0isWeth ? 0 : requiredEthForBuyback - // ); - - // mint _mint(Stage.FLOOR, token0isWeth ? vwapTick : floorTick, token0isWeth ? floorTick : vwapTick, liquidity); } - // ### set Anchor position + // set Anchor position uint128 anchorLiquidity; uint24 anchorWidth; { @@ -310,7 +299,7 @@ contract LiquidityManager { } currentTick = currentTick / TICK_SPACING * TICK_SPACING; - // ## set Discovery position + // set Discovery position { int24 tickLower = token0isWeth ? currentTick - DISCOVERY_SPACING - ANCHOR_SPACING : currentTick + ANCHOR_SPACING; int24 tickUpper = token0isWeth ? currentTick - ANCHOR_SPACING : currentTick + DISCOVERY_SPACING + ANCHOR_SPACING; diff --git a/onchain/src/Stake.sol b/onchain/src/Stake.sol index 0337b6a..6a316d0 100644 --- a/onchain/src/Stake.sol +++ b/onchain/src/Stake.sol @@ -86,6 +86,61 @@ contract Stake { return totalSupply * MAX_STAKE / 100; } + /// @dev Internal function to calculate and pay taxes for a position, adjusting shares and handling position liquidation if necessary. + function _payTax(uint256 positionId, StakingPosition storage pos, uint256 taxFloorDuration) private { + // ihet = Implied Holding Expiry Timestamp + uint256 ihet = (block.timestamp - pos.creationTime < taxFloorDuration) + ? pos.creationTime + taxFloorDuration + : block.timestamp; + uint256 elapsedTime = ihet - pos.lastTaxTime; + uint256 assetsBefore = sharesToAssets(pos.share); + uint256 taxAmountDue = assetsBefore * TAX_RATES[pos.taxRate] * elapsedTime / (365 * 24 * 60 * 60) / TAX_RATE_BASE; + if (taxAmountDue >= assetsBefore) { + // can not pay more tax than value of position + taxAmountDue = assetsBefore; + } + SafeERC20.safeTransfer(harb, taxPool, taxAmountDue); + + if (assetsBefore - taxAmountDue > 0) { + // if something left over, update storage + uint256 shareAfterTax = assetsToShares(assetsBefore - taxAmountDue); + outstandingStake -= pos.share - shareAfterTax; + pos.share = shareAfterTax; + pos.lastTaxTime = uint32(block.timestamp); + emit PositionTaxPaid(positionId, pos.owner, taxAmountDue, shareAfterTax, pos.taxRate); + } else { + // if nothing left over, liquidate position + outstandingStake -= pos.share; + emit PositionTaxPaid(positionId, pos.owner, taxAmountDue, 0, pos.taxRate); + emit PositionRemoved(positionId, pos.owner, 0); + delete pos.owner; + delete pos.creationTime; + delete pos.share; + } + } + + /// @dev Internal function to close a staking position, transferring the remaining Harb tokens back to the owner after tax payment. + function _exitPosition(uint256 positionId, StakingPosition storage pos) private { + outstandingStake -= pos.share; + address owner = pos.owner; + uint256 assets = sharesToAssets(pos.share); + emit PositionRemoved(positionId, owner, assets); + delete pos.owner; + delete pos.creationTime; + delete pos.share; + SafeERC20.safeTransfer(harb, owner, assets); + } + + /// @dev Internal function to reduce the size of a staking position by a specified number of shares, transferring the corresponding Harb tokens to the owner. + function _shrinkPosition(uint256 positionId, StakingPosition storage pos, uint256 sharesToTake) private { + require (sharesToTake < pos.share, "position too small"); + uint256 assets = sharesToAssets(sharesToTake); + pos.share -= sharesToTake; + outstandingStake -= sharesToTake; + emit PositionShrunk(positionId, pos.owner, pos.share, assets); + SafeERC20.safeTransfer(harb, pos.owner, assets); + } + /// @notice Converts Harb token assets to shares of the total staking pool. /// @param assets Number of Harb tokens to convert. /// @return Number of shares corresponding to the input assets based on the current total supply of Harb tokens. @@ -100,33 +155,6 @@ contract Stake { return shares.mulDiv(harb.totalSupply(), totalSupply, Math.Rounding.Down); } - /// @notice Combines an ERC20 permit operation with the snatch function, allowing a staking position creation in one transaction. - /// @param assets Number of Harb tokens to stake. - /// @param receiver Address that will own the new staking position. - /// @param taxRate The initial tax rate for the new staking position. - /// @param positionsToSnatch Array of position IDs that the new position will replace by snatching. - /// @param deadline Time until which the permit is valid. - /// @param v, r, s Components of the signature for the permit. - /// @return positionId The ID of the newly created staking position. - function permitAndSnatch( - uint256 assets, - address receiver, - uint32 taxRate, - uint256[] calldata positionsToSnatch, - // address owner, - // address spender, - // uint256 value, - uint256 deadline, - uint8 v, - bytes32 r, - bytes32 s - ) external - returns (uint256 positionId) - { - ERC20Permit(address(harb)).permit(receiver, address(this), assets, deadline, v, r, s); - return snatch(assets, receiver, taxRate, positionsToSnatch); - } - /// @notice Creates a new staking position by potentially snatching shares from existing positions. /// @param assets Amount of Harb tokens to convert into a staking position. /// @param receiver Address that will own the new staking position. @@ -227,11 +255,38 @@ contract Stake { emit PositionCreated(positionId, sp.owner, assets, sp.share, sp.taxRate); } + /// @notice Combines an ERC20 permit operation with the snatch function, allowing a staking position creation in one transaction. + /// @param assets Number of Harb tokens to stake. + /// @param receiver Address that will own the new staking position. + /// @param taxRate The initial tax rate for the new staking position. + /// @param positionsToSnatch Array of position IDs that the new position will replace by snatching. + /// @param deadline Time until which the permit is valid. + /// @param v, r, s Components of the signature for the permit. + /// @return positionId The ID of the newly created staking position. + function permitAndSnatch( + uint256 assets, + address receiver, + uint32 taxRate, + uint256[] calldata positionsToSnatch, + // address owner, + // address spender, + // uint256 value, + uint256 deadline, + uint8 v, + bytes32 r, + bytes32 s + ) external + returns (uint256 positionId) + { + ERC20Permit(address(harb)).permit(receiver, address(this), assets, deadline, v, r, s); + return snatch(assets, receiver, taxRate, positionsToSnatch); + } + /// @notice Changes the tax rate of an existing staking position. /// @param positionId The ID of the staking position to update. /// @param taxRate The new tax rate to apply to the position. /// @dev Ensures that the tax rate change is valid and applies the minimum tax based on the TAX_FLOOR_DURATION. - function changeTax(uint256 positionId, uint32 taxRate) public { + function changeTax(uint256 positionId, uint32 taxRate) external { require(taxRate < TAX_RATES.length, "tax rate out of bounds"); StakingPosition storage pos = positions[positionId]; if (pos.creationTime == 0) { @@ -250,7 +305,7 @@ contract Stake { /// @notice Allows the owner of a staking position to exit, returning the staked assets. /// @param positionId The ID of the staking position to exit. /// @dev Pays the due taxes based on the TAX_FLOOR_DURATION and returns the remaining assets to the position owner. - function exitPosition(uint256 positionId) public { + function exitPosition(uint256 positionId) external { StakingPosition storage pos = positions[positionId]; if (pos.creationTime == 0) { revert PositionNotFound(positionId, msg.sender); @@ -266,7 +321,7 @@ contract Stake { /// @notice Manually triggers the tax payment for a specified staking position. /// @param positionId The ID of the staking position for which to pay taxes. /// @dev Calculates and pays the tax due, possibly adjusting the position's share count. - function payTax(uint256 positionId) public { + function payTax(uint256 positionId) external { StakingPosition storage pos = positions[positionId]; _payTax(positionId, pos, 0); } @@ -285,59 +340,4 @@ contract Stake { uint256 assetsBefore = sharesToAssets(pos.share); amountDue = assetsBefore * TAX_RATES[pos.taxRate] * elapsedTime / (365 * 24 * 60 * 60) / TAX_RATE_BASE; } - - /// @dev Internal function to calculate and pay taxes for a position, adjusting shares and handling position liquidation if necessary. - function _payTax(uint256 positionId, StakingPosition storage pos, uint256 taxFloorDuration) private { - // ihet = Implied Holding Expiry Timestamp - uint256 ihet = (block.timestamp - pos.creationTime < taxFloorDuration) - ? pos.creationTime + taxFloorDuration - : block.timestamp; - uint256 elapsedTime = ihet - pos.lastTaxTime; - uint256 assetsBefore = sharesToAssets(pos.share); - uint256 taxAmountDue = assetsBefore * TAX_RATES[pos.taxRate] * elapsedTime / (365 * 24 * 60 * 60) / TAX_RATE_BASE; - if (taxAmountDue >= assetsBefore) { - // can not pay more tax than value of position - taxAmountDue = assetsBefore; - } - SafeERC20.safeTransfer(harb, taxPool, taxAmountDue); - - if (assetsBefore - taxAmountDue > 0) { - // if something left over, update storage - uint256 shareAfterTax = assetsToShares(assetsBefore - taxAmountDue); - outstandingStake -= pos.share - shareAfterTax; - pos.share = shareAfterTax; - pos.lastTaxTime = uint32(block.timestamp); - emit PositionTaxPaid(positionId, pos.owner, taxAmountDue, shareAfterTax, pos.taxRate); - } else { - // if nothing left over, liquidate position - outstandingStake -= pos.share; - emit PositionTaxPaid(positionId, pos.owner, taxAmountDue, 0, pos.taxRate); - emit PositionRemoved(positionId, pos.owner, 0); - delete pos.owner; - delete pos.creationTime; - delete pos.share; - } - } - - /// @dev Internal function to close a staking position, transferring the remaining Harb tokens back to the owner after tax payment. - function _exitPosition(uint256 positionId, StakingPosition storage pos) private { - outstandingStake -= pos.share; - address owner = pos.owner; - uint256 assets = sharesToAssets(pos.share); - emit PositionRemoved(positionId, owner, assets); - delete pos.owner; - delete pos.creationTime; - delete pos.share; - SafeERC20.safeTransfer(harb, owner, assets); - } - - /// @dev Internal function to reduce the size of a staking position by a specified number of shares, transferring the corresponding Harb tokens to the owner. - function _shrinkPosition(uint256 positionId, StakingPosition storage pos, uint256 sharesToTake) private { - require (sharesToTake < pos.share, "position too small"); - uint256 assets = sharesToAssets(sharesToTake); - pos.share -= sharesToTake; - outstandingStake -= sharesToTake; - emit PositionShrunk(positionId, pos.owner, pos.share, assets); - SafeERC20.safeTransfer(harb, pos.owner, assets); - } }