Skip to content

Commit

Permalink
Simplified Liquidator
Browse files Browse the repository at this point in the history
- User now sets an arb target and max slippage
- whenever user wants to swap, first it runs a buyback then swaps.
  • Loading branch information
asselstine committed May 19, 2022
1 parent 47106f3 commit 013ac74
Show file tree
Hide file tree
Showing 6 changed files with 126 additions and 179 deletions.
22 changes: 22 additions & 0 deletions ArbTarget.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
Slippage: 5%
Initial Arb Target: 500
Initial exchange rate: X


Then the arb target is when we expect them to buy to get a discount. It determines the LP size.

After a user purchases, we set the new arb target based on the current arb + previous.

Does this drive arb target to zero?

PRBMath.SD59x18 memory one = PRBMath.SD59x18(1 ether);

desiredExchangeRate = (1-slippage)*exchangeRate

want = arbTarget.div(desiredExchangeRate).div(
arbTarget.mul(exchangeRate).div(arbTarget.mul(desiredExchangeRate)).sub(one)
);

ay = bx + ab

b = (ay)/(x+a)
59 changes: 21 additions & 38 deletions contracts/PrizePoolLiquidator.sol
Original file line number Diff line number Diff line change
Expand Up @@ -36,39 +36,33 @@ contract PrizePoolLiquidator {
address target,
IERC20 want,
int256 exchangeRate,
int256 deltaRatePerSecond,
int256 maxSlippage
int256 maxSlippage,
uint256 haveArbTarget
) external returns (bool) {
return setPrizePoolAtTime(pool, target, want, exchangeRate, deltaRatePerSecond, maxSlippage, block.timestamp);
return setPrizePoolAtTime(pool, target, want, exchangeRate, maxSlippage, haveArbTarget);
}

function setPrizePoolAtTime(
IPrizePool pool,
address target,
IERC20 want,
int256 exchangeRate,
int256 deltaRatePerSecond,
int256 maxSlippage,
uint256 currentTime
uint256 haveArbTarget
) public returns (bool) {
poolTargets[pool] = Target({
target: target,
want: want
});
poolLiquidatorStates[pool] = LiquidatorLib.State({
exchangeRate: PRBMath.SD59x18(exchangeRate),
lastSaleTime: currentTime,
// positive price range change per second.
deltaRatePerSecond: PRBMath.SD59x18(deltaRatePerSecond),
// Price impact for purchase of accrued funds
// low slippage => higher frequency arbs, but it tracks the market rate slower (slower to change)
maxSlippage: PRBMath.SD59x18(maxSlippage)
maxSlippage: PRBMath.SD59x18(maxSlippage),
haveArbTarget: haveArbTarget
});
return true;
}

function setPrizePoolLiquidationState(IPrizePool _prizePool, int256 deltaRatePerSecond, int256 maxSlippage) external {
poolLiquidatorStates[_prizePool].deltaRatePerSecond = PRBMath.SD59x18(deltaRatePerSecond);
function setPrizePoolLiquidationState(IPrizePool _prizePool, int256 maxSlippage) external {
poolLiquidatorStates[_prizePool].maxSlippage = PRBMath.SD59x18(maxSlippage);
}

Expand All @@ -84,34 +78,26 @@ contract PrizePoolLiquidator {
time = _time;
}

function currentExchangeRate(IPrizePool _prizePool) external view returns (int256) {
return poolLiquidatorStates[_prizePool].computeExchangeRate(block.timestamp).toInt();
function currentExchangeRate(IPrizePool _prizePool) external returns (int256) {
return poolLiquidatorStates[_prizePool].computeExchangeRate(_availableStreamHaveBalance(_prizePool)).toInt();
}

function computeExactAmountIn(IPrizePool _prizePool, uint256 amountOut) external returns (uint256) {
return poolLiquidatorStates[_prizePool].computeExactAmountInAtTime(_availableStreamHaveBalance(_prizePool), amountOut, block.timestamp);
}

function computeExactAmountInAtTime(IPrizePool _prizePool, uint256 amountOut, uint256 currentTime) external returns (uint256) {
return poolLiquidatorStates[_prizePool].computeExactAmountInAtTime(_availableStreamHaveBalance(_prizePool), amountOut, currentTime);
return poolLiquidatorStates[_prizePool].computeExactAmountIn(_availableStreamHaveBalance(_prizePool), amountOut);
}

function computeExactAmountOut(IPrizePool _prizePool, uint256 amountIn) external returns (uint256) {
return poolLiquidatorStates[_prizePool].computeExactAmountOutAtTime(_availableStreamHaveBalance(_prizePool), amountIn, block.timestamp);
}

function computeExactAmountOutAtTime(IPrizePool _prizePool, uint256 amountIn, uint256 currentTime) external returns (uint256) {
return poolLiquidatorStates[_prizePool].computeExactAmountOutAtTime(_availableStreamHaveBalance(_prizePool), amountIn, currentTime);
return poolLiquidatorStates[_prizePool].computeExactAmountOut(_availableStreamHaveBalance(_prizePool), amountIn);
}

function swapExactAmountIn(IPrizePool _prizePool, uint256 amountIn, uint256 amountOutMin) external returns (uint256) {
return _swapExactAmountInAtTime(_prizePool, amountIn, amountOutMin, block.timestamp);
return _swapExactAmountIn(_prizePool, amountIn, amountOutMin);
}

function _swapExactAmountInAtTime(IPrizePool _prizePool, uint256 amountIn, uint256 amountOutMin, uint256 currentTime) internal returns (uint256) {
function _swapExactAmountIn(IPrizePool _prizePool, uint256 amountIn, uint256 amountOutMin) internal returns (uint256) {
uint256 availableBalance = _availableStreamHaveBalance(_prizePool);
uint256 amountOut = poolLiquidatorStates[_prizePool].swapExactAmountInAtTime(
availableBalance, amountIn, currentTime
uint256 amountOut = poolLiquidatorStates[_prizePool].swapExactAmountIn(
availableBalance, amountIn
);

require(amountOut <= availableBalance, "Whoops! have exceeds available");
Expand All @@ -123,18 +109,17 @@ contract PrizePoolLiquidator {
}

function swapExactAmountOut(IPrizePool _prizePool, uint256 amountOut, uint256 amountInMax) external returns (uint256) {
return _swapExactAmountOutAtTime(_prizePool, amountOut, amountInMax, block.timestamp);
return _swapExactAmountOut(_prizePool, amountOut, amountInMax);
}

function _swapExactAmountOutAtTime(
function _swapExactAmountOut(
IPrizePool _prizePool,
uint256 amountOut,
uint256 amountInMax,
uint256 currentTime
uint256 amountInMax
) internal returns (uint256) {
uint256 availableBalance = _availableStreamHaveBalance(_prizePool);
uint256 amountIn = poolLiquidatorStates[_prizePool].swapExactAmountOutAtTime(
availableBalance, amountOut, currentTime
uint256 amountIn = poolLiquidatorStates[_prizePool].swapExactAmountOut(
availableBalance, amountOut
);

require(amountIn <= amountInMax, "trade does not meet min");
Expand All @@ -157,11 +142,9 @@ contract PrizePoolLiquidator {
}

function getLiquidationState(IPrizePool _prizePool) external view returns (
int exchangeRate,
uint256 lastSaleTime
int exchangeRate
) {
LiquidatorLib.State memory state = poolLiquidatorStates[_prizePool];
exchangeRate = state.exchangeRate.value;
lastSaleTime = state.lastSaleTime;
}
}
102 changes: 34 additions & 68 deletions contracts/libraries/LiquidatorLib.sol
Original file line number Diff line number Diff line change
Expand Up @@ -16,107 +16,77 @@ library LiquidatorLib {
using PRBMathSD59x18Typed for PRBMath.SD59x18;

struct State {
PRBMath.SD59x18 exchangeRate;
uint256 lastSaleTime;
// positive price range change per second.
PRBMath.SD59x18 deltaRatePerSecond;
// Price impact for purchase of accrued funds
// low slippage => higher frequency arbs, but it tracks the market rate slower (slower to change)
PRBMath.SD59x18 maxSlippage;
PRBMath.SD59x18 exchangeRate;
uint256 haveArbTarget;
}

function _increaseExchangeRateByDeltaTime(
PRBMath.SD59x18 memory exchangeRate,
PRBMath.SD59x18 memory deltaRatePerSecond,
uint256 lastSaleTime,
uint256 currentTime
) internal pure returns (PRBMath.SD59x18 memory) {
// over time, buying power of POOL goes up.
PRBMath.SD59x18 memory dt = PRBMathSD59x18Typed.fromInt(
(currentTime - lastSaleTime).toInt256()
);
return exchangeRate.add(exchangeRate.mul(dt.mul(deltaRatePerSecond)));
}

function computeExchangeRate(State storage _liquidationState, uint256 _currentTime)
function computeExchangeRate(State storage _liquidationState, uint256 availableBalance)
internal
view
returns (PRBMath.SD59x18 memory)
{
return
_increaseExchangeRateByDeltaTime(
_liquidationState.exchangeRate,
_liquidationState.deltaRatePerSecond,
_liquidationState.lastSaleTime,
_currentTime
);
VirtualCpmmLib.Cpmm memory cpmm = _computeCpmm(_liquidationState, availableBalance);
return _cpmmToExchangeRate(cpmm);
}

function computeExactAmountInAtTime(
function computeExactAmountIn(
State storage _liquidationState,
uint256 availableBalance,
uint256 amountOut,
uint256 currentTime
uint256 amountOut
) internal pure returns (uint256) {
if (availableBalance == 0) {
return 0;
}
require(amountOut <= availableBalance, "insuff balance");
VirtualCpmmLib.Cpmm memory cpmm = _computeCpmm(
_liquidationState,
availableBalance,
currentTime
availableBalance
);
return VirtualCpmmLib.getAmountIn(amountOut, cpmm.want, cpmm.have);
}

function computeExactAmountOutAtTime(
function computeExactAmountOut(
State storage _liquidationState,
uint256 availableBalance,
uint256 amountIn,
uint256 currentTime
uint256 amountIn
) internal pure returns (uint256) {
if (availableBalance == 0) {
return 0;
}
VirtualCpmmLib.Cpmm memory cpmm = _computeCpmm(
_liquidationState,
availableBalance,
currentTime
availableBalance
);
return VirtualCpmmLib.getAmountOut(amountIn, cpmm.want, cpmm.have);
uint256 amountOut = VirtualCpmmLib.getAmountOut(amountIn, cpmm.want, cpmm.have);
require(amountOut <= availableBalance, "insuff balance");
return amountOut;
}

function _computeCpmm(
State storage _liquidationState,
uint256 availableBalance,
uint256 currentTime
uint256 availableBalance
) internal pure returns (VirtualCpmmLib.Cpmm memory) {
State memory liquidationState = _liquidationState;
PRBMath.SD59x18 memory newExchangeRate = _increaseExchangeRateByDeltaTime(
VirtualCpmmLib.Cpmm memory cpmm = VirtualCpmmLib.newCpmm(
liquidationState.maxSlippage,
liquidationState.exchangeRate,
liquidationState.deltaRatePerSecond,
liquidationState.lastSaleTime,
currentTime
PRBMathSD59x18Typed.fromInt(liquidationState.haveArbTarget.toInt256())
);
return
VirtualCpmmLib.newCpmm(
liquidationState.maxSlippage,
newExchangeRate,
PRBMathSD59x18Typed.fromInt(availableBalance.toInt256())
);

// Now we swap available balance for POOL

uint256 wantAmount = VirtualCpmmLib.getAmountOut(availableBalance, cpmm.have, cpmm.want);

cpmm.want -= wantAmount;
cpmm.have += availableBalance;

return cpmm;
}

function swapExactAmountInAtTime(
function swapExactAmountIn(
State storage liquidationState,
uint256 availableBalance,
uint256 amountIn,
uint256 currentTime
uint256 amountIn
) internal returns (uint256) {
require(availableBalance > 0, "Whoops! no funds available");
VirtualCpmmLib.Cpmm memory cpmm = _computeCpmm(
liquidationState,
availableBalance,
currentTime
availableBalance
);

uint256 amountOut = VirtualCpmmLib.getAmountOut(amountIn, cpmm.want, cpmm.have);
Expand All @@ -125,23 +95,20 @@ library LiquidatorLib {

require(amountOut <= availableBalance, "Whoops! have exceeds available");

liquidationState.lastSaleTime = currentTime;
liquidationState.exchangeRate = _cpmmToExchangeRate(cpmm);

return amountOut;
}

function swapExactAmountOutAtTime(
function swapExactAmountOut(
State storage liquidationState,
uint256 availableBalance,
uint256 amountOut,
uint256 currentTime
uint256 amountOut
) internal returns (uint256) {
require(availableBalance > 0, "Whoops! no funds available");
VirtualCpmmLib.Cpmm memory cpmm = _computeCpmm(
liquidationState,
availableBalance,
currentTime
availableBalance
);

uint256 amountIn = VirtualCpmmLib.getAmountIn(amountOut, cpmm.want, cpmm.have);
Expand All @@ -150,7 +117,6 @@ library LiquidatorLib {

require(amountOut <= availableBalance, "Whoops! have exceeds available");

liquidationState.lastSaleTime = currentTime;
liquidationState.exchangeRate = _cpmmToExchangeRate(cpmm);

return amountIn;
Expand Down
13 changes: 0 additions & 13 deletions contracts/test/PrizePoolLiquidatorHarness.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,4 @@ pragma solidity 0.8.6;
import "../PrizePoolLiquidator.sol";

contract PrizePoolLiquidatorHarness is PrizePoolLiquidator {

function swapExactAmountInAtTime(IPrizePool _prizePool, uint256 amountIn, uint256 amountOutMin, uint256 currentTime) external returns (uint256) {
return _swapExactAmountInAtTime(_prizePool, amountIn, amountOutMin, currentTime);
}

function swapExactAmountOutAtTime(
IPrizePool _prizePool,
uint256 amountOut,
uint256 amountInMax,
uint256 currentTime
) external returns (uint256) {
return _swapExactAmountOutAtTime(_prizePool, amountOut, amountInMax, currentTime);
}
}
Loading

0 comments on commit 013ac74

Please sign in to comment.