Skip to content

Commit

Permalink
Merge 4c004d0 into f68718a
Browse files Browse the repository at this point in the history
  • Loading branch information
asselstine committed Jun 6, 2022
2 parents f68718a + 4c004d0 commit 65b1902
Show file tree
Hide file tree
Showing 14 changed files with 535 additions and 480 deletions.
169 changes: 70 additions & 99 deletions contracts/PrizePoolLiquidator.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,73 +5,56 @@ pragma solidity 0.8.6;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/utils/math/SafeCast.sol";
import "@openzeppelin/contracts/utils/math/SafeMath.sol";
import "@prb/math/contracts/PRBMath.sol";
import "@prb/math/contracts/PRBMathSD59x18Typed.sol";

import "./libraries/ExtendedSafeCastLib.sol";
import "./libraries/LiquidatorLib.sol";
import "./interfaces/IPrizePool.sol";
import "./interfaces/IPrizePoolLiquidatorListener.sol";

contract PrizePoolLiquidator {
using SafeMath for uint256;
using SafeCast for uint256;
using PRBMathSD59x18Typed for PRBMath.SD59x18;
using LiquidatorLib for LiquidatorLib.State;
using ExtendedSafeCastLib for uint256;

uint256 time;

struct Target {
struct LiquidatorConfig {
address target;
IERC20 want;
uint32 swapMultiplier;
uint32 liquidityFraction;
}

struct LiquidatorState {
uint256 reserveA;
uint256 reserveB;
}

// mapping from prize pool to tokens.
mapping(IPrizePool => Target) poolTargets;
mapping(IPrizePool => LiquidatorLib.State) poolLiquidatorStates;
mapping(IPrizePool => LiquidatorConfig) poolLiquidatorConfigs;
mapping(IPrizePool => LiquidatorState) poolLiquidatorStates;

IPrizePoolLiquidatorListener public listener;

function setPrizePool(
IPrizePool pool,
address target,
IERC20 want,
int256 exchangeRate,
int256 deltaRatePerSecond,
int256 maxSlippage
uint32 _swapMultiplier,
uint32 _liquidityFraction,
uint192 _reserveA,
uint192 _reserveB
) external returns (bool) {
return setPrizePoolAtTime(pool, target, want, exchangeRate, deltaRatePerSecond, maxSlippage, block.timestamp);
}

function setPrizePoolAtTime(
IPrizePool pool,
address target,
IERC20 want,
int256 exchangeRate,
int256 deltaRatePerSecond,
int256 maxSlippage,
uint256 currentTime
) public returns (bool) {
poolTargets[pool] = Target({
target: target,
want: want
poolLiquidatorConfigs[pool] = LiquidatorConfig({
target: target,
want: want,
swapMultiplier: _swapMultiplier,
liquidityFraction: _liquidityFraction
});
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)
poolLiquidatorStates[pool] = LiquidatorState({
reserveA: _reserveA,
reserveB: _reserveB
});
return true;
}

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

function availableBalanceOf(IPrizePool _prizePool) external returns (uint256) {
return _availableStreamHaveBalance(_prizePool);
}
Expand All @@ -80,88 +63,76 @@ contract PrizePoolLiquidator {
return _prizePool.captureAwardBalance();
}

function setTime(uint256 _time) external {
time = _time;
}

function currentExchangeRate(IPrizePool _prizePool) external view returns (int256) {
return poolLiquidatorStates[_prizePool].computeExchangeRate(block.timestamp).toInt();
function currentExchangeRate(IPrizePool _prizePool) external returns (uint256) {
LiquidatorState memory state = poolLiquidatorStates[_prizePool];
(uint256 reserveA, uint256 reserveB) = LiquidatorLib.prepareSwap(
state.reserveA,
state.reserveB,
_availableStreamHaveBalance(_prizePool)
);
return (reserveA*1e18) / reserveB;
}

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);
LiquidatorConfig memory config = poolLiquidatorConfigs[_prizePool];
LiquidatorState memory state = poolLiquidatorStates[_prizePool];
return LiquidatorLib.computeExactAmountIn(
state.reserveA, state.reserveB, _availableStreamHaveBalance(_prizePool), amountOut, config.swapMultiplier, config.liquidityFraction
);
}

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

function swapExactAmountIn(IPrizePool _prizePool, uint256 amountIn, uint256 amountOutMin) external returns (uint256) {
return _swapExactAmountInAtTime(_prizePool, amountIn, amountOutMin, block.timestamp);
LiquidatorConfig memory config = poolLiquidatorConfigs[_prizePool];
LiquidatorState memory state = poolLiquidatorStates[_prizePool];
return LiquidatorLib.computeExactAmountOut(
state.reserveA, state.reserveB, _availableStreamHaveBalance(_prizePool), amountIn, config.swapMultiplier, config.liquidityFraction
);
}

function _swapExactAmountInAtTime(IPrizePool _prizePool, uint256 amountIn, uint256 amountOutMin, uint256 currentTime) internal returns (uint256) {
function swapExactAmountIn(IPrizePool _prizePool, uint256 _amountIn, uint256 _amountOutMin) external returns (uint256) {
LiquidatorConfig memory config = poolLiquidatorConfigs[_prizePool];
LiquidatorState memory state = poolLiquidatorStates[_prizePool];
uint256 availableBalance = _availableStreamHaveBalance(_prizePool);
uint256 amountOut = poolLiquidatorStates[_prizePool].swapExactAmountInAtTime(
availableBalance, amountIn, currentTime
(uint256 reserveA, uint256 reserveB, uint256 amountOut) = LiquidatorLib.swapExactAmountIn(
state.reserveA, state.reserveB,
availableBalance, _amountIn, config.swapMultiplier, config.liquidityFraction
);

state.reserveA = reserveA;
state.reserveB = reserveB;
require(amountOut <= availableBalance, "Whoops! have exceeds available");
require(amountOut >= amountOutMin, "trade does not meet min");

_swap(_prizePool, msg.sender, amountOut, amountIn);

require(amountOut >= _amountOutMin, "trade does not meet min");
poolLiquidatorStates[_prizePool] = state;
_swap(_prizePool, config.want, config.target, msg.sender, amountOut, _amountIn);
return amountOut;
}

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

function _swapExactAmountOutAtTime(
IPrizePool _prizePool,
uint256 amountOut,
uint256 amountInMax,
uint256 currentTime
) internal returns (uint256) {
function swapExactAmountOut(IPrizePool _prizePool, uint256 _amountOut, uint256 _amountInMax) external returns (uint256) {
LiquidatorConfig memory config = poolLiquidatorConfigs[_prizePool];
LiquidatorState memory state = poolLiquidatorStates[_prizePool];
uint256 availableBalance = _availableStreamHaveBalance(_prizePool);
uint256 amountIn = poolLiquidatorStates[_prizePool].swapExactAmountOutAtTime(
availableBalance, amountOut, currentTime
(uint256 reserveA, uint256 reserveB, uint256 amountIn) = LiquidatorLib.swapExactAmountOut(
state.reserveA, state.reserveB,
availableBalance, _amountOut, config.swapMultiplier, config.liquidityFraction
);

require(amountIn <= amountInMax, "trade does not meet min");
require(amountOut <= availableBalance, "Whoops! have exceeds available");

_swap(_prizePool, msg.sender, amountOut, amountIn);

state.reserveA = reserveA;
state.reserveB = reserveB;
require(amountIn <= _amountInMax, "trade does not meet min");
require(_amountOut <= availableBalance, "Whoops! have exceeds available");
poolLiquidatorStates[_prizePool] = state;
_swap(_prizePool, config.want, config.target, msg.sender, _amountOut, amountIn);
return amountIn;
}

function _swap(IPrizePool _prizePool, address _account, uint256 _amountOut, uint256 _amountIn) internal {
Target storage target = poolTargets[_prizePool];
IERC20 want = target.want;
function _swap(IPrizePool _prizePool, IERC20 _want, address _target, address _account, uint256 _amountOut, uint256 _amountIn) internal {
_prizePool.award(_account, _amountOut);
want.transferFrom(_account, target.target, _amountIn);
_want.transferFrom(_account, _target, _amountIn);
IPrizePoolLiquidatorListener _listener = listener;
if (address(_listener) != address(0)) {
_listener.afterSwap(_prizePool, _prizePool.getTicket(), _amountOut, want, _amountIn);
_listener.afterSwap(_prizePool, _prizePool.getTicket(), _amountOut, _want, _amountIn);
}
}

function getLiquidationState(IPrizePool _prizePool) external view returns (
int exchangeRate,
uint256 lastSaleTime
) {
LiquidatorLib.State memory state = poolLiquidatorStates[_prizePool];
exchangeRate = state.exchangeRate.value;
lastSaleTime = state.lastSaleTime;
function getLiquidationState(IPrizePool _prizePool) external view returns (LiquidatorState memory state) {
return poolLiquidatorStates[_prizePool];
}
}
28 changes: 28 additions & 0 deletions contracts/libraries/CpmmLib.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// SPDX-License-Identifier: GPL-3.0

pragma solidity 0.8.6;

import "@openzeppelin/contracts/utils/math/SafeMath.sol";

library CpmmLib {
using SafeMath for uint256;

// given an input amount of an asset and pair reserves, returns the maximum output amount of the other asset
function getAmountOut(uint256 amountIn, uint256 reserveIn, uint256 reserveOut) internal pure returns (uint256 amountOut) {
// require(amountIn > 0, "CpmmLib: INSUFFICIENT_INPUT_AMOUNT");
require(reserveIn > 0 && reserveOut > 0, "CpmmLib: INSUFFICIENT_LIQUIDITY");
uint256 numerator = amountIn.mul(reserveOut);
uint256 denominator = reserveIn.add(amountIn);
return numerator / denominator;
}

// given an output amount of an asset and pair reserves, returns a required input amount of the other asset
function getAmountIn(uint256 amountOut, uint256 reserveIn, uint256 reserveOut) internal pure returns (uint256 amountIn) {
// require(amountOut > 0, "CpmmLib: INSUFFICIENT_OUTPUT_AMOUNT");
require(reserveIn > 0 && reserveOut > 0, "CpmmLib: INSUFFICIENT_LIQUIDITY");
uint256 numerator = reserveIn.mul(amountOut);
uint256 denominator = reserveOut.sub(amountOut);
amountIn = (numerator / denominator);
}

}
16 changes: 16 additions & 0 deletions contracts/libraries/ExtendedSafeCastLib.sol
Original file line number Diff line number Diff line change
Expand Up @@ -63,4 +63,20 @@ library ExtendedSafeCastLib {
require(_value <= type(uint224).max, "SafeCast: value doesn't fit in 224 bits");
return uint224(_value);
}

/**
* @dev Returns the downcasted uint192 from uint256, reverting on
* overflow (when the input is greater than largest uint192).
*
* Counterpart to Solidity's `uint192` operator.
*
* Requirements:
*
* - input must fit into 224 bits
*/
function toUint192(uint256 value) internal pure returns (uint192) {
require(value <= type(uint192).max, "SafeCast: value doesn't fit in 192 bits");
return uint192(value);
}

}
Loading

0 comments on commit 65b1902

Please sign in to comment.