Contract Name:
FluidDexReservesResolver
Contract Source Code:
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.21;
/// @notice implements calculation of address for contracts deployed through CREATE.
/// Accepts contract deployed from which address & nonce
library AddressCalcs {
/// @notice Computes the address of a contract based
/// @param deployedFrom_ Address from which the contract was deployed
/// @param nonce_ Nonce at which the contract was deployed
/// @return contract_ Address of deployed contract
function addressCalc(address deployedFrom_, uint nonce_) internal pure returns (address contract_) {
// @dev based on https://ethereum.stackexchange.com/a/61413
// nonce of smart contract always starts with 1. so, with nonce 0 there won't be any deployment
// hence, nonce of vault deployment starts with 1.
bytes memory data;
if (nonce_ == 0x00) {
return address(0);
} else if (nonce_ <= 0x7f) {
data = abi.encodePacked(bytes1(0xd6), bytes1(0x94), deployedFrom_, uint8(nonce_));
} else if (nonce_ <= 0xff) {
data = abi.encodePacked(bytes1(0xd7), bytes1(0x94), deployedFrom_, bytes1(0x81), uint8(nonce_));
} else if (nonce_ <= 0xffff) {
data = abi.encodePacked(bytes1(0xd8), bytes1(0x94), deployedFrom_, bytes1(0x82), uint16(nonce_));
} else if (nonce_ <= 0xffffff) {
data = abi.encodePacked(bytes1(0xd9), bytes1(0x94), deployedFrom_, bytes1(0x83), uint24(nonce_));
} else {
data = abi.encodePacked(bytes1(0xda), bytes1(0x94), deployedFrom_, bytes1(0x84), uint32(nonce_));
}
return address(uint160(uint256(keccak256(data))));
}
}
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.21;
library BytesSliceAndConcat {
// @dev taken from https://github.com/GNSPS/solidity-bytes-utils/blob/master/contracts/BytesLib.sol
function bytesConcat(
bytes memory _preBytes,
bytes memory _postBytes
) internal pure returns (bytes memory tempBytes) {
assembly {
// Get a location of some free memory and store it in tempBytes as
// Solidity does for memory variables.
tempBytes := mload(0x40)
// Store the length of the first bytes array at the beginning of
// the memory for tempBytes.
let length := mload(_preBytes)
mstore(tempBytes, length)
// Maintain a memory counter for the current write location in the
// temp bytes array by adding the 32 bytes for the array length to
// the starting location.
let mc := add(tempBytes, 0x20)
// Stop copying when the memory counter reaches the length of the
// first bytes array.
let end := add(mc, length)
for {
// Initialize a copy counter to the start of the _preBytes data,
// 32 bytes into its memory.
let cc := add(_preBytes, 0x20)
} lt(mc, end) {
// Increase both counters by 32 bytes each iteration.
mc := add(mc, 0x20)
cc := add(cc, 0x20)
} {
// Write the _preBytes data into the tempBytes memory 32 bytes
// at a time.
mstore(mc, mload(cc))
}
// Add the length of _postBytes to the current length of tempBytes
// and store it as the new length in the first 32 bytes of the
// tempBytes memory.
length := mload(_postBytes)
mstore(tempBytes, add(length, mload(tempBytes)))
// Move the memory counter back from a multiple of 0x20 to the
// actual end of the _preBytes data.
mc := end
// Stop copying when the memory counter reaches the new combined
// length of the arrays.
end := add(mc, length)
for {
let cc := add(_postBytes, 0x20)
} lt(mc, end) {
mc := add(mc, 0x20)
cc := add(cc, 0x20)
} {
mstore(mc, mload(cc))
}
// Update the free-memory pointer by padding our last write location
// to 32 bytes: add 31 bytes to the end of tempBytes to move to the
// next 32 byte block, then round down to the nearest multiple of
// 32. If the sum of the length of the two arrays is zero then add
// one before rounding down to leave a blank 32 bytes (the length block with 0).
mstore(
0x40,
and(
add(add(end, iszero(add(length, mload(_preBytes)))), 31),
not(31) // Round down to the nearest 32 bytes.
)
)
}
return tempBytes;
}
// @dev taken from https://github.com/GNSPS/solidity-bytes-utils/blob/master/contracts/BytesLib.sol
function bytesSlice(
bytes memory _bytes,
uint256 _start,
uint256 _length
) internal pure returns (bytes memory tempBytes) {
require(_length + 31 >= _length, "slice_overflow");
require(_bytes.length >= _start + _length, "slice_outOfBounds");
assembly {
switch iszero(_length)
case 0 {
// Get a location of some free memory and store it in tempBytes as
// Solidity does for memory variables.
tempBytes := mload(0x40)
// The first word of the slice result is potentially a partial
// word read from the original array. To read it, we calculate
// the length of that partial word and start copying that many
// bytes into the array. The first word we copy will start with
// data we don't care about, but the last `lengthmod` bytes will
// land at the beginning of the contents of the new array. When
// we're done copying, we overwrite the full first word with
// the actual length of the slice.
let lengthmod := and(_length, 31)
// The multiplication in the next line is necessary
// because when slicing multiples of 32 bytes (lengthmod == 0)
// the following copy loop was copying the origin's length
// and then ending prematurely not copying everything it should.
let mc := add(add(tempBytes, lengthmod), mul(0x20, iszero(lengthmod)))
let end := add(mc, _length)
for {
// The multiplication in the next line has the same exact purpose
// as the one above.
let cc := add(add(add(_bytes, lengthmod), mul(0x20, iszero(lengthmod))), _start)
} lt(mc, end) {
mc := add(mc, 0x20)
cc := add(cc, 0x20)
} {
mstore(mc, mload(cc))
}
mstore(tempBytes, _length)
//update free-memory pointer
//allocating the array padded to 32 bytes like the compiler does now
mstore(0x40, and(add(mc, 31), not(31)))
}
//if we want a zero-length slice let's just return a zero-length array
default {
tempBytes := mload(0x40)
//zero out the 32 bytes slice we are about to return
//we need to do it because Solidity does not garbage collect
mstore(tempBytes, 0)
mstore(0x40, add(tempBytes, 0x20))
}
}
return tempBytes;
}
}
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.21;
/// @notice library that helps in reading / working with storage slot data of Fluid Dex.
/// @dev as all data for Fluid Dex is internal, any data must be fetched directly through manual
/// slot reading through this library or, if gas usage is less important, through the FluidDexResolver.
library DexSlotsLink {
/// @dev storage slot for variables at Dex
uint256 internal constant DEX_VARIABLES_SLOT = 0;
/// @dev storage slot for variables2 at Dex
uint256 internal constant DEX_VARIABLES2_SLOT = 1;
/// @dev storage slot for total supply shares at Dex
uint256 internal constant DEX_TOTAL_SUPPLY_SHARES_SLOT = 2;
/// @dev storage slot for user supply mapping at Dex
uint256 internal constant DEX_USER_SUPPLY_MAPPING_SLOT = 3;
/// @dev storage slot for total borrow shares at Dex
uint256 internal constant DEX_TOTAL_BORROW_SHARES_SLOT = 4;
/// @dev storage slot for user borrow mapping at Dex
uint256 internal constant DEX_USER_BORROW_MAPPING_SLOT = 5;
/// @dev storage slot for oracle mapping at Dex
uint256 internal constant DEX_ORACLE_MAPPING_SLOT = 6;
/// @dev storage slot for range and threshold shifts at Dex
uint256 internal constant DEX_RANGE_THRESHOLD_SHIFTS_SLOT = 7;
/// @dev storage slot for center price shift at Dex
uint256 internal constant DEX_CENTER_PRICE_SHIFT_SLOT = 8;
// --------------------------------
// @dev stacked uint256 storage slots bits position data for each:
// UserSupplyData
uint256 internal constant BITS_USER_SUPPLY_ALLOWED = 0;
uint256 internal constant BITS_USER_SUPPLY_AMOUNT = 1;
uint256 internal constant BITS_USER_SUPPLY_PREVIOUS_WITHDRAWAL_LIMIT = 65;
uint256 internal constant BITS_USER_SUPPLY_LAST_UPDATE_TIMESTAMP = 129;
uint256 internal constant BITS_USER_SUPPLY_EXPAND_PERCENT = 162;
uint256 internal constant BITS_USER_SUPPLY_EXPAND_DURATION = 176;
uint256 internal constant BITS_USER_SUPPLY_BASE_WITHDRAWAL_LIMIT = 200;
// UserBorrowData
uint256 internal constant BITS_USER_BORROW_ALLOWED = 0;
uint256 internal constant BITS_USER_BORROW_AMOUNT = 1;
uint256 internal constant BITS_USER_BORROW_PREVIOUS_BORROW_LIMIT = 65;
uint256 internal constant BITS_USER_BORROW_LAST_UPDATE_TIMESTAMP = 129;
uint256 internal constant BITS_USER_BORROW_EXPAND_PERCENT = 162;
uint256 internal constant BITS_USER_BORROW_EXPAND_DURATION = 176;
uint256 internal constant BITS_USER_BORROW_BASE_BORROW_LIMIT = 200;
uint256 internal constant BITS_USER_BORROW_MAX_BORROW_LIMIT = 218;
// --------------------------------
/// @notice Calculating the slot ID for Dex contract for single mapping at `slot_` for `key_`
function calculateMappingStorageSlot(uint256 slot_, address key_) internal pure returns (bytes32) {
return keccak256(abi.encode(key_, slot_));
}
/// @notice Calculating the slot ID for Dex contract for double mapping at `slot_` for `key1_` and `key2_`
function calculateDoubleMappingStorageSlot(
uint256 slot_,
address key1_,
address key2_
) internal pure returns (bytes32) {
bytes32 intermediateSlot_ = keccak256(abi.encode(key1_, slot_));
return keccak256(abi.encode(key2_, intermediateSlot_));
}
}
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.21;
import { AddressCalcs } from "../../../libraries/addressCalcs.sol";
import { DexSlotsLink } from "../../../libraries/dexSlotsLink.sol";
import { BytesSliceAndConcat } from "../../../libraries/bytesSliceAndConcat.sol";
import { IFluidDexT1 } from "../../../protocols/dex/interfaces/iDexT1.sol";
import { Variables } from "./variables.sol";
import { Structs } from "./structs.sol";
/// @title DexFactoryViews
/// @notice Abstract contract providing view functions for DEX factory-related operations
abstract contract DexFactoryViews is Variables {
/// @notice Get the address of a Pool given its ID
/// @param poolId_ The ID of the Pool
/// @return pool_ The address of the Pool
function getPoolAddress(uint256 poolId_) public view returns (address pool_) {
return AddressCalcs.addressCalc(address(FACTORY), poolId_);
}
/// @notice Get the total number of Pools
/// @return The total number of Pools
function getTotalPools() public view returns (uint) {
return FACTORY.totalDexes();
}
/// @notice Get an array of all Pool addresses
/// @return pools_ An array containing all Pool addresses
function getAllPoolAddresses() public view returns (address[] memory pools_) {
uint totalPools_ = getTotalPools();
pools_ = new address[](totalPools_);
for (uint i = 0; i < totalPools_; i++) {
pools_[i] = getPoolAddress((i + 1));
}
}
}
/// @title DexPublicViews
/// @notice Abstract contract providing view functions for DEX public data
abstract contract DexPublicViews {
/// @notice Get the prices and exchange prices for a DEX
/// @param dex_ The address of the DEX
/// @return pex_ A struct containing prices and exchange prices
/// @dev expected to be called via callStatic
function getDexPricesAndExchangePrices(
address dex_
) public returns (IFluidDexT1.PricesAndExchangePrice memory pex_) {
try IFluidDexT1(dex_).getPricesAndExchangePrices() {} catch (bytes memory lowLevelData_) {
bytes4 errorSelector_;
assembly {
// Extract the selector from the error data
errorSelector_ := mload(add(lowLevelData_, 0x20))
}
if (errorSelector_ == IFluidDexT1.FluidDexPricesAndExchangeRates.selector) {
pex_ = abi.decode(
BytesSliceAndConcat.bytesSlice(lowLevelData_, 4, lowLevelData_.length - 4),
(IFluidDexT1.PricesAndExchangePrice)
);
}
}
}
/// @notice Get the collateral reserves for a DEX in token decimals amounts
/// @param dex_ The address of the DEX
/// @return reserves_ A struct containing collateral reserve information
/// @dev expected to be called via callStatic
function getDexCollateralReserves(address dex_) public returns (IFluidDexT1.CollateralReserves memory reserves_) {
reserves_ = getDexCollateralReservesAdjusted(dex_);
IFluidDexT1.ConstantViews2 memory constantsView2_ = IFluidDexT1(dex_).constantsView2();
// returned reserves are in 1e12 decimals -> normalize to token decimals
reserves_.token0RealReserves =
(reserves_.token0RealReserves * constantsView2_.token0DenominatorPrecision) /
constantsView2_.token0NumeratorPrecision;
reserves_.token0ImaginaryReserves =
(reserves_.token0ImaginaryReserves * constantsView2_.token0DenominatorPrecision) /
constantsView2_.token0NumeratorPrecision;
reserves_.token1RealReserves =
(reserves_.token1RealReserves * constantsView2_.token1DenominatorPrecision) /
constantsView2_.token1NumeratorPrecision;
reserves_.token1ImaginaryReserves =
(reserves_.token1ImaginaryReserves * constantsView2_.token1DenominatorPrecision) /
constantsView2_.token1NumeratorPrecision;
}
/// @notice Get the collateral reserves for a DEX scaled to 1e12
/// @param dex_ The address of the DEX
/// @return reserves_ A struct containing collateral reserve information
/// @dev expected to be called via callStatic
function getDexCollateralReservesAdjusted(
address dex_
) public returns (IFluidDexT1.CollateralReserves memory reserves_) {
uint256 dexVariables2_ = IFluidDexT1(dex_).readFromStorage(bytes32(DexSlotsLink.DEX_VARIABLES2_SLOT));
if ((dexVariables2_ & 1) != 1) {
// smart col not enabled
return IFluidDexT1.CollateralReserves(0, 0, 0, 0);
}
try this.getDexPricesAndExchangePrices(dex_) returns (IFluidDexT1.PricesAndExchangePrice memory pex_) {
try
IFluidDexT1(dex_).getCollateralReserves(
pex_.geometricMean,
pex_.upperRange,
pex_.lowerRange,
pex_.supplyToken0ExchangePrice,
pex_.supplyToken1ExchangePrice
)
returns (IFluidDexT1.CollateralReserves memory colReserves_) {
// returned reserves are in 1e12 decimals -> normalize to token decimals
reserves_ = colReserves_;
} catch {
reserves_ = IFluidDexT1.CollateralReserves(0, 0, 0, 0);
}
} catch {
reserves_ = IFluidDexT1.CollateralReserves(0, 0, 0, 0);
}
}
/// @notice Get the debt reserves for a DEX in token decimals amounts
/// @param dex_ The address of the DEX
/// @return reserves_ A struct containing debt reserve information
/// @dev expected to be called via callStatic
function getDexDebtReserves(address dex_) public returns (IFluidDexT1.DebtReserves memory reserves_) {
reserves_ = getDexDebtReservesAdjusted(dex_);
IFluidDexT1.ConstantViews2 memory constantsView2_ = IFluidDexT1(dex_).constantsView2();
// returned reserves are in 1e12 decimals -> normalize to token decimals
reserves_.token0Debt =
(reserves_.token0Debt * constantsView2_.token0DenominatorPrecision) /
constantsView2_.token0NumeratorPrecision;
reserves_.token0RealReserves =
(reserves_.token0RealReserves * constantsView2_.token0DenominatorPrecision) /
constantsView2_.token0NumeratorPrecision;
reserves_.token0ImaginaryReserves =
(reserves_.token0ImaginaryReserves * constantsView2_.token0DenominatorPrecision) /
constantsView2_.token0NumeratorPrecision;
reserves_.token1Debt =
(reserves_.token1Debt * constantsView2_.token1DenominatorPrecision) /
constantsView2_.token1NumeratorPrecision;
reserves_.token1RealReserves =
(reserves_.token1RealReserves * constantsView2_.token1DenominatorPrecision) /
constantsView2_.token1NumeratorPrecision;
reserves_.token1ImaginaryReserves =
(reserves_.token1ImaginaryReserves * constantsView2_.token1DenominatorPrecision) /
constantsView2_.token1NumeratorPrecision;
}
/// @notice Get the debt reserves for a DEX scaled to 1e12
/// @param dex_ The address of the DEX
/// @return reserves_ A struct containing debt reserve information
/// @dev expected to be called via callStatic
function getDexDebtReservesAdjusted(address dex_) public returns (IFluidDexT1.DebtReserves memory reserves_) {
uint256 dexVariables2_ = IFluidDexT1(dex_).readFromStorage(bytes32(DexSlotsLink.DEX_VARIABLES2_SLOT));
if ((dexVariables2_ & 2) != 2) {
// smart debt not enabled
return IFluidDexT1.DebtReserves(0, 0, 0, 0, 0, 0);
}
try this.getDexPricesAndExchangePrices(dex_) returns (IFluidDexT1.PricesAndExchangePrice memory pex_) {
try
IFluidDexT1(dex_).getDebtReserves(
pex_.geometricMean,
pex_.upperRange,
pex_.lowerRange,
pex_.borrowToken0ExchangePrice,
pex_.borrowToken1ExchangePrice
)
returns (IFluidDexT1.DebtReserves memory debtReserves_) {
// returned reserves are in 1e12 decimals -> normalize to token decimals
reserves_ = debtReserves_;
} catch {
reserves_ = IFluidDexT1.DebtReserves(0, 0, 0, 0, 0, 0);
}
} catch {
reserves_ = IFluidDexT1.DebtReserves(0, 0, 0, 0, 0, 0);
}
}
}
/// @title DexConstantsViews
/// @notice Abstract contract providing view functions for DEX constants
abstract contract DexConstantsViews {
/// @notice returns all Pool constants
function getPoolConstantsView(address pool_) public view returns (IFluidDexT1.ConstantViews memory constantsView_) {
return IFluidDexT1(pool_).constantsView();
}
/// @notice returns all Pool constants 2
function getPoolConstantsView2(
address pool_
) public view returns (IFluidDexT1.ConstantViews2 memory constantsView2_) {
return IFluidDexT1(pool_).constantsView2();
}
/// @notice Get the addresses of the tokens in a Pool
/// @param pool_ The address of the Pool
/// @return token0_ The address of token0 in the Pool
/// @return token1_ The address of token1 in the Pool
function getPoolTokens(address pool_) public view returns (address token0_, address token1_) {
IFluidDexT1.ConstantViews memory constantsView_ = IFluidDexT1(pool_).constantsView();
return (constantsView_.token0, constantsView_.token1);
}
}
abstract contract DexActionEstimates {
address private constant ADDRESS_DEAD = 0x000000000000000000000000000000000000dEaD;
/// @notice estimates swap IN tokens execution
/// @param dex_ Dex pool
/// @param swap0to1_ Direction of swap. If true, swaps token0 for token1; if false, swaps token1 for token0
/// @param amountIn_ The exact amount of input tokens to swap
/// @param amountOutMin_ The minimum amount of output tokens the user is willing to accept
/// @return amountOut_ The amount of output tokens received from the swap
function estimateSwapIn(
address dex_,
bool swap0to1_,
uint256 amountIn_,
uint256 amountOutMin_
) public payable returns (uint256 amountOut_) {
try IFluidDexT1(dex_).swapIn{ value: msg.value }(swap0to1_, amountIn_, amountOutMin_, ADDRESS_DEAD) {} catch (
bytes memory lowLevelData_
) {
(amountOut_) = _decodeLowLevelUint1x(lowLevelData_, IFluidDexT1.FluidDexSwapResult.selector);
}
}
/// @notice estimates swap OUT tokens execution
/// @param dex_ Dex pool
/// @param swap0to1_ Direction of swap. If true, swaps token0 for token1; if false, swaps token1 for token0
/// @param amountOut_ The exact amount of tokens to receive after swap
/// @param amountInMax_ Maximum amount of tokens to swap in
/// @return amountIn_ The amount of input tokens used for the swap
function estimateSwapOut(
address dex_,
bool swap0to1_,
uint256 amountOut_,
uint256 amountInMax_
) public payable returns (uint256 amountIn_) {
try IFluidDexT1(dex_).swapOut{ value: msg.value }(swap0to1_, amountOut_, amountInMax_, ADDRESS_DEAD) {} catch (
bytes memory lowLevelData_
) {
(amountIn_) = _decodeLowLevelUint1x(lowLevelData_, IFluidDexT1.FluidDexSwapResult.selector);
}
}
function _decodeLowLevelUint1x(
bytes memory lowLevelData_,
bytes4 targetErrorSelector_
) internal pure returns (uint value1_) {
if (lowLevelData_.length < 36) {
return 0;
}
bytes4 errorSelector_;
assembly {
// Extract the selector from the error data
errorSelector_ := mload(add(lowLevelData_, 0x20))
}
if (errorSelector_ == targetErrorSelector_) {
assembly {
value1_ := mload(add(lowLevelData_, 36))
}
}
// else => values remain 0
}
}
/// @notice Fluid Dex Reserves resolver
/// Implements various view-only methods to give easy access to Dex protocol reserves data.
contract FluidDexReservesResolver is
Variables,
Structs,
DexFactoryViews,
DexConstantsViews,
DexPublicViews,
DexActionEstimates
{
constructor(address factory_) Variables(factory_) {}
/// @notice Get a Pool's address and its token addresses
/// @param poolId_ The ID of the Pool
/// @return pool_ The Pool data
function getPool(uint256 poolId_) public view returns (Pool memory pool_) {
address poolAddress_ = getPoolAddress(poolId_);
(address token0_, address token1_) = getPoolTokens(poolAddress_);
return Pool(poolAddress_, token0_, token1_, getPoolFee(poolAddress_));
}
/// @notice Get a Pool's fee
/// @param pool_ The Pool address
/// @return fee_ The Pool fee as 1% = 10000
function getPoolFee(address pool_) public view returns (uint256 fee_) {
uint256 dexVariables2_ = IFluidDexT1(pool_).readFromStorage(bytes32(DexSlotsLink.DEX_VARIABLES2_SLOT));
return (dexVariables2_ >> 2) & X17;
}
/// @notice Get an array of all Pool addresses and their token addresses
/// @return pools_ An array containing all Pool data
function getAllPools() public view returns (Pool[] memory pools_) {
uint256 totalPools_ = getTotalPools();
pools_ = new Pool[](totalPools_);
for (uint256 i; i < totalPools_; i++) {
pools_[i] = getPool(i + 1);
}
}
/// @notice Get the token addresses, collateral reserves, and debt reserves for a given Pool address
/// @param pool_ The Pool address
/// @return poolReserves_ The Pool data with reserves
/// @dev expected to be called via callStatic
function getPoolReserves(address pool_) public returns (PoolWithReserves memory poolReserves_) {
(address token0_, address token1_) = getPoolTokens(pool_);
IFluidDexT1.CollateralReserves memory collateralReserves_ = getDexCollateralReserves(pool_);
IFluidDexT1.DebtReserves memory debtReserves_ = getDexDebtReserves(pool_);
return PoolWithReserves(pool_, token0_, token1_, getPoolFee(pool_), collateralReserves_, debtReserves_);
}
/// @notice Get an array of Pool addresses, their token addresses, collateral reserves, and debt reserves for a given array of Pool addresses
/// @param pools_ The array of Pool addresses
/// @return poolsReserves_ An array containing all Pool data with reserves
/// @dev expected to be called via callStatic
function getPoolsReserves(address[] memory pools_) public returns (PoolWithReserves[] memory poolsReserves_) {
poolsReserves_ = new PoolWithReserves[](pools_.length);
for (uint256 i; i < pools_.length; i++) {
poolsReserves_[i] = getPoolReserves(pools_[i]);
}
}
/// @notice Get an array of all Pool addresses, their token addresses, collateral reserves, and debt reserves
/// @return poolsReserves_ An array containing all Pool data with reserves
/// @dev expected to be called via callStatic
function getAllPoolsReserves() public returns (PoolWithReserves[] memory poolsReserves_) {
return getPoolsReserves(getAllPoolAddresses());
}
/// @notice Get the token addresses, adjusted collateral reserves, and adjusted debt reserves for a given Pool address
/// @param pool_ The Pool address
/// @return poolReserves_ The Pool data with adjusted reserves scaled to 1e12
/// @dev expected to be called via callStatic
function getPoolReservesAdjusted(address pool_) public returns (PoolWithReserves memory poolReserves_) {
(address token0_, address token1_) = getPoolTokens(pool_);
IFluidDexT1.CollateralReserves memory collateralReserves_ = getDexCollateralReservesAdjusted(pool_);
IFluidDexT1.DebtReserves memory debtReserves_ = getDexDebtReservesAdjusted(pool_);
return PoolWithReserves(pool_, token0_, token1_, getPoolFee(pool_), collateralReserves_, debtReserves_);
}
/// @notice Get an array of Pool addresses, their token addresses, adjusted collateral reserves, and adjusted debt reserves for a given array of Pool addresses
/// @param pools_ The array of Pool addresses
/// @return poolsReserves_ An array containing all Pool data with adjusted reserves scaled to 1e12
/// @dev expected to be called via callStatic
function getPoolsReservesAdjusted(address[] memory pools_) public returns (PoolWithReserves[] memory poolsReserves_) {
poolsReserves_ = new PoolWithReserves[](pools_.length);
for (uint256 i; i < pools_.length; i++) {
poolsReserves_[i] = getPoolReservesAdjusted(pools_[i]);
}
}
/// @notice Get an array of all Pool addresses, their token addresses, adjusted collateral reserves, and adjusted debt reserves
/// @return poolsReserves_ An array containing all Pool data with adjusted reserves scaled to 1e12
/// @dev expected to be called via callStatic
function getAllPoolsReservesAdjusted() public returns (PoolWithReserves[] memory poolsReserves_) {
return getPoolsReservesAdjusted(getAllPoolAddresses());
}
}
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.21;
import { IFluidDexT1 } from "../../../protocols/dex/interfaces/iDexT1.sol";
abstract contract Structs {
struct Pool {
address pool;
address token0;
address token1;
uint256 fee;
}
struct PoolWithReserves {
address pool;
address token0;
address token1;
uint256 fee;
IFluidDexT1.CollateralReserves collateralReserves;
IFluidDexT1.DebtReserves debtReserves;
}
}
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.21;
import { IFluidDexFactory } from "../../../protocols/dex/interfaces/iDexFactory.sol";
abstract contract Variables {
uint256 internal constant X17 = 0x1ffff;
IFluidDexFactory public immutable FACTORY;
constructor(address factory_) {
FACTORY = IFluidDexFactory(factory_);
}
}
// SPDX-License-Identifier: MIT
pragma solidity 0.8.21;
interface IFluidDexFactory {
/// @notice Global auth is auth for all dexes
function isGlobalAuth(address auth_) external view returns (bool);
/// @notice Dex auth is auth for a specific dex
function isDexAuth(address vault_, address auth_) external view returns (bool);
/// @notice Total dexes deployed.
function totalDexes() external view returns (uint256);
/// @notice Compute dexAddress
function getDexAddress(uint256 dexId_) external view returns (address);
/// @notice read uint256 `result_` for a storage `slot_` key
function readFromStorage(bytes32 slot_) external view returns (uint256 result_);
}
// SPDX-License-Identifier: MIT
pragma solidity 0.8.21;
interface IFluidDexT1 {
error FluidDexError(uint256 errorId);
/// @notice used to simulate swap to find the output amount
error FluidDexSwapResult(uint256 amountOut);
error FluidDexPerfectLiquidityOutput(uint256 token0Amt, uint token1Amt);
error FluidDexSingleTokenOutput(uint256 tokenAmt);
error FluidDexLiquidityOutput(uint256 shares);
error FluidDexPricesAndExchangeRates(PricesAndExchangePrice pex_);
/// @notice returns the dex id
function DEX_ID() external view returns (uint256);
/// @notice reads uint256 data `result_` from storage at a bytes32 storage `slot_` key.
function readFromStorage(bytes32 slot_) external view returns (uint256 result_);
struct Implementations {
address shift;
address admin;
address colOperations;
address debtOperations;
address perfectOperationsAndOracle;
}
struct ConstantViews {
uint256 dexId;
address liquidity;
address factory;
Implementations implementations;
address deployerContract;
address token0;
address token1;
bytes32 supplyToken0Slot;
bytes32 borrowToken0Slot;
bytes32 supplyToken1Slot;
bytes32 borrowToken1Slot;
bytes32 exchangePriceToken0Slot;
bytes32 exchangePriceToken1Slot;
uint256 oracleMapping;
}
struct ConstantViews2 {
uint token0NumeratorPrecision;
uint token0DenominatorPrecision;
uint token1NumeratorPrecision;
uint token1DenominatorPrecision;
}
struct PricesAndExchangePrice {
uint lastStoredPrice; // last stored price in 1e27 decimals
uint centerPrice; // last stored price in 1e27 decimals
uint upperRange; // price at upper range in 1e27 decimals
uint lowerRange; // price at lower range in 1e27 decimals
uint geometricMean; // geometric mean of upper range & lower range in 1e27 decimals
uint supplyToken0ExchangePrice;
uint borrowToken0ExchangePrice;
uint supplyToken1ExchangePrice;
uint borrowToken1ExchangePrice;
}
struct CollateralReserves {
uint token0RealReserves;
uint token1RealReserves;
uint token0ImaginaryReserves;
uint token1ImaginaryReserves;
}
struct DebtReserves {
uint token0Debt;
uint token1Debt;
uint token0RealReserves;
uint token1RealReserves;
uint token0ImaginaryReserves;
uint token1ImaginaryReserves;
}
function getCollateralReserves(
uint geometricMean_,
uint upperRange_,
uint lowerRange_,
uint token0SupplyExchangePrice_,
uint token1SupplyExchangePrice_
) external view returns (CollateralReserves memory c_);
function getDebtReserves(
uint geometricMean_,
uint upperRange_,
uint lowerRange_,
uint token0BorrowExchangePrice_,
uint token1BorrowExchangePrice_
) external view returns (DebtReserves memory d_);
// reverts with FluidDexPricesAndExchangeRates(pex_);
function getPricesAndExchangePrices() external;
function constantsView() external view returns (ConstantViews memory constantsView_);
function constantsView2() external view returns (ConstantViews2 memory constantsView2_);
struct Oracle {
uint twap1by0; // TWAP price
uint lowestPrice1by0; // lowest price point
uint highestPrice1by0; // highest price point
uint twap0by1; // TWAP price
uint lowestPrice0by1; // lowest price point
uint highestPrice0by1; // highest price point
}
/// @dev This function allows users to swap a specific amount of input tokens for output tokens
/// @param swap0to1_ Direction of swap. If true, swaps token0 for token1; if false, swaps token1 for token0
/// @param amountIn_ The exact amount of input tokens to swap
/// @param amountOutMin_ The minimum amount of output tokens the user is willing to accept
/// @param to_ Recipient of swapped tokens. If to_ == address(0) then out tokens will be sent to msg.sender. If to_ == ADDRESS_DEAD then function will revert with amountOut_
/// @return amountOut_ The amount of output tokens received from the swap
function swapIn(
bool swap0to1_,
uint256 amountIn_,
uint256 amountOutMin_,
address to_
) external payable returns (uint256 amountOut_);
/// @dev Swap tokens with perfect amount out
/// @param swap0to1_ Direction of swap. If true, swaps token0 for token1; if false, swaps token1 for token0
/// @param amountOut_ The exact amount of tokens to receive after swap
/// @param amountInMax_ Maximum amount of tokens to swap in
/// @param to_ Recipient of swapped tokens. If to_ == address(0) then out tokens will be sent to msg.sender. If to_ == ADDRESS_DEAD then function will revert with amountIn_
/// @return amountIn_ The amount of input tokens used for the swap
function swapOut(
bool swap0to1_,
uint256 amountOut_,
uint256 amountInMax_,
address to_
) external payable returns (uint256 amountIn_);
/// @dev Deposit tokens in equal proportion to the current pool ratio
/// @param shares_ The number of shares to mint
/// @param maxToken0Deposit_ Maximum amount of token0 to deposit
/// @param maxToken1Deposit_ Maximum amount of token1 to deposit
/// @param estimate_ If true, function will revert with estimated deposit amounts without executing the deposit
/// @return token0Amt_ Amount of token0 deposited
/// @return token1Amt_ Amount of token1 deposited
function depositPerfect(
uint shares_,
uint maxToken0Deposit_,
uint maxToken1Deposit_,
bool estimate_
) external payable returns (uint token0Amt_, uint token1Amt_);
/// @dev This function allows users to withdraw a perfect amount of collateral liquidity
/// @param shares_ The number of shares to withdraw
/// @param minToken0Withdraw_ The minimum amount of token0 the user is willing to accept
/// @param minToken1Withdraw_ The minimum amount of token1 the user is willing to accept
/// @param to_ Recipient of swapped tokens. If to_ == address(0) then out tokens will be sent to msg.sender. If to_ == ADDRESS_DEAD then function will revert with token0Amt_ & token1Amt_
/// @return token0Amt_ The amount of token0 withdrawn
/// @return token1Amt_ The amount of token1 withdrawn
function withdrawPerfect(
uint shares_,
uint minToken0Withdraw_,
uint minToken1Withdraw_,
address to_
) external returns (uint token0Amt_, uint token1Amt_);
/// @dev This function allows users to borrow tokens in equal proportion to the current debt pool ratio
/// @param shares_ The number of shares to borrow
/// @param minToken0Borrow_ Minimum amount of token0 to borrow
/// @param minToken1Borrow_ Minimum amount of token1 to borrow
/// @param to_ Recipient of swapped tokens. If to_ == address(0) then out tokens will be sent to msg.sender. If to_ == ADDRESS_DEAD then function will revert with token0Amt_ & token1Amt_
/// @return token0Amt_ Amount of token0 borrowed
/// @return token1Amt_ Amount of token1 borrowed
function borrowPerfect(
uint shares_,
uint minToken0Borrow_,
uint minToken1Borrow_,
address to_
) external returns (uint token0Amt_, uint token1Amt_);
/// @dev This function allows users to pay back borrowed tokens in equal proportion to the current debt pool ratio
/// @param shares_ The number of shares to pay back
/// @param maxToken0Payback_ Maximum amount of token0 to pay back
/// @param maxToken1Payback_ Maximum amount of token1 to pay back
/// @param estimate_ If true, function will revert with estimated payback amounts without executing the payback
/// @return token0Amt_ Amount of token0 paid back
/// @return token1Amt_ Amount of token1 paid back
function paybackPerfect(
uint shares_,
uint maxToken0Payback_,
uint maxToken1Payback_,
bool estimate_
) external payable returns (uint token0Amt_, uint token1Amt_);
/// @dev This function allows users to deposit tokens in any proportion into the col pool
/// @param token0Amt_ The amount of token0 to deposit
/// @param token1Amt_ The amount of token1 to deposit
/// @param minSharesAmt_ The minimum amount of shares the user expects to receive
/// @param estimate_ If true, function will revert with estimated shares without executing the deposit
/// @return shares_ The amount of shares minted for the deposit
function deposit(
uint token0Amt_,
uint token1Amt_,
uint minSharesAmt_,
bool estimate_
) external payable returns (uint shares_);
/// @dev This function allows users to withdraw tokens in any proportion from the col pool
/// @param token0Amt_ The amount of token0 to withdraw
/// @param token1Amt_ The amount of token1 to withdraw
/// @param maxSharesAmt_ The maximum number of shares the user is willing to burn
/// @param to_ Recipient of swapped tokens. If to_ == address(0) then out tokens will be sent to msg.sender. If to_ == ADDRESS_DEAD then function will revert with shares_
/// @return shares_ The number of shares burned for the withdrawal
function withdraw(
uint token0Amt_,
uint token1Amt_,
uint maxSharesAmt_,
address to_
) external returns (uint shares_);
/// @dev This function allows users to borrow tokens in any proportion from the debt pool
/// @param token0Amt_ The amount of token0 to borrow
/// @param token1Amt_ The amount of token1 to borrow
/// @param maxSharesAmt_ The maximum amount of shares the user is willing to receive
/// @param to_ Recipient of swapped tokens. If to_ == address(0) then out tokens will be sent to msg.sender. If to_ == ADDRESS_DEAD then function will revert with shares_
/// @return shares_ The amount of borrow shares minted to represent the borrowed amount
function borrow(
uint token0Amt_,
uint token1Amt_,
uint maxSharesAmt_,
address to_
) external returns (uint shares_);
/// @dev This function allows users to payback tokens in any proportion to the debt pool
/// @param token0Amt_ The amount of token0 to payback
/// @param token1Amt_ The amount of token1 to payback
/// @param minSharesAmt_ The minimum amount of shares the user expects to burn
/// @param estimate_ If true, function will revert with estimated shares without executing the payback
/// @return shares_ The amount of borrow shares burned for the payback
function payback(
uint token0Amt_,
uint token1Amt_,
uint minSharesAmt_,
bool estimate_
) external payable returns (uint shares_);
/// @dev This function allows users to withdraw their collateral with perfect shares in one token
/// @param shares_ The number of shares to burn for withdrawal
/// @param minToken0_ The minimum amount of token0 the user expects to receive (set to 0 if withdrawing in token1)
/// @param minToken1_ The minimum amount of token1 the user expects to receive (set to 0 if withdrawing in token0)
/// @param to_ Recipient of swapped tokens. If to_ == address(0) then out tokens will be sent to msg.sender. If to_ == ADDRESS_DEAD then function will revert with withdrawAmt_
/// @return withdrawAmt_ The amount of tokens withdrawn in the chosen token
function withdrawPerfectInOneToken(
uint shares_,
uint minToken0_,
uint minToken1_,
address to_
) external returns (
uint withdrawAmt_
);
/// @dev This function allows users to payback their debt with perfect shares in one token
/// @param shares_ The number of shares to burn for payback
/// @param maxToken0_ The maximum amount of token0 the user is willing to pay (set to 0 if paying back in token1)
/// @param maxToken1_ The maximum amount of token1 the user is willing to pay (set to 0 if paying back in token0)
/// @param estimate_ If true, the function will revert with the estimated payback amount without executing the payback
/// @return paybackAmt_ The amount of tokens paid back in the chosen token
function paybackPerfectInOneToken(
uint shares_,
uint maxToken0_,
uint maxToken1_,
bool estimate_
) external payable returns (
uint paybackAmt_
);
/// @dev the oracle assumes last set price of pool till the next swap happens.
/// There's a possibility that during that time some interest is generated hence the last stored price is not the 100% correct price for the whole duration
/// but the difference due to interest will be super low so this difference is ignored
/// For example 2 swaps happened 10min (600 seconds) apart and 1 token has 10% higher interest than other.
/// then that token will accrue about 10% * 600 / secondsInAYear = ~0.0002%
/// @param secondsAgos_ array of seconds ago for which TWAP is needed. If user sends [10, 30, 60] then twaps_ will return [10-0, 30-10, 60-30]
/// @return twaps_ twap price, lowest price (aka minima) & highest price (aka maxima) between secondsAgo checkpoints
/// @return currentPrice_ price of pool after the most recent swap
function oraclePrice(
uint[] memory secondsAgos_
) external view returns (
Oracle[] memory twaps_,
uint currentPrice_
);
}