Skip to content

Commit

Permalink
Finish support for SlipStream.
Browse files Browse the repository at this point in the history
  • Loading branch information
gnarlycow committed Jul 27, 2024
1 parent c207a54 commit 0642c4e
Show file tree
Hide file tree
Showing 26 changed files with 514 additions and 1,779 deletions.
8 changes: 8 additions & 0 deletions contracts/Dex.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

enum DEX {
UniswapV3,
PancakeSwapV3,
SlipStream
}
6 changes: 5 additions & 1 deletion contracts/EphemeralAllPositionsByOwner.sol
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,11 @@ contract EphemeralAllPositionsByOwner is PositionUtils {
/// @param dex DEX
/// @param npm Nonfungible position manager
/// @param owner The address that owns the NFTs
function allPositions(DEX dex, address npm, address owner) public payable returns (PositionState[] memory positions) {
function allPositions(
DEX dex,
address npm,
address owner
) public payable returns (PositionState[] memory positions) {
uint256 balance = NPMCaller.balanceOf(INPM(npm), owner);
positions = new PositionState[](balance);
unchecked {
Expand Down
38 changes: 29 additions & 9 deletions contracts/EphemeralGetPopulatedTicksInRange.sol
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;

import {DEX} from "./Dex.sol";
import "./PoolUtils.sol";
import "./interfaces/ISlipStreamCLPool.sol";

/// @notice A lens that fetches chunks of tick data in a range for a Uniswap v3 pool without deployment
/// @author Aperture Finance
/// @dev The return data can be accessed externally by `eth_call` without a `to` address or internally by catching the
/// revert data, and decoded by `abi.decode(data, (PopulatedTick[]))`
contract EphemeralGetPopulatedTicksInRange is PoolUtils {
constructor(V3PoolCallee pool, int24 tickLower, int24 tickUpper) payable {
PopulatedTick[] memory populatedTicks = getPopulatedTicksInRange(pool, tickLower, tickUpper);
constructor(DEX dex, V3PoolCallee pool, int24 tickLower, int24 tickUpper) payable {
PopulatedTick[] memory populatedTicks = getPopulatedTicksInRange(dex, pool, tickLower, tickUpper);
bytes memory returnData = abi.encode(populatedTicks);
assembly ("memory-safe") {
revert(add(returnData, 0x20), mload(returnData))
Expand All @@ -22,6 +24,7 @@ contract EphemeralGetPopulatedTicksInRange is PoolUtils {
/// @param tickUpper The upper tick boundary of the populated ticks to fetch
/// @return populatedTicks An array of tick data for the given word in the tick bitmap
function getPopulatedTicksInRange(
DEX dex,
V3PoolCallee pool,
int24 tickLower,
int24 tickUpper
Expand All @@ -37,6 +40,7 @@ contract EphemeralGetPopulatedTicksInRange is PoolUtils {
uint256 idx;
for (int16 wordPos = wordPosLower; wordPos <= wordPosUpper; ++wordPos) {
idx = populateTicksInWord(
dex,
pool,
wordPos,
tickSpacing,
Expand All @@ -50,6 +54,7 @@ contract EphemeralGetPopulatedTicksInRange is PoolUtils {

/// @notice Get the tick data for all populated ticks in a word of the tick bitmap
function populateTicksInWord(
DEX dex,
V3PoolCallee pool,
int16 wordPos,
int24 tickSpacing,
Expand All @@ -65,19 +70,34 @@ contract EphemeralGetPopulatedTicksInRange is PoolUtils {
assembly {
tick := mul(tickSpacing, add(shl(8, wordPos), bitPos))
}
populateTick(pool, tick, populatedTicks[idx++]);
populateTick(dex, pool, tick, populatedTicks[idx++]);
}
}
return idx;
}
}

function populateTick(V3PoolCallee pool, int24 tick, PopulatedTick memory populatedTick) internal view {
PoolCaller.TickInfo memory info = pool.ticks(tick);
function populateTick(DEX dex, V3PoolCallee pool, int24 tick, PopulatedTick memory populatedTick) internal view {
populatedTick.tick = tick;
populatedTick.liquidityNet = info.liquidityNet;
populatedTick.liquidityGross = info.liquidityGross;
populatedTick.feeGrowthOutside0X128 = info.feeGrowthOutside0X128;
populatedTick.feeGrowthOutside1X128 = info.feeGrowthOutside1X128;
if (dex == DEX.SlipStream) {
(
populatedTick.liquidityGross,
populatedTick.liquidityNet,
,
populatedTick.feeGrowthOutside0X128,
populatedTick.feeGrowthOutside1X128,
,
,
,
,

) = ISlipStreamCLPool(V3PoolCallee.unwrap(pool)).ticks(tick);
} else {
PoolCaller.TickInfo memory info = pool.ticks(tick);
populatedTick.liquidityNet = info.liquidityNet;
populatedTick.liquidityGross = info.liquidityGross;
populatedTick.feeGrowthOutside0X128 = info.feeGrowthOutside0X128;
populatedTick.feeGrowthOutside1X128 = info.feeGrowthOutside1X128;
}
}
}
29 changes: 13 additions & 16 deletions contracts/EphemeralPoolPositions.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,33 +2,39 @@
pragma solidity ^0.8.0;

import "./PoolUtils.sol";
import {DEX} from "./Dex.sol";

/// @notice A lens that batches fetching of the `positions` mapping for a Uniswap v3 pool without deployment
/// @author Aperture Finance
/// @dev The return data can be accessed externally by `eth_call` without a `to` address or internally by catching the
/// revert data, and decoded by `abi.decode(data, (Slot[]))`
contract EphemeralPoolPositions is PoolUtils {
constructor(V3PoolCallee pool, PositionKey[] memory keys) payable {
Slot[] memory slots = getPositions(pool, keys);
constructor(DEX dex, V3PoolCallee pool, PositionKey[] memory keys) payable {
Slot[] memory slots = getPositions(dex, pool, keys);
bytes memory returnData = abi.encode(slots);
assembly ("memory-safe") {
revert(add(returnData, 0x20), mload(returnData))
}
}

function getPositionsSlot() internal pure virtual returns (uint256) {
// Storage slot of the `positions` mapping in UniswapV3Pool.
return 7;
function getPositionsSlot(DEX dex) internal pure virtual returns (uint256) {
if (dex == DEX.UniswapV3) return 7;
if (dex == DEX.PancakeSwapV3) return 8;
revert("EphemeralPoolPositions: invalid or unsupported DEX");
}

/// @notice Get liquidity positions in a pool
/// @dev Public function to expose the abi for easier decoding using TypeChain
/// @param pool The address of the pool for which to fetch the tick bitmap
/// @param keys The position keys to fetch
/// @return slots An array of storage slots and their raw data
function getPositions(V3PoolCallee pool, PositionKey[] memory keys) public payable returns (Slot[] memory slots) {
function getPositions(
DEX dex,
V3PoolCallee pool,
PositionKey[] memory keys
) public payable returns (Slot[] memory slots) {
unchecked {
uint256 POSITIONS_SLOT = getPositionsSlot();
uint256 POSITIONS_SLOT = getPositionsSlot(dex);
uint256 length = keys.length;
// each position occupies 4 storage slots
slots = new Slot[](length << 2);
Expand Down Expand Up @@ -56,12 +62,3 @@ contract EphemeralPoolPositions is PoolUtils {
}
}
}

contract EphemeralPCSV3PoolPositions is EphemeralPoolPositions {
constructor(V3PoolCallee pool, PositionKey[] memory keys) payable EphemeralPoolPositions(pool, keys) {}

function getPositionsSlot() internal pure override returns (uint256) {
// Storage slot of the `positions` mapping in PancakeSwapV3Pool.
return 8;
}
}
38 changes: 19 additions & 19 deletions contracts/EphemeralPoolSlots.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,15 @@ pragma solidity ^0.8.0;

import "@pancakeswap/v3-core/contracts/interfaces/IPancakeV3Pool.sol";
import "./PoolUtils.sol";
import {DEX} from "./Dex.sol";

/// @notice A lens for fetching static state variables in a Uniswap v3 pool without deployment
/// @author Aperture Finance
/// @dev The return data can be accessed externally by `eth_call` without a `to` address or internally by catching the
/// revert data, and decoded by `abi.decode(data, (Slot[]))`
contract EphemeralPoolSlots is PoolUtils {
constructor(V3PoolCallee pool) payable {
Slot[] memory slots = getSlots(pool);
constructor(DEX dex, V3PoolCallee pool) payable {
Slot[] memory slots = getSlots(dex, pool);
bytes memory returnData = abi.encode(slots);
assembly ("memory-safe") {
revert(add(returnData, 0x20), mload(returnData))
Expand All @@ -21,7 +22,20 @@ contract EphemeralPoolSlots is PoolUtils {
/// @dev Public function to expose the abi for easier decoding using TypeChain
/// @param pool The Uniswap v3 pool
/// @return slots An array of storage slots and their raw data
function getSlots(V3PoolCallee pool) public payable returns (Slot[] memory slots) {
function getSlots(DEX dex, V3PoolCallee pool) public payable returns (Slot[] memory slots) {
if (dex == DEX.UniswapV3) {
return getUniV3PoolSlots(pool);
} else if (dex == DEX.PancakeSwapV3) {
return getPCSV3PoolSlots(pool);
}
revert("EphemeralPoolSlots: invalid or unsupported DEX");
}

/// @notice Get the static storage slots of a Uniswap v3 pool
/// @dev Public function to expose the abi for easier decoding using TypeChain
/// @param pool The Uniswap v3 pool
/// @return slots An array of storage slots and their raw data
function getUniV3PoolSlots(V3PoolCallee pool) internal view returns (Slot[] memory slots) {
unchecked {
uint256 length;
{
Expand Down Expand Up @@ -78,26 +92,12 @@ contract EphemeralPoolSlots is PoolUtils {
}
}
}
}

/// @notice A lens for fetching static state variables in a PancakeSwap v3 pool without deployment
/// @author Aperture Finance
/// @dev The return data can be accessed externally by `eth_call` without a `to` address or internally by catching the
/// revert data, and decoded by `abi.decode(data, (Slot[]))`
contract EphemeralPCSV3PoolSlots is PoolUtils {
constructor(V3PoolCallee pool) payable {
Slot[] memory slots = getSlots(pool);
bytes memory returnData = abi.encode(slots);
assembly ("memory-safe") {
revert(add(returnData, 0x20), mload(returnData))
}
}

/// @notice Get the static storage slots of a pool
/// @notice Get the static storage slots of a PancakeSwap v3 pool
/// @dev Public function to expose the abi for easier decoding using TypeChain
/// @param pool The PancakeSwap v3 pool
/// @return slots An array of storage slots and their raw data
function getSlots(V3PoolCallee pool) public payable returns (Slot[] memory slots) {
function getPCSV3PoolSlots(V3PoolCallee pool) internal view returns (Slot[] memory slots) {
unchecked {
uint256 length;
{
Expand Down
25 changes: 9 additions & 16 deletions contracts/EphemeralPoolTickBitmap.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,34 +3,36 @@ pragma solidity ^0.8.0;

import {TickMath} from "@aperture_finance/uni-v3-lib/src/TickMath.sol";
import "./PoolUtils.sol";
import {DEX} from "./Dex.sol";

/// @notice A lens that fetches the `tickBitmap` for a Uniswap v3 pool without deployment
/// @author Aperture Finance
/// @dev The return data can be accessed externally by `eth_call` without a `to` address or internally by catching the
/// revert data, and decoded by `abi.decode(data, (Slot[]))`
contract EphemeralPoolTickBitmap is PoolUtils {
constructor(V3PoolCallee pool) payable {
Slot[] memory slots = getTickBitmap(pool);
constructor(DEX dex, V3PoolCallee pool) payable {
Slot[] memory slots = getTickBitmap(dex, pool);
bytes memory returnData = abi.encode(slots);
assembly ("memory-safe") {
revert(add(returnData, 0x20), mload(returnData))
}
}

function getTickBitmapSlot() internal pure virtual returns (uint256) {
// Storage slot of the `tickBitmap` mapping in UniswapV3Pool.
return 6;
function getTickBitmapSlot(DEX dex) internal pure virtual returns (uint256) {
if (dex == DEX.UniswapV3) return 6;
if (dex == DEX.PancakeSwapV3) return 7;
revert("EphemeralPoolTickBitmap: invalid or unsupported DEX");
}

/// @notice Get the tick bitmap for a pool
/// @dev Public function to expose the abi for easier decoding using TypeChain
/// @param pool The address of the pool for which to fetch the tick bitmap
/// @return slots An array of storage slots and their raw data
function getTickBitmap(V3PoolCallee pool) public payable returns (Slot[] memory slots) {
function getTickBitmap(DEX dex, V3PoolCallee pool) public payable returns (Slot[] memory slots) {
// checks that the pool exists
int24 tickSpacing = IUniswapV3Pool(V3PoolCallee.unwrap(pool)).tickSpacing();
(int16 wordPosLower, int16 wordPosUpper) = getWordPositions(TickMath.MIN_TICK, TickMath.MAX_TICK, tickSpacing);
uint256 TICKBITMAP_SLOT = getTickBitmapSlot();
uint256 TICKBITMAP_SLOT = getTickBitmapSlot(dex);
unchecked {
slots = new Slot[](uint16(wordPosUpper - wordPosLower + 1));
for (int16 wordPos = wordPosLower; wordPos <= wordPosUpper; ++wordPos) {
Expand All @@ -47,12 +49,3 @@ contract EphemeralPoolTickBitmap is PoolUtils {
}
}
}

contract EphemeralPCSV3PoolTickBitmap is EphemeralPoolTickBitmap {
constructor(V3PoolCallee pool) payable EphemeralPoolTickBitmap(pool) {}

function getTickBitmapSlot() internal pure override returns (uint256) {
// Storage slot of the `tickBitmap` mapping in PancakeSwapV3Pool.
return 7;
}
}
30 changes: 11 additions & 19 deletions contracts/EphemeralPoolTicks.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,25 @@
pragma solidity ^0.8.0;

import "./PoolUtils.sol";
import {DEX} from "./Dex.sol";

/// @notice A lens that fetches raw storage slots of the `ticks` mapping in a range for a Uniswap v3 pool without deployment
/// @author Aperture Finance
/// @dev The return data can be accessed externally by `eth_call` without a `to` address or internally by catching the
/// revert data, and decoded by `abi.decode(data, (Slot[]))`
contract EphemeralPoolTicks is PoolUtils {
constructor(V3PoolCallee pool, int24 tickLower, int24 tickUpper) payable {
Slot[] memory slots = getPopulatedTicksInRange(pool, tickLower, tickUpper);
constructor(DEX dex, V3PoolCallee pool, int24 tickLower, int24 tickUpper) payable {
Slot[] memory slots = getPopulatedTicksInRange(dex, pool, tickLower, tickUpper);
bytes memory returnData = abi.encode(slots);
assembly ("memory-safe") {
revert(add(returnData, 0x20), mload(returnData))
}
}

function getTicksSlot() internal pure virtual returns (uint256) {
// Storage slot of the `ticks` mapping in UniswapV3Pool.
return 5;
function getTicksSlot(DEX dex) internal pure virtual returns (uint256) {
if (dex == DEX.UniswapV3) return 5;
if (dex == DEX.PancakeSwapV3) return 6;
revert("EphemeralPoolTicks: invalid or unsupported DEX");
}

/// @notice Get all the tick data for the populated ticks from tickLower to tickUpper
Expand All @@ -28,6 +30,7 @@ contract EphemeralPoolTicks is PoolUtils {
/// @param tickUpper The upper tick boundary of the populated ticks to fetch
/// @return slots An array of storage slots and their raw data
function getPopulatedTicksInRange(
DEX dex,
V3PoolCallee pool,
int24 tickLower,
int24 tickUpper
Expand All @@ -44,6 +47,7 @@ contract EphemeralPoolTicks is PoolUtils {
uint256 idx;
for (int16 wordPos = wordPosLower; wordPos <= wordPosUpper; ++wordPos) {
idx = populateTicksInWord(
dex,
pool,
wordPos,
tickSpacing,
Expand All @@ -57,6 +61,7 @@ contract EphemeralPoolTicks is PoolUtils {

/// @notice Get the tick data for all populated ticks in a word of the tick bitmap
function populateTicksInWord(
DEX dex,
V3PoolCallee pool,
int16 wordPos,
int24 tickSpacing,
Expand All @@ -65,7 +70,7 @@ contract EphemeralPoolTicks is PoolUtils {
uint256 idx
) internal view returns (uint256) {
unchecked {
uint256 TICKS_SLOT = getTicksSlot();
uint256 TICKS_SLOT = getTicksSlot(dex);
for (uint256 bitPos; bitPos < 256; ++bitPos) {
//slither-disable-next-line incorrect-shift
if (bitmap & (1 << bitPos) != 0) {
Expand Down Expand Up @@ -113,16 +118,3 @@ contract EphemeralPoolTicks is PoolUtils {
}
}
}

contract EphemeralPCSV3PoolTicks is EphemeralPoolTicks {
constructor(
V3PoolCallee pool,
int24 tickLower,
int24 tickUpper
) payable EphemeralPoolTicks(pool, tickLower, tickUpper) {}

function getTicksSlot() internal pure override returns (uint256) {
// Storage slot of the `ticks` mapping in PancakeSwapV3Pool.
return 6;
}
}
Loading

0 comments on commit 0642c4e

Please sign in to comment.