added price oracle

This commit is contained in:
JulesCrown 2024-06-07 11:22:22 +02:00
parent 5d209e5e19
commit ec71bcd287
3 changed files with 83 additions and 7 deletions

View file

@ -24,6 +24,7 @@ contract BaseLineLP {
int24 constant TICK_SPACING = 200;
int24 constant ANCHOR_SPACING = 5 * TICK_SPACING;
int24 constant DISCOVERY_SPACING = 11000;
int24 constant MAX_TICK_DEVIATION = 50;
// default fee of 1%
uint24 constant FEE = uint24(10_000);
// uint256 constant FLOOR = 0;
@ -345,12 +346,27 @@ contract BaseLineLP {
}
}
function _checkPriceStability(int24 currentTick) internal view returns (bool) {
uint32 timeInterval = 300; // 5 minutes in seconds
uint32[] memory secondsAgo = new uint32[](2);
secondsAgo[0] = timeInterval; // 5 minutes ago
secondsAgo[1] = 0; // current block timestamp
(int56[] memory tickCumulatives,) = pool.observe(secondsAgo);
int56 tickCumulativeDiff = tickCumulatives[1] - tickCumulatives[0];
int24 averageTick = int24(tickCumulativeDiff / int56(int32(timeInterval)));
return (currentTick >= averageTick - MAX_TICK_DEVIATION && currentTick <= averageTick + MAX_TICK_DEVIATION);
}
// call this function when price has moved up 15%
function shift() external {
require(positions[Stage.ANCHOR].liquidity > 0, "Not initialized");
// Fetch the current tick from the Uniswap V3 pool
(uint160 sqrtPriceX96, int24 currentTick, , , , , ) = pool.slot0();
// TODO: check slippage with oracle
// check slippage with oracle
_checkPriceStability(currentTick);
// ## check price moved up
{
@ -406,7 +422,8 @@ contract BaseLineLP {
function slide() external {
// Fetch the current tick from the Uniswap V3 pool
(uint160 sqrtPriceX96, int24 currentTick, , , , , ) = pool.slot0();
// TODO: check slippage with oracle
// check slippage with oracle
_checkPriceStability(currentTick);
// ## check price moved down
if (positions[Stage.ANCHOR].liquidity > 0) {

View file

@ -11,6 +11,7 @@ import "./interfaces/IStake.sol";
import "./Harb.sol";
error ExceededAvailableStake(address receiver, uint256 stakeWanted, uint256 availableStake);
error TooMuchSnatch(address receiver, uint256 stakeWanted, uint256 availableStake, uint256 smallestShare);
contract Stake is IStake {
using Math for uint256;
@ -32,6 +33,7 @@ contract Stake is IStake {
event PositionCreated(uint256 indexed positionId, address indexed owner, uint256 share, uint32 creationTime, uint32 taxRate);
event TaxPaid(uint256 indexed positionId, address indexed owner, uint256 taxAmount);
event PositionRemoved(uint256 indexed positionId, uint256 share, uint32 lastTaxTime);
event PositionShrunk(uint256 indexed positionId, uint256 share, uint32 lastTaxTime, uint256 sharesTaken);
struct StakingPosition {
uint256 share;
@ -93,6 +95,8 @@ contract Stake is IStake {
return snatch(assets, receiver, taxRate, positionsToSnatch);
}
/**
* 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,8 +115,10 @@ contract Stake is IStake {
}
// TODO: check that position size is multiple of minStake
uint256 smallestPositionShare = totalSupply;
// run through all suggested positions to snatch
for (uint256 i = 0; i < positionsToSnatch.length; i++) {
for (uint256 i = 0; i < positionsToSnatch.length - 1; i++) {
StakingPosition storage pos = positions[positionsToSnatch[i]];
if (pos.creationTime == 0) {
//TODO:
@ -122,20 +128,48 @@ contract Stake is IStake {
if (taxRate <= pos.taxRate) {
revert TaxTooLow(receiver, taxRate, pos.taxRate, i);
}
if (pos.share < smallestPositionShare) {
smallestPositionShare = pos.share;
}
// dissolve position
// TODO: what if someone calls payTax and exitPosition in the same transaction?
_payTax(positionsToSnatch[i], pos, 0);
_exitPosition(positionsToSnatch[i], pos);
// TODO: exit positions partially, if needed
// TODO: avoid greeving where more positions are freed than needed.
}
// try to make a new position in the free space and hope it is big enough
uint256 availableStake = authorizedStake() - outstandingStake;
// handle last position
if (positionsToSnatch.length > 0) {
uint256 index = positionsToSnatch.length - 1;
StakingPosition storage pos = positions[positionsToSnatch[index]];
if (pos.creationTime == 0) {
//TODO:
revert PositionNotFound();
}
// check that tax lower
if (taxRate <= pos.taxRate) {
revert TaxTooLow(receiver, taxRate, pos.taxRate, index);
}
if (pos.share < smallestPositionShare) {
smallestPositionShare = pos.share;
}
// dissolve position
_payTax(positionsToSnatch[index], pos, 0);
uint256 lastBitNeeded = sharesWanted - availableStake;
_shrinkPosition(positionsToSnatch[index], pos, lastBitNeeded);
availableStake += lastBitNeeded;
}
if (sharesWanted > availableStake) {
revert ExceededAvailableStake(receiver, sharesWanted, availableStake);
}
// avoid greeving where more positions are freed than needed.
if (availableStake - sharesWanted > smallestPositionShare) {
revert TooMuchSnatch(receiver, sharesWanted, availableStake, smallestPositionShare);
}
// transfer
SafeERC20.safeTransferFrom(tokenContract, msg.sender, address(this), assets);
@ -210,7 +244,6 @@ contract Stake is IStake {
pos.lastTaxTime = uint32(block.timestamp);
} else {
// if nothing left over, liquidate position
// TODO: emit event
outstandingStake -= pos.share;
emit PositionRemoved(positionID, pos.share, pos.lastTaxTime);
delete pos.owner;
@ -227,4 +260,12 @@ contract Stake is IStake {
delete pos.creationTime;
SafeERC20.safeTransfer(tokenContract, owner, assets);
}
function _shrinkPosition(uint256 positionId, StakingPosition storage pos, uint256 sharesToTake) private {
require (sharesToTake > pos.share, "position too small");
pos.share -= sharesToTake;
uint256 assets = sharesToAssets(sharesToTake, Math.Rounding.Down);
emit PositionShrunk(positionId, pos.share, pos.lastTaxTime, sharesToTake);
SafeERC20.safeTransfer(tokenContract, pos.owner, assets);
}
}

View file

@ -98,6 +98,10 @@ contract BaseLineLPTest is PoolSerializer {
vm.expectRevert();
lm.shift();
// have some time pass to record prices in uni oracle
uint256 timeBefore = block.timestamp;
vm.warp(timeBefore + (60 * 60 * 5));
// Setup of liquidity
lm.slide();
appendPossitions(lm, pool, token0isWeth);
@ -114,6 +118,10 @@ contract BaseLineLPTest is PoolSerializer {
token0isWeth ? TickMath.MIN_SQRT_RATIO + 1 : TickMath.MAX_SQRT_RATIO - 1,
abi.encode(account, int256(0.5 ether), true)
);
// have some time pass to record prices in uni oracle
timeBefore = block.timestamp;
vm.warp(timeBefore + (60 * 60 * 5));
lm.shift();
appendPossitions(lm, pool, token0isWeth);
@ -126,6 +134,10 @@ contract BaseLineLPTest is PoolSerializer {
token0isWeth ? TickMath.MIN_SQRT_RATIO + 1 : TickMath.MAX_SQRT_RATIO - 1,
abi.encode(account, int256(3 ether), true)
);
// have some time pass to record prices in uni oracle
timeBefore = block.timestamp;
vm.warp(timeBefore + (60 * 60 * 5));
lm.shift();
appendPossitions(lm, pool, token0isWeth);
@ -141,6 +153,9 @@ contract BaseLineLPTest is PoolSerializer {
!token0isWeth ? TickMath.MIN_SQRT_RATIO + 1 : TickMath.MAX_SQRT_RATIO - 1,
abi.encode(account, int256(300000 ether), false)
);
// have some time pass to record prices in uni oracle
timeBefore = block.timestamp;
vm.warp(timeBefore + (60 * 60 * 5));
lm.slide();
appendPossitions(lm, pool, token0isWeth);
@ -157,6 +172,9 @@ contract BaseLineLPTest is PoolSerializer {
!token0isWeth ? TickMath.MIN_SQRT_RATIO + 1 : TickMath.MAX_SQRT_RATIO - 1,
abi.encode(account, int256(3600000 ether), false)
);
// have some time pass to record prices in uni oracle
timeBefore = block.timestamp;
vm.warp(timeBefore + (60 * 60 * 5));
// add to CSV
lm.slide();