implemented minStake
This commit is contained in:
parent
72abf097df
commit
b94f5559dd
4 changed files with 38 additions and 32 deletions
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue