implemented minStake

This commit is contained in:
JulesCrown 2024-06-13 10:50:09 +02:00
parent 72abf097df
commit b94f5559dd
4 changed files with 38 additions and 32 deletions

View file

@ -386,6 +386,7 @@ contract BaseLineLP {
}
// call this function when price has moved up 15%
// TODO: write a bot that calls this function regularly
function shift() external {
require(positions[Stage.ANCHOR].liquidity > 0, "Not initialized");
// Fetch the current tick from the Uniswap V3 pool
@ -425,6 +426,7 @@ contract BaseLineLP {
ethInAnchor = (ethInAnchor > ethBalance / 10) ? ethBalance / 10 : ethInAnchor;
currentTick = currentTick / TICK_SPACING * TICK_SPACING;
harb.setPreviousTotalSupply(harb.totalSupply());
_set(sqrtPriceX96, currentTick, ethInAnchor);
}

View file

@ -39,6 +39,8 @@ contract Harb is ERC20, ERC20Permit {
uint256 public sumTaxCollected;
uint256 public previousTotalSupply;
struct UbiTitle {
uint256 sumTaxCollected;
uint256 time;
@ -107,6 +109,10 @@ contract Harb is ERC20, ERC20Permit {
_burn(address(liquidityManager), _amount);
}
function setPreviousTotalSupply(uint256 _ts) external onlyLiquidityManager {
previousTotalSupply = _ts;
}
/* ============ Public ERC20 Overrides ============ */
/// @inheritdoc ERC20

View file

@ -20,7 +20,7 @@ contract Stake is IStake {
uint256 internal constant MAX_STAKE = 20; // 20% of HARB supply
uint256 internal constant TAX_RATE_BASE = 100;
uint256 internal constant TAX_FLOOR_DURATION = 60 * 60 * 24 * 3; //this duration is the minimum basis for fee calculation, regardless of actual holding time.
uint256[] public TAX_RATES = [1, 3, 5, 8, 12, 18, 24, 30, 40, 50, 60, 80, 100, 130, 180, 250, 320, 420, 540, 700, 920, 1200, 1600]; //TODO: increate until 3 days take all position
uint256[] public TAX_RATES = [1, 3, 5, 8, 12, 18, 24, 30, 40, 50, 60, 80, 100, 130, 180, 250, 320, 420, 540, 700, 920, 1200, 1600, 2000, 2600, 3400, 4400, 5700, 7500, 9700];
/**
* @dev Attempted to deposit more assets than the max amount for `receiver`.
*/
@ -44,18 +44,17 @@ contract Stake is IStake {
}
uint256 public immutable totalSupply;
IERC20Metadata private immutable tokenContract;
Harb private immutable harb;
address private immutable taxPool;
uint256 public outstandingStake;
uint256 public nextPositionId;
uint256 public minStake; // TODO: handle this
mapping(uint256 => StakingPosition) public positions;
constructor(address _tokenContract) {
tokenContract = IERC20Metadata(_tokenContract);
constructor(address _harb) {
harb = Harb(_harb);
totalSupply = 10 ** (tokenContract.decimals() + DECIMAL_OFFSET);
taxPool = Harb(_tokenContract).TAX_POOL();
totalSupply = 10 ** (harb.decimals() + DECIMAL_OFFSET);
taxPool = Harb(_harb).TAX_POOL();
nextPositionId = 654321;
}
@ -68,13 +67,14 @@ contract Stake is IStake {
}
function assetsToShares(uint256 assets, Math.Rounding rounding) private view returns (uint256) {
return assets.mulDiv(totalSupply, tokenContract.totalSupply(), rounding);
//return assets.mulDiv(totalSupply, tokenContract.totalSupply() + 1, rounding);
return assets.mulDiv(totalSupply, harb.totalSupply(), rounding);
//return assets.mulDiv(totalSupply, harb.totalSupply() + 1, rounding);
}
function sharesToAssets(uint256 shares, Math.Rounding rounding) private view returns (uint256) {
//return shares.mulDiv(tokenContract.totalSupply() + 1, totalSupply, rounding);
return shares.mulDiv(tokenContract.totalSupply(), totalSupply, rounding);
//return shares.mulDiv(harb.totalSupply() + 1, totalSupply, rounding);
// TODO: should the average total supply be used for this calculation?
return shares.mulDiv(harb.totalSupply(), totalSupply, rounding);
}
function permitAndSnatch(
@ -92,12 +92,10 @@ contract Stake is IStake {
) external
returns (uint256 positionId)
{
ERC20Permit(address(tokenContract)).permit(receiver, address(this), assets, deadline, v, r, s);
ERC20Permit(address(harb)).permit(receiver, address(this), assets, deadline, v, r, s);
return snatch(assets, receiver, taxRate, positionsToSnatch);
}
event DEBUG(uint256 line);
/**
* TODO: deal with metatransactions: While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
@ -111,19 +109,19 @@ contract Stake is IStake {
{
// check lower boundary
uint256 sharesWanted = assetsToShares(assets, Math.Rounding.Down);
if (sharesWanted < minStake) {
revert SharesTooLow(receiver, assets, sharesWanted, minStake);
{
// check that position size is multiple of minStake
// to prevent excessive fragmentation, increasing snatch cost
uint256 minStake = harb.previousTotalSupply() / 3000;
if (sharesWanted < minStake) {
revert SharesTooLow(receiver, assets, sharesWanted, minStake);
}
}
emit DEBUG(authorizedStake());
// TODO: check that position size is multiple of minStake
// TODO: check that tax rate within limits of array
require(taxRate < TAX_RATES.length, "tax rate out of bounds");
uint256 smallestPositionShare = totalSupply;
emit DEBUG(outstandingStake);
uint256 availableStake = authorizedStake() - outstandingStake;
emit DEBUG(2);
if (positionsToSnatch.length >= 2) {
// run through all but last positions to snatch
for (uint256 i = 0; i < positionsToSnatch.length - 1; i++) {
@ -164,8 +162,6 @@ contract Stake is IStake {
}
// dissolve position
_payTax(positionsToSnatch[index], lastPos, 0);
emit DEBUG(sharesWanted);
emit DEBUG(availableStake);
if (availableStake > sharesWanted) {
revert TooMuchSnatch(receiver, sharesWanted, availableStake, smallestPositionShare);
}
@ -188,7 +184,7 @@ contract Stake is IStake {
}
// transfer
SafeERC20.safeTransferFrom(tokenContract, msg.sender, address(this), assets);
SafeERC20.safeTransferFrom(harb, msg.sender, address(this), assets);
// mint
positionId = nextPositionId++;
@ -204,12 +200,13 @@ contract Stake is IStake {
}
function changeTax(uint256 positionID, uint32 taxRate) public {
require(taxRate < TAX_RATES.length, "tax rate out of bounds");
StakingPosition storage pos = positions[positionID];
if (pos.owner != msg.sender) {
revert NoPermission(msg.sender, pos.owner);
}
// to prevent snatch-and-change grieving attack, pay TAX_FLOOR_DURATION
require(taxRate > pos.taxRate);
require(taxRate > pos.taxRate, "tax too low to snatch");
_payTax(positionID, pos, TAX_FLOOR_DURATION);
pos.taxRate = taxRate;
}
@ -224,6 +221,7 @@ contract Stake is IStake {
_exitPosition(positionId, pos);
}
// TODO: write a bot that calls this function regularly
function payTax(uint256 positionID) public {
StakingPosition storage pos = positions[positionID];
// TODO: what if someone calls payTax and exitPosition in the same transaction?
@ -253,7 +251,7 @@ contract Stake is IStake {
// can not pay more tax than value of position
taxAmountDue = assetsBefore;
}
SafeERC20.safeTransfer(tokenContract, taxPool, taxAmountDue);
SafeERC20.safeTransfer(harb, taxPool, taxAmountDue);
emit TaxPaid(positionID, pos.owner, taxAmountDue);
if (assetsBefore - taxAmountDue > 0) {
// if something left over, update storage
@ -277,7 +275,7 @@ contract Stake is IStake {
emit PositionRemoved(positionId, pos.share, pos.lastTaxTime);
delete pos.owner;
delete pos.creationTime;
SafeERC20.safeTransfer(tokenContract, owner, assets);
SafeERC20.safeTransfer(harb, owner, assets);
}
function _shrinkPosition(uint256 positionId, StakingPosition storage pos, uint256 sharesToTake) private {
@ -285,6 +283,6 @@ contract Stake is IStake {
uint256 assets = sharesToAssets(sharesToTake, Math.Rounding.Down);
pos.share -= sharesToTake;
emit PositionShrunk(positionId, pos.share, pos.lastTaxTime, sharesToTake);
SafeERC20.safeTransfer(tokenContract, pos.owner, assets);
SafeERC20.safeTransfer(harb, pos.owner, assets);
}
}

View file

@ -99,7 +99,7 @@ contract HarbTest is Test {
assertEq(harb.balanceOf(account), amount * 4, "balance should match after stake");
assertEq(harb.balanceOf(address(stake)), amount, "balance should match after stake");
// check stake position
(uint256 share, address owner, uint32 creationTime, uint32 lastTaxTime, uint32 taxRate) = stake.positions(0);
(uint256 share, address owner, uint32 creationTime, uint32 lastTaxTime, uint32 taxRate) = stake.positions(654321);
assertEq(share, stake.totalSupply() / 5, "share should match");
assertEq(owner, account, "owners should match");
assertEq(creationTime, block.timestamp, "time should match");
@ -122,11 +122,11 @@ contract HarbTest is Test {
// advance the time
uint256 timeBefore = block.timestamp;
vm.warp(timeBefore + (60 * 60 * 24 * 4));
uint256 taxDue = stake.taxDue(0, 60 * 60 * 24 * 3);
uint256 taxDue = stake.taxDue(654321, 60 * 60 * 24 * 3);
uint256 sumTaxCollectedBefore = harb.sumTaxCollected();
vm.prank(account);
stake.exitPosition(0);
stake.exitPosition(654321);
assertApproxEqRel(harb.balanceOf(account), amount * 5 - taxDue, 1e14, "account balance should match");
assertEq(harb.balanceOf(TAX_POOL), taxDue, "tax pool balance should match");
assertEq(sumTaxCollectedBefore + taxDue, harb.sumTaxCollected(), "collected tax should have increased");