This commit is contained in:
JulesCrown 2024-07-16 20:59:42 +02:00
parent 694afbe9fe
commit 9f03bd9f5d
3 changed files with 89 additions and 108 deletions

View file

@ -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

View file

@ -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;

View file

@ -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);
}
}