/**
* Created by Pragma Labs
* SPDX-License-Identifier: BUSL-1.1
*/
pragma solidity 0.8.22;
import { IAssetModule } from "../../interfaces/IAssetModule.sol";
import { Owned } from "../../../lib/solmate/src/auth/Owned.sol";
/**
* @title Abstract Asset Module
* @author Pragma Labs
* @notice Abstract contract with the minimal implementation of an Asset Module.
* @dev Each different asset class should have its own Oracle Module.
* The Asset Modules will:
* - Implement the pricing logic to calculate the USD value (with 18 decimals precision).
* - Process Deposits and Withdrawals.
* - Manage the risk parameters.
*/
abstract contract AssetModule is Owned, IAssetModule {
/* //////////////////////////////////////////////////////////////
CONSTANTS
////////////////////////////////////////////////////////////// */
// Identifier for the type of the asset.
uint256 public immutable ASSET_TYPE;
// The contract address of the Registry.
address public immutable REGISTRY;
/* //////////////////////////////////////////////////////////////
STORAGE
////////////////////////////////////////////////////////////// */
// Map asset => flag.
mapping(address => bool) public inAssetModule;
/* //////////////////////////////////////////////////////////////
ERRORS
////////////////////////////////////////////////////////////// */
error ExposureNotInLimits();
error OnlyRegistry();
error Overflow();
/* //////////////////////////////////////////////////////////////
MODIFIERS
////////////////////////////////////////////////////////////// */
/**
* @dev Only the Registry can call functions with this modifier.
*/
modifier onlyRegistry() {
if (msg.sender != REGISTRY) revert OnlyRegistry();
_;
}
/* //////////////////////////////////////////////////////////////
CONSTRUCTOR
////////////////////////////////////////////////////////////// */
/**
* @param registry_ The contract address of the Registry.
* @param assetType_ Identifier for the token standard of the asset.
* 0 = Unknown asset.
* 1 = ERC20.
* 2 = ERC721.
* 3 = ERC1155.
*/
constructor(address registry_, uint256 assetType_) Owned(msg.sender) {
REGISTRY = registry_;
ASSET_TYPE = assetType_;
}
/*///////////////////////////////////////////////////////////////
ASSET INFORMATION
///////////////////////////////////////////////////////////////*/
/**
* @notice Checks for a token address and the corresponding id if it is allowed.
* @param asset The contract address of the asset.
* @param assetId The id of the asset.
* @return A boolean, indicating if the asset is allowed.
* @dev For assets without id (ERC20, ERC4626...), the id should be set to 0.
*/
function isAllowed(address asset, uint256 assetId) public view virtual returns (bool);
/**
* @notice Returns the unique identifier of an asset based on the contract address and id.
* @param asset The contract address of the asset.
* @param assetId The id of the asset.
* @return key The unique identifier.
* @dev Unsafe bitshift from uint256 to uint96, use only when the ids of the assets cannot exceed type(uint96).max.
* For asset where the id can be bigger than a uint96, use a mapping of asset and assetId to storage.
* These assets can however NOT be used as underlying assets (processIndirectDeposit() must revert).
*/
function _getKeyFromAsset(address asset, uint256 assetId) internal view virtual returns (bytes32 key) {
assembly {
// Shift the assetId to the left by 20 bytes (160 bits).
// Then OR the result with the address.
key := or(shl(160, assetId), asset)
}
}
/**
* @notice Returns the contract address and id of an asset based on the unique identifier.
* @param key The unique identifier.
* @return asset The contract address of the asset.
* @return assetId The id of the asset.
*/
function _getAssetFromKey(bytes32 key) internal view virtual returns (address asset, uint256 assetId) {
assembly {
// Shift to the right by 20 bytes (160 bits) to extract the uint96 assetId.
assetId := shr(160, key)
// Use bitmask to extract the address from the rightmost 160 bits.
asset := and(key, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)
}
}
/*///////////////////////////////////////////////////////////////
PRICING LOGIC
///////////////////////////////////////////////////////////////*/
/**
* @notice Returns the usd value of an asset.
* @param creditor The contract address of the Creditor.
* @param asset The contract address of the asset.
* @param assetId The id of the asset.
* @param assetAmount The amount of assets.
* @return valueInUsd The value of the asset denominated in USD, with 18 Decimals precision.
* @return collateralFactor The collateral factor of the asset for a given Creditor, with 4 decimals precision.
* @return liquidationFactor The liquidation factor of the asset for a given Creditor, with 4 decimals precision.
*/
function getValue(address creditor, address asset, uint256 assetId, uint256 assetAmount)
public
view
virtual
returns (uint256, uint256, uint256);
/*///////////////////////////////////////////////////////////////
RISK VARIABLES MANAGEMENT
///////////////////////////////////////////////////////////////*/
/**
* @notice Returns the risk factors of an asset for a Creditor.
* @param creditor The contract address of the Creditor.
* @param asset The contract address of the asset.
* @param assetId The id of the asset.
* @return collateralFactor The collateral factor of the asset for the Creditor, 4 decimals precision.
* @return liquidationFactor The liquidation factor of the asset for the Creditor, 4 decimals precision.
*/
function getRiskFactors(address creditor, address asset, uint256 assetId)
external
view
virtual
returns (uint16 collateralFactor, uint16 liquidationFactor);
/*///////////////////////////////////////////////////////////////
WITHDRAWALS AND DEPOSITS
///////////////////////////////////////////////////////////////*/
/**
* @notice Increases the exposure to an asset on a direct deposit.
* @param creditor The contract address of the Creditor.
* @param asset The contract address of the asset.
* @param assetId The id of the asset.
* @param amount The amount of tokens.
* @return recursiveCalls The number of calls done to different asset modules to process the deposit/withdrawal of the asset.
*/
function processDirectDeposit(address creditor, address asset, uint256 assetId, uint256 amount)
public
virtual
returns (uint256 recursiveCalls);
/**
* @notice Increases the exposure to an asset on an indirect deposit.
* @param creditor The contract address of the Creditor.
* @param asset The contract address of the asset.
* @param assetId The id of the asset.
* @param exposureUpperAssetToAsset The amount of exposure of the upper asset to the asset of this Asset Module.
* @param deltaExposureUpperAssetToAsset The increase or decrease in exposure of the upper asset to the asset of this Asset Module since last interaction.
* @return recursiveCalls The number of calls done to different asset modules to process the deposit/withdrawal of the asset.
* @return usdExposureUpperAssetToAsset The USD value of the exposure of the upper asset to the asset of this Asset Module, 18 decimals precision.
*/
function processIndirectDeposit(
address creditor,
address asset,
uint256 assetId,
uint256 exposureUpperAssetToAsset,
int256 deltaExposureUpperAssetToAsset
) public virtual returns (uint256 recursiveCalls, uint256 usdExposureUpperAssetToAsset);
/**
* @notice Decreases the exposure to an asset on a direct withdrawal.
* @param creditor The contract address of the Creditor.
* @param asset The contract address of the asset.
* @param assetId The id of the asset.
* @param amount The amount of tokens.
*/
function processDirectWithdrawal(address creditor, address asset, uint256 assetId, uint256 amount) public virtual;
/**
* @notice Decreases the exposure to an asset on an indirect withdrawal.
* @param creditor The contract address of the creditor.
* @param asset The contract address of the asset.
* @param assetId The id of the asset.
* @param exposureUpperAssetToAsset The amount of exposure of the upper asset to the asset of this Asset Module.
* @param deltaExposureUpperAssetToAsset The increase or decrease in exposure of the upper asset to the asset of this Asset Module since last interaction.
* @return usdExposureUpperAssetToAsset The USD value of the exposure of the upper asset to the asset of this Asset Module, 18 decimals precision.
*/
function processIndirectWithdrawal(
address creditor,
address asset,
uint256 assetId,
uint256 exposureUpperAssetToAsset,
int256 deltaExposureUpperAssetToAsset
) public virtual returns (uint256 usdExposureUpperAssetToAsset);
}
/**
* Created by Pragma Labs
* SPDX-License-Identifier: BUSL-1.1
*/
pragma solidity 0.8.22;
import { AssetModule } from "./AbstractAM.sol";
import { FixedPointMathLib } from "../../../lib/solmate/src/utils/FixedPointMathLib.sol";
import { SafeCastLib } from "../../../lib/solmate/src/utils/SafeCastLib.sol";
import { IRegistry } from "../interfaces/IRegistry.sol";
import { AssetValuationLib, AssetValueAndRiskFactors } from "../../libraries/AssetValuationLib.sol";
/**
* @title Derived Asset Module
* @author Pragma Labs
* @notice Abstract contract with the minimal implementation of a Derived Asset Module.
* @dev Derived Assets are assets with underlying assets, the underlying assets can be Primary Assets or also Derived Assets.
* For Derived Assets there are no direct external oracles.
* USD values of assets must be calculated in a recursive manner via the pricing logic of the Underlying Assets.
*/
abstract contract DerivedAM is AssetModule {
using FixedPointMathLib for uint256;
/* //////////////////////////////////////////////////////////////
STORAGE
////////////////////////////////////////////////////////////// */
// Map with the risk parameters of the protocol for each Creditor.
mapping(address creditor => RiskParameters riskParameters) public riskParams;
// Map with the last exposures of each asset for each Creditor.
mapping(address creditor => mapping(bytes32 assetKey => ExposuresPerAsset)) public lastExposuresAsset;
// Map with the last amount of exposure of each underlying asset for each asset for each Creditor.
mapping(address creditor => mapping(bytes32 assetKey => mapping(bytes32 underlyingAssetKey => uint256 exposure)))
public lastExposureAssetToUnderlyingAsset;
// Struct with the risk parameters of the protocol for a specific Creditor.
struct RiskParameters {
// The exposure in USD of the Creditor to the protocol at the last interaction, 18 decimals precision.
uint112 lastUsdExposureProtocol;
// The maximum exposure in USD of the Creditor to the protocol, 18 decimals precision.
uint112 maxUsdExposureProtocol;
// The risk factor of the protocol for a Creditor, 4 decimals precision.
uint16 riskFactor;
}
// Struct with the exposures of a specific asset for a specific Creditor.
struct ExposuresPerAsset {
// The amount of exposure of the Creditor to the asset at the last interaction.
uint112 lastExposureAsset;
// The exposure in USD of the Creditor to the asset at the last interaction, 18 decimals precision.
uint112 lastUsdExposureAsset;
}
/* //////////////////////////////////////////////////////////////
ERRORS
////////////////////////////////////////////////////////////// */
error RiskFactorNotInLimits();
/* //////////////////////////////////////////////////////////////
CONSTRUCTOR
////////////////////////////////////////////////////////////// */
/**
* @param registry_ The contract address of the Registry.
* @param assetType_ Identifier for the token standard of the asset.
* 0 = Unknown asset.
* 1 = ERC20.
* 2 = ERC721.
* 3 = ERC1155.
* ...
*/
constructor(address registry_, uint256 assetType_) AssetModule(registry_, assetType_) { }
/*///////////////////////////////////////////////////////////////
ASSET INFORMATION
///////////////////////////////////////////////////////////////*/
/**
* @notice Returns the unique identifiers of the underlying assets.
* @param assetKey The unique identifier of the asset.
* @return underlyingAssetKeys The unique identifiers of the underlying assets.
*/
function _getUnderlyingAssets(bytes32 assetKey)
internal
view
virtual
returns (bytes32[] memory underlyingAssetKeys);
/**
* @notice Calculates the USD rate of 10**18 underlying assets.
* @param creditor The contract address of the Creditor.
* @param underlyingAssetKeys The unique identifiers of the underlying assets.
* @return rateUnderlyingAssetsToUsd The USD rates of 10**18 tokens of underlying asset, with 18 decimals precision.
* @dev The USD price per 10**18 tokens is used (instead of the USD price per token) to guarantee sufficient precision.
*/
function _getRateUnderlyingAssetsToUsd(address creditor, bytes32[] memory underlyingAssetKeys)
internal
view
virtual
returns (AssetValueAndRiskFactors[] memory rateUnderlyingAssetsToUsd)
{
uint256 length = underlyingAssetKeys.length;
address[] memory underlyingAssets = new address[](length);
uint256[] memory underlyingAssetIds = new uint256[](length);
uint256[] memory amounts = new uint256[](length);
for (uint256 i; i < length; ++i) {
(underlyingAssets[i], underlyingAssetIds[i]) = _getAssetFromKey(underlyingAssetKeys[i]);
// We use the USD price per 10**18 tokens instead of the USD price per token to guarantee
// sufficient precision.
amounts[i] = 1e18;
}
rateUnderlyingAssetsToUsd =
IRegistry(REGISTRY).getValuesInUsdRecursive(creditor, underlyingAssets, underlyingAssetIds, amounts);
}
/**
* @notice Calculates for a given amount of an Asset the corresponding amount(s) of Underlying Asset(s).
* @param creditor The contract address of the Creditor.
* @param assetKey The unique identifier of the asset.
* @param assetAmount The amount of the asset, in the decimal precision of the Asset.
* @param underlyingAssetKeys The unique identifiers of the underlying assets.
* @return underlyingAssetsAmounts The corresponding amount(s) of Underlying Asset(s), in the decimal precision of the Underlying Asset.
* @return rateUnderlyingAssetsToUsd The USD rates of 10**18 tokens of underlying asset, with 18 decimals precision.
* @dev The USD price per 10**18 tokens is used (instead of the USD price per token) to guarantee sufficient precision.
*/
function _getUnderlyingAssetsAmounts(
address creditor,
bytes32 assetKey,
uint256 assetAmount,
bytes32[] memory underlyingAssetKeys
)
internal
view
virtual
returns (uint256[] memory underlyingAssetsAmounts, AssetValueAndRiskFactors[] memory rateUnderlyingAssetsToUsd);
/*///////////////////////////////////////////////////////////////
RISK VARIABLES MANAGEMENT
///////////////////////////////////////////////////////////////*/
/**
* @notice Returns the risk factors of an asset for a Creditor.
* @param creditor The contract address of the Creditor.
* @param asset The contract address of the asset.
* @param assetId The id of the asset.
* @return collateralFactor The collateral factor of the asset for the Creditor, 4 decimals precision.
* @return liquidationFactor The liquidation factor of the asset for the Creditor, 4 decimals precision.
*/
function getRiskFactors(address creditor, address asset, uint256 assetId)
external
view
virtual
override
returns (uint16 collateralFactor, uint16 liquidationFactor);
/**
* @notice Sets the risk parameters of the Protocol for a given Creditor.
* @param creditor The contract address of the Creditor.
* @param maxUsdExposureProtocol_ The maximum USD exposure of the protocol for each Creditor, denominated in USD with 18 decimals precision.
* @param riskFactor The risk factor of the asset for the Creditor, 4 decimals precision.
*/
function setRiskParameters(address creditor, uint112 maxUsdExposureProtocol_, uint16 riskFactor)
external
onlyRegistry
{
if (riskFactor > AssetValuationLib.ONE_4) revert RiskFactorNotInLimits();
riskParams[creditor].maxUsdExposureProtocol = maxUsdExposureProtocol_;
riskParams[creditor].riskFactor = riskFactor;
}
/*///////////////////////////////////////////////////////////////
PRICING LOGIC
///////////////////////////////////////////////////////////////*/
/**
* @notice Returns the USD value of an asset.
* @param creditor The contract address of the Creditor.
* @param asset The contract address of the asset.
* @param assetId The id of the asset.
* @param assetAmount The amount of assets.
* @return valueInUsd The value of the asset denominated in USD, with 18 Decimals precision.
* @return collateralFactor The collateral factor of the asset for a given Creditor, with 4 decimals precision.
* @return liquidationFactor The liquidation factor of the asset for a given Creditor, with 4 decimals precision.
*/
function getValue(address creditor, address asset, uint256 assetId, uint256 assetAmount)
public
view
virtual
override
returns (uint256 valueInUsd, uint256 collateralFactor, uint256 liquidationFactor)
{
bytes32 assetKey = _getKeyFromAsset(asset, assetId);
bytes32[] memory underlyingAssetKeys = _getUnderlyingAssets(assetKey);
(uint256[] memory underlyingAssetsAmounts, AssetValueAndRiskFactors[] memory rateUnderlyingAssetsToUsd) =
_getUnderlyingAssetsAmounts(creditor, assetKey, assetAmount, underlyingAssetKeys);
// Check if rateToUsd for the underlying assets was already calculated in _getUnderlyingAssetsAmounts().
if (rateUnderlyingAssetsToUsd.length == 0) {
// If not, get the USD value of the underlying assets recursively.
rateUnderlyingAssetsToUsd = _getRateUnderlyingAssetsToUsd(creditor, underlyingAssetKeys);
}
(valueInUsd, collateralFactor, liquidationFactor) =
_calculateValueAndRiskFactors(creditor, underlyingAssetsAmounts, rateUnderlyingAssetsToUsd);
}
/**
* @notice Returns the USD value of an asset.
* @param creditor The contract address of the Creditor.
* @param underlyingAssetsAmounts The corresponding amount(s) of Underlying Asset(s), in the decimal precision of the Underlying Asset.
* @param rateUnderlyingAssetsToUsd The USD rates of 10**18 tokens of underlying asset, with 18 decimals precision.
* @return valueInUsd The value of the asset denominated in USD, with 18 Decimals precision.
* @return collateralFactor The collateral factor of the asset for a given Creditor, with 4 decimals precision.
* @return liquidationFactor The liquidation factor of the asset for a given Creditor, with 4 decimals precision.
* @dev We take the most conservative (lowest) risk factor of all underlying assets.
*/
function _calculateValueAndRiskFactors(
address creditor,
uint256[] memory underlyingAssetsAmounts,
AssetValueAndRiskFactors[] memory rateUnderlyingAssetsToUsd
) internal view virtual returns (uint256 valueInUsd, uint256 collateralFactor, uint256 liquidationFactor);
/*///////////////////////////////////////////////////////////////
WITHDRAWALS AND DEPOSITS
///////////////////////////////////////////////////////////////*/
/**
* @notice Increases the exposure to an asset on a direct deposit.
* @param creditor The contract address of the Creditor.
* @param asset The contract address of the asset.
* @param assetId The id of the asset.
* @param amount The amount of tokens.
* @return recursiveCalls The number of calls done to different asset modules to process the deposit/withdrawal of the asset.
*/
function processDirectDeposit(address creditor, address asset, uint256 assetId, uint256 amount)
public
virtual
override
onlyRegistry
returns (uint256 recursiveCalls)
{
bytes32 assetKey = _getKeyFromAsset(asset, assetId);
// Calculate and update the new exposure to Asset.
uint256 exposureAsset = _getAndUpdateExposureAsset(creditor, assetKey, int256(amount));
(uint256 underlyingCalls,) = _processDeposit(exposureAsset, creditor, assetKey);
unchecked {
recursiveCalls = underlyingCalls + 1;
}
}
/**
* @notice Increases the exposure to an asset on an indirect deposit.
* @param creditor The contract address of the Creditor.
* @param asset The contract address of the asset.
* @param assetId The id of the asset.
* @param exposureUpperAssetToAsset The amount of exposure of the upper asset to the asset of this Asset Module.
* @param deltaExposureUpperAssetToAsset The increase or decrease in exposure of the upper asset to the asset of this Asset Module since last interaction.
* @return recursiveCalls The number of calls done to different asset modules to process the deposit/withdrawal of the asset.
* @return usdExposureUpperAssetToAsset The USD value of the exposure of the upper asset to the asset of this Asset Module, 18 decimals precision.
* @dev An indirect deposit, is initiated by a deposit of another derived asset (the upper asset),
* from which the asset of this Asset Module is an underlying asset.
*/
function processIndirectDeposit(
address creditor,
address asset,
uint256 assetId,
uint256 exposureUpperAssetToAsset,
int256 deltaExposureUpperAssetToAsset
) public virtual override onlyRegistry returns (uint256 recursiveCalls, uint256 usdExposureUpperAssetToAsset) {
bytes32 assetKey = _getKeyFromAsset(asset, assetId);
// Calculate and update the new exposure to "Asset".
uint256 exposureAsset = _getAndUpdateExposureAsset(creditor, assetKey, deltaExposureUpperAssetToAsset);
(uint256 underlyingCalls, uint256 usdExposureAsset) = _processDeposit(exposureAsset, creditor, assetKey);
if (exposureAsset == 0 || usdExposureAsset == 0) {
usdExposureUpperAssetToAsset = 0;
} else {
// Calculate the USD value of the exposure of the upper asset to the underlying asset.
usdExposureUpperAssetToAsset = usdExposureAsset.mulDivDown(exposureUpperAssetToAsset, exposureAsset);
}
unchecked {
recursiveCalls = underlyingCalls + 1;
}
}
/**
* @notice Decreases the exposure to an asset on a direct withdrawal.
* @param creditor The contract address of the Creditor.
* @param asset The contract address of the asset.
* @param assetId The id of the asset.
* @param amount The amount of tokens.
*/
function processDirectWithdrawal(address creditor, address asset, uint256 assetId, uint256 amount)
public
virtual
override
onlyRegistry
{
bytes32 assetKey = _getKeyFromAsset(asset, assetId);
// Calculate and update the new exposure to "Asset".
uint256 exposureAsset = _getAndUpdateExposureAsset(creditor, assetKey, -int256(amount));
_processWithdrawal(creditor, assetKey, exposureAsset);
}
/**
* @notice Decreases the exposure to an asset on an indirect withdrawal.
* @param creditor The contract address of the Creditor.
* @param asset The contract address of the asset.
* @param assetId The id of the asset.
* @param exposureUpperAssetToAsset The amount of exposure of the upper asset to the asset of this Asset Module.
* @param deltaExposureUpperAssetToAsset The increase or decrease in exposure of the upper asset to the asset of this Asset Module since last interaction.
* @return usdExposureUpperAssetToAsset The USD value of the exposure of the upper asset to the asset of this Asset Module, 18 decimals precision.
* @dev An indirect withdrawal is initiated by a withdrawal of another Derived Asset (the upper asset),
* from which the asset of this Asset Module is an Underlying Asset.
*/
function processIndirectWithdrawal(
address creditor,
address asset,
uint256 assetId,
uint256 exposureUpperAssetToAsset,
int256 deltaExposureUpperAssetToAsset
) public virtual override onlyRegistry returns (uint256 usdExposureUpperAssetToAsset) {
bytes32 assetKey = _getKeyFromAsset(asset, assetId);
// Calculate and update the new exposure to "Asset".
uint256 exposureAsset = _getAndUpdateExposureAsset(creditor, assetKey, deltaExposureUpperAssetToAsset);
uint256 usdExposureAsset = _processWithdrawal(creditor, assetKey, exposureAsset);
if (exposureAsset == 0 || usdExposureAsset == 0) {
usdExposureUpperAssetToAsset = 0;
} else {
// Calculate the USD value of the exposure of the Upper Asset to the Underlying asset.
usdExposureUpperAssetToAsset = usdExposureAsset.mulDivDown(exposureUpperAssetToAsset, exposureAsset);
}
}
/**
* @notice Update the exposure to an asset and its underlying asset(s) on deposit.
* @param exposureAsset The updated exposure to the asset.
* @param creditor The contract address of the Creditor.
* @param assetKey The unique identifier of the asset.
* @return underlyingCalls The number of calls done to different asset modules to process the deposit/withdrawal of the underlying assets.
* @return usdExposureAsset The USD value of the exposure of the asset, 18 decimals precision.
* @dev The checks on exposures are only done to block deposits that would over-expose a Creditor to a certain asset or protocol.
* Underflows will not revert, but the exposure is instead set to 0.
*/
function _processDeposit(uint256 exposureAsset, address creditor, bytes32 assetKey)
internal
virtual
returns (uint256 underlyingCalls, uint256 usdExposureAsset)
{
uint256 usdExposureProtocol;
{
// Get the unique identifier(s) of the underlying asset(s).
bytes32[] memory underlyingAssetKeys = _getUnderlyingAssets(assetKey);
// Get the exposure to the asset's underlying asset(s) (in the decimal precision of the underlying assets).
(uint256[] memory exposureAssetToUnderlyingAssets,) =
_getUnderlyingAssetsAmounts(creditor, assetKey, exposureAsset, underlyingAssetKeys);
int256 deltaExposureAssetToUnderlyingAsset;
address underlyingAsset;
uint256 underlyingId;
uint256 underlyingCalls_;
uint256 usdExposureToUnderlyingAsset;
for (uint256 i; i < underlyingAssetKeys.length; ++i) {
// Calculate the change in exposure to the underlying assets since last interaction.
deltaExposureAssetToUnderlyingAsset = int256(exposureAssetToUnderlyingAssets[i])
- int256(uint256(lastExposureAssetToUnderlyingAsset[creditor][assetKey][underlyingAssetKeys[i]]));
// Update "lastExposureAssetToUnderlyingAsset".
lastExposureAssetToUnderlyingAsset[creditor][assetKey][underlyingAssetKeys[i]] =
exposureAssetToUnderlyingAssets[i];
// Get the USD Value of the total exposure of "Asset" for its "Underlying Assets" at index "i".
// If the "underlyingAsset" has one or more underlying assets itself, the lower level
// Asset Module(s) will recursively update their respective exposures and return
// the requested USD value to this Asset Module.
(underlyingAsset, underlyingId) = _getAssetFromKey(underlyingAssetKeys[i]);
(underlyingCalls_, usdExposureToUnderlyingAsset) = IRegistry(REGISTRY)
.getUsdValueExposureToUnderlyingAssetAfterDeposit(
creditor,
underlyingAsset,
underlyingId,
exposureAssetToUnderlyingAssets[i],
deltaExposureAssetToUnderlyingAsset
);
usdExposureAsset += usdExposureToUnderlyingAsset;
unchecked {
underlyingCalls += underlyingCalls_;
}
}
// Cache and update lastUsdExposureAsset.
uint256 lastUsdExposureAsset = lastExposuresAsset[creditor][assetKey].lastUsdExposureAsset;
// If usdExposureAsset is bigger than uint112, then check on usdExposureProtocol below will revert.
lastExposuresAsset[creditor][assetKey].lastUsdExposureAsset = uint112(usdExposureAsset);
// Cache lastUsdExposureProtocol.
uint256 lastUsdExposureProtocol = riskParams[creditor].lastUsdExposureProtocol;
// Update lastUsdExposureProtocol.
unchecked {
if (usdExposureAsset >= lastUsdExposureAsset) {
usdExposureProtocol = lastUsdExposureProtocol + (usdExposureAsset - lastUsdExposureAsset);
} else if (lastUsdExposureProtocol > lastUsdExposureAsset - usdExposureAsset) {
usdExposureProtocol = lastUsdExposureProtocol - (lastUsdExposureAsset - usdExposureAsset);
}
// For the else case: (lastUsdExposureProtocol < lastUsdExposureAsset - usdExposureAsset),
// usdExposureProtocol is set to 0, but usdExposureProtocol is already 0.
}
// The exposure must be strictly smaller than the maxExposure, not equal to or smaller than.
// This is to ensure that all deposits revert when maxExposure is set to 0, also deposits with 0 amounts.
if (usdExposureProtocol >= riskParams[creditor].maxUsdExposureProtocol) {
revert AssetModule.ExposureNotInLimits();
}
}
riskParams[creditor].lastUsdExposureProtocol = uint112(usdExposureProtocol);
}
/**
* @notice Update the exposure to an asset and its underlying asset(s) on withdrawal.
* @param creditor The contract address of the Creditor.
* @param assetKey The unique identifier of the asset.
* @param exposureAsset The updated exposure to the asset.
* @return usdExposureAsset The USD value of the exposure of the asset, 18 decimals precision.
* @dev The checks on exposures are only done to block deposits that would over-expose a Creditor to a certain asset or protocol.
* Underflows will not revert, but the exposure is instead set to 0.
* @dev Due to changing usd-prices of underlying assets, or due to changing compositions of upper assets,
* the exposure to a derived asset can increase or decrease over time independent of deposits/withdrawals.
* When derived assets are deposited/withdrawn, these changes in exposure since last interaction are also synced.
* As such the actual exposure on a withdrawal of a derived asset can exceed the maxExposure, but this should never be blocked,
* (the withdrawal actually improves the situation by making the asset less over-exposed).
*/
function _processWithdrawal(address creditor, bytes32 assetKey, uint256 exposureAsset)
internal
virtual
returns (uint256 usdExposureAsset)
{
// Get the unique identifier(s) of the underlying asset(s).
bytes32[] memory underlyingAssetKeys = _getUnderlyingAssets(assetKey);
// Get the exposure to the asset's underlying asset(s) (in the decimal precision of the underlying assets).
(uint256[] memory exposureAssetToUnderlyingAssets,) =
_getUnderlyingAssetsAmounts(creditor, assetKey, exposureAsset, underlyingAssetKeys);
int256 deltaExposureAssetToUnderlyingAsset;
address underlyingAsset;
uint256 underlyingId;
for (uint256 i; i < underlyingAssetKeys.length; ++i) {
// Calculate the change in exposure to the underlying assets since last interaction.
deltaExposureAssetToUnderlyingAsset = int256(exposureAssetToUnderlyingAssets[i])
- int256(uint256(lastExposureAssetToUnderlyingAsset[creditor][assetKey][underlyingAssetKeys[i]]));
// Update "lastExposureAssetToUnderlyingAsset".
lastExposureAssetToUnderlyingAsset[creditor][assetKey][underlyingAssetKeys[i]] =
exposureAssetToUnderlyingAssets[i];
// Get the USD Value of the total exposure of "Asset" for for all of its "Underlying Assets".
// If an "underlyingAsset" has one or more underlying assets itself, the lower level
// Asset Modules will recursively update their respective exposures and return
// the requested USD value to this Asset Module.
(underlyingAsset, underlyingId) = _getAssetFromKey(underlyingAssetKeys[i]);
usdExposureAsset += IRegistry(REGISTRY).getUsdValueExposureToUnderlyingAssetAfterWithdrawal(
creditor,
underlyingAsset,
underlyingId,
exposureAssetToUnderlyingAssets[i],
deltaExposureAssetToUnderlyingAsset
);
}
// Cache and update lastUsdExposureAsset.
uint256 lastUsdExposureAsset = lastExposuresAsset[creditor][assetKey].lastUsdExposureAsset;
// If usdExposureAsset is bigger than uint112, then safecast on usdExposureProtocol below will revert.
lastExposuresAsset[creditor][assetKey].lastUsdExposureAsset = uint112(usdExposureAsset);
// Cache lastUsdExposureProtocol.
uint256 lastUsdExposureProtocol = riskParams[creditor].lastUsdExposureProtocol;
// Update lastUsdExposureProtocol.
uint256 usdExposureProtocol;
unchecked {
if (usdExposureAsset >= lastUsdExposureAsset) {
usdExposureProtocol = lastUsdExposureProtocol + (usdExposureAsset - lastUsdExposureAsset);
} else if (lastUsdExposureProtocol > lastUsdExposureAsset - usdExposureAsset) {
usdExposureProtocol = lastUsdExposureProtocol - (lastUsdExposureAsset - usdExposureAsset);
}
// For the else case: (lastUsdExposureProtocol < lastUsdExposureAsset - usdExposureAsset),
// usdExposureProtocol is set to 0, but usdExposureProtocol is already 0.
}
riskParams[creditor].lastUsdExposureProtocol = SafeCastLib.safeCastTo112(usdExposureProtocol);
}
/**
* @notice Updates the exposure to the asset.
* @param creditor The contract address of the Creditor.
* @param assetKey The unique identifier of the asset.
* @param deltaAsset The increase or decrease in asset amount since the last interaction.
* @return exposureAsset The updated exposure to the asset.
* @dev The checks on exposures are only done to block deposits that would over-expose a Creditor to a certain asset or protocol.
* Underflows will not revert, but the exposure is instead set to 0.
*/
function _getAndUpdateExposureAsset(address creditor, bytes32 assetKey, int256 deltaAsset)
internal
returns (uint256 exposureAsset)
{
// Update exposureAssetLast.
if (deltaAsset > 0) {
exposureAsset = lastExposuresAsset[creditor][assetKey].lastExposureAsset + uint256(deltaAsset);
} else {
uint256 exposureAssetLast = lastExposuresAsset[creditor][assetKey].lastExposureAsset;
exposureAsset = exposureAssetLast > uint256(-deltaAsset) ? exposureAssetLast - uint256(-deltaAsset) : 0;
}
lastExposuresAsset[creditor][assetKey].lastExposureAsset = SafeCastLib.safeCastTo112(exposureAsset);
}
}
/**
* Created by Pragma Labs
* SPDX-License-Identifier: BUSL-1.1
*/
pragma solidity 0.8.22;
// Struct with risk and valuation related information for a certain asset.
struct AssetValueAndRiskFactors {
// The value of the asset.
uint256 assetValue;
// The collateral factor of the asset, for a given creditor.
uint256 collateralFactor;
// The liquidation factor of the asset, for a given creditor.
uint256 liquidationFactor;
}
/**
* @title Asset Valuation Library
* @author Pragma Labs
* @notice The Asset Valuation Library is responsible for calculating the risk weighted values of combinations of assets.
*/
library AssetValuationLib {
/*///////////////////////////////////////////////////////////////
CONSTANTS
///////////////////////////////////////////////////////////////*/
uint256 internal constant ONE_4 = 10_000;
/*///////////////////////////////////////////////////////////////
RISK FACTORS
///////////////////////////////////////////////////////////////*/
/**
* @notice Calculates the collateral value given a combination of asset values and corresponding collateral factors.
* @param valuesAndRiskFactors Array of asset values and corresponding collateral factors.
* @return collateralValue The collateral value of the given assets.
*/
function _calculateCollateralValue(AssetValueAndRiskFactors[] memory valuesAndRiskFactors)
internal
pure
returns (uint256 collateralValue)
{
for (uint256 i; i < valuesAndRiskFactors.length; ++i) {
collateralValue += valuesAndRiskFactors[i].assetValue * valuesAndRiskFactors[i].collateralFactor;
}
collateralValue = collateralValue / ONE_4;
}
/**
* @notice Calculates the liquidation value given a combination of asset values and corresponding liquidation factors.
* @param valuesAndRiskFactors List of asset values and corresponding liquidation factors.
* @return liquidationValue The liquidation value of the given assets.
*/
function _calculateLiquidationValue(AssetValueAndRiskFactors[] memory valuesAndRiskFactors)
internal
pure
returns (uint256 liquidationValue)
{
for (uint256 i; i < valuesAndRiskFactors.length; ++i) {
liquidationValue += valuesAndRiskFactors[i].assetValue * valuesAndRiskFactors[i].liquidationFactor;
}
liquidationValue = liquidationValue / ONE_4;
}
}
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;
/// @notice Modern and gas efficient ERC20 + EIP-2612 implementation.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC20.sol)
/// @author Modified from Uniswap (https://github.com/Uniswap/uniswap-v2-core/blob/master/contracts/UniswapV2ERC20.sol)
/// @dev Do not manually set balances without updating totalSupply, as the sum of all user balances must not exceed it.
abstract contract ERC20 {
/*//////////////////////////////////////////////////////////////
EVENTS
//////////////////////////////////////////////////////////////*/
event Transfer(address indexed from, address indexed to, uint256 amount);
event Approval(address indexed owner, address indexed spender, uint256 amount);
/*//////////////////////////////////////////////////////////////
METADATA STORAGE
//////////////////////////////////////////////////////////////*/
string public name;
string public symbol;
uint8 public immutable decimals;
/*//////////////////////////////////////////////////////////////
ERC20 STORAGE
//////////////////////////////////////////////////////////////*/
uint256 public totalSupply;
mapping(address => uint256) public balanceOf;
mapping(address => mapping(address => uint256)) public allowance;
/*//////////////////////////////////////////////////////////////
EIP-2612 STORAGE
//////////////////////////////////////////////////////////////*/
uint256 internal immutable INITIAL_CHAIN_ID;
bytes32 internal immutable INITIAL_DOMAIN_SEPARATOR;
mapping(address => uint256) public nonces;
/*//////////////////////////////////////////////////////////////
CONSTRUCTOR
//////////////////////////////////////////////////////////////*/
constructor(
string memory _name,
string memory _symbol,
uint8 _decimals
) {
name = _name;
symbol = _symbol;
decimals = _decimals;
INITIAL_CHAIN_ID = block.chainid;
INITIAL_DOMAIN_SEPARATOR = computeDomainSeparator();
}
/*//////////////////////////////////////////////////////////////
ERC20 LOGIC
//////////////////////////////////////////////////////////////*/
function approve(address spender, uint256 amount) public virtual returns (bool) {
allowance[msg.sender][spender] = amount;
emit Approval(msg.sender, spender, amount);
return true;
}
function transfer(address to, uint256 amount) public virtual returns (bool) {
balanceOf[msg.sender] -= amount;
// Cannot overflow because the sum of all user
// balances can't exceed the max uint256 value.
unchecked {
balanceOf[to] += amount;
}
emit Transfer(msg.sender, to, amount);
return true;
}
function transferFrom(
address from,
address to,
uint256 amount
) public virtual returns (bool) {
uint256 allowed = allowance[from][msg.sender]; // Saves gas for limited approvals.
if (allowed != type(uint256).max) allowance[from][msg.sender] = allowed - amount;
balanceOf[from] -= amount;
// Cannot overflow because the sum of all user
// balances can't exceed the max uint256 value.
unchecked {
balanceOf[to] += amount;
}
emit Transfer(from, to, amount);
return true;
}
/*//////////////////////////////////////////////////////////////
EIP-2612 LOGIC
//////////////////////////////////////////////////////////////*/
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) public virtual {
require(deadline >= block.timestamp, "PERMIT_DEADLINE_EXPIRED");
// Unchecked because the only math done is incrementing
// the owner's nonce which cannot realistically overflow.
unchecked {
address recoveredAddress = ecrecover(
keccak256(
abi.encodePacked(
"\x19\x01",
DOMAIN_SEPARATOR(),
keccak256(
abi.encode(
keccak256(
"Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"
),
owner,
spender,
value,
nonces[owner]++,
deadline
)
)
)
),
v,
r,
s
);
require(recoveredAddress != address(0) && recoveredAddress == owner, "INVALID_SIGNER");
allowance[recoveredAddress][spender] = value;
}
emit Approval(owner, spender, value);
}
function DOMAIN_SEPARATOR() public view virtual returns (bytes32) {
return block.chainid == INITIAL_CHAIN_ID ? INITIAL_DOMAIN_SEPARATOR : computeDomainSeparator();
}
function computeDomainSeparator() internal view virtual returns (bytes32) {
return
keccak256(
abi.encode(
keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
keccak256(bytes(name)),
keccak256("1"),
block.chainid,
address(this)
)
);
}
/*//////////////////////////////////////////////////////////////
INTERNAL MINT/BURN LOGIC
//////////////////////////////////////////////////////////////*/
function _mint(address to, uint256 amount) internal virtual {
totalSupply += amount;
// Cannot overflow because the sum of all user
// balances can't exceed the max uint256 value.
unchecked {
balanceOf[to] += amount;
}
emit Transfer(address(0), to, amount);
}
function _burn(address from, uint256 amount) internal virtual {
balanceOf[from] -= amount;
// Cannot underflow because a user's balance
// will never be larger than the total supply.
unchecked {
totalSupply -= amount;
}
emit Transfer(from, address(0), amount);
}
}
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;
/// @notice Modern, minimalist, and gas efficient ERC-721 implementation.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC721.sol)
abstract contract ERC721 {
/*//////////////////////////////////////////////////////////////
EVENTS
//////////////////////////////////////////////////////////////*/
event Transfer(address indexed from, address indexed to, uint256 indexed id);
event Approval(address indexed owner, address indexed spender, uint256 indexed id);
event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
/*//////////////////////////////////////////////////////////////
METADATA STORAGE/LOGIC
//////////////////////////////////////////////////////////////*/
string public name;
string public symbol;
function tokenURI(uint256 id) public view virtual returns (string memory);
/*//////////////////////////////////////////////////////////////
ERC721 BALANCE/OWNER STORAGE
//////////////////////////////////////////////////////////////*/
mapping(uint256 => address) internal _ownerOf;
mapping(address => uint256) internal _balanceOf;
function ownerOf(uint256 id) public view virtual returns (address owner) {
require((owner = _ownerOf[id]) != address(0), "NOT_MINTED");
}
function balanceOf(address owner) public view virtual returns (uint256) {
require(owner != address(0), "ZERO_ADDRESS");
return _balanceOf[owner];
}
/*//////////////////////////////////////////////////////////////
ERC721 APPROVAL STORAGE
//////////////////////////////////////////////////////////////*/
mapping(uint256 => address) public getApproved;
mapping(address => mapping(address => bool)) public isApprovedForAll;
/*//////////////////////////////////////////////////////////////
CONSTRUCTOR
//////////////////////////////////////////////////////////////*/
constructor(string memory _name, string memory _symbol) {
name = _name;
symbol = _symbol;
}
/*//////////////////////////////////////////////////////////////
ERC721 LOGIC
//////////////////////////////////////////////////////////////*/
function approve(address spender, uint256 id) public virtual {
address owner = _ownerOf[id];
require(msg.sender == owner || isApprovedForAll[owner][msg.sender], "NOT_AUTHORIZED");
getApproved[id] = spender;
emit Approval(owner, spender, id);
}
function setApprovalForAll(address operator, bool approved) public virtual {
isApprovedForAll[msg.sender][operator] = approved;
emit ApprovalForAll(msg.sender, operator, approved);
}
function transferFrom(
address from,
address to,
uint256 id
) public virtual {
require(from == _ownerOf[id], "WRONG_FROM");
require(to != address(0), "INVALID_RECIPIENT");
require(
msg.sender == from || isApprovedForAll[from][msg.sender] || msg.sender == getApproved[id],
"NOT_AUTHORIZED"
);
// Underflow of the sender's balance is impossible because we check for
// ownership above and the recipient's balance can't realistically overflow.
unchecked {
_balanceOf[from]--;
_balanceOf[to]++;
}
_ownerOf[id] = to;
delete getApproved[id];
emit Transfer(from, to, id);
}
function safeTransferFrom(
address from,
address to,
uint256 id
) public virtual {
transferFrom(from, to, id);
require(
to.code.length == 0 ||
ERC721TokenReceiver(to).onERC721Received(msg.sender, from, id, "") ==
ERC721TokenReceiver.onERC721Received.selector,
"UNSAFE_RECIPIENT"
);
}
function safeTransferFrom(
address from,
address to,
uint256 id,
bytes calldata data
) public virtual {
transferFrom(from, to, id);
require(
to.code.length == 0 ||
ERC721TokenReceiver(to).onERC721Received(msg.sender, from, id, data) ==
ERC721TokenReceiver.onERC721Received.selector,
"UNSAFE_RECIPIENT"
);
}
/*//////////////////////////////////////////////////////////////
ERC165 LOGIC
//////////////////////////////////////////////////////////////*/
function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {
return
interfaceId == 0x01ffc9a7 || // ERC165 Interface ID for ERC165
interfaceId == 0x80ac58cd || // ERC165 Interface ID for ERC721
interfaceId == 0x5b5e139f; // ERC165 Interface ID for ERC721Metadata
}
/*//////////////////////////////////////////////////////////////
INTERNAL MINT/BURN LOGIC
//////////////////////////////////////////////////////////////*/
function _mint(address to, uint256 id) internal virtual {
require(to != address(0), "INVALID_RECIPIENT");
require(_ownerOf[id] == address(0), "ALREADY_MINTED");
// Counter overflow is incredibly unrealistic.
unchecked {
_balanceOf[to]++;
}
_ownerOf[id] = to;
emit Transfer(address(0), to, id);
}
function _burn(uint256 id) internal virtual {
address owner = _ownerOf[id];
require(owner != address(0), "NOT_MINTED");
// Ownership check above ensures no underflow.
unchecked {
_balanceOf[owner]--;
}
delete _ownerOf[id];
delete getApproved[id];
emit Transfer(owner, address(0), id);
}
/*//////////////////////////////////////////////////////////////
INTERNAL SAFE MINT LOGIC
//////////////////////////////////////////////////////////////*/
function _safeMint(address to, uint256 id) internal virtual {
_mint(to, id);
require(
to.code.length == 0 ||
ERC721TokenReceiver(to).onERC721Received(msg.sender, address(0), id, "") ==
ERC721TokenReceiver.onERC721Received.selector,
"UNSAFE_RECIPIENT"
);
}
function _safeMint(
address to,
uint256 id,
bytes memory data
) internal virtual {
_mint(to, id);
require(
to.code.length == 0 ||
ERC721TokenReceiver(to).onERC721Received(msg.sender, address(0), id, data) ==
ERC721TokenReceiver.onERC721Received.selector,
"UNSAFE_RECIPIENT"
);
}
}
/// @notice A generic interface for a contract which properly accepts ERC721 tokens.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC721.sol)
abstract contract ERC721TokenReceiver {
function onERC721Received(
address,
address,
uint256,
bytes calldata
) external virtual returns (bytes4) {
return ERC721TokenReceiver.onERC721Received.selector;
}
}
// https://github.com/Uniswap/v3-core/blob/main/contracts/libraries/FixedPoint96.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity 0.8.22;
/// @title FixedPoint96
/// @notice A library for handling binary fixed point numbers, see https://en.wikipedia.org/wiki/Q_(number_format)
/// @dev Used in SqrtPriceMath.sol
library FixedPoint96 {
uint8 internal constant RESOLUTION = 96;
uint256 internal constant Q96 = 0x1000000000000000000000000;
}
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;
/// @notice Arithmetic library with operations for fixed-point numbers.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/FixedPointMathLib.sol)
/// @author Inspired by USM (https://github.com/usmfum/USM/blob/master/contracts/WadMath.sol)
library FixedPointMathLib {
/*//////////////////////////////////////////////////////////////
SIMPLIFIED FIXED POINT OPERATIONS
//////////////////////////////////////////////////////////////*/
uint256 internal constant MAX_UINT256 = 2**256 - 1;
uint256 internal constant WAD = 1e18; // The scalar of ETH and most ERC20s.
function mulWadDown(uint256 x, uint256 y) internal pure returns (uint256) {
return mulDivDown(x, y, WAD); // Equivalent to (x * y) / WAD rounded down.
}
function mulWadUp(uint256 x, uint256 y) internal pure returns (uint256) {
return mulDivUp(x, y, WAD); // Equivalent to (x * y) / WAD rounded up.
}
function divWadDown(uint256 x, uint256 y) internal pure returns (uint256) {
return mulDivDown(x, WAD, y); // Equivalent to (x * WAD) / y rounded down.
}
function divWadUp(uint256 x, uint256 y) internal pure returns (uint256) {
return mulDivUp(x, WAD, y); // Equivalent to (x * WAD) / y rounded up.
}
/*//////////////////////////////////////////////////////////////
LOW LEVEL FIXED POINT OPERATIONS
//////////////////////////////////////////////////////////////*/
function mulDivDown(
uint256 x,
uint256 y,
uint256 denominator
) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
// Equivalent to require(denominator != 0 && (y == 0 || x <= type(uint256).max / y))
if iszero(mul(denominator, iszero(mul(y, gt(x, div(MAX_UINT256, y)))))) {
revert(0, 0)
}
// Divide x * y by the denominator.
z := div(mul(x, y), denominator)
}
}
function mulDivUp(
uint256 x,
uint256 y,
uint256 denominator
) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
// Equivalent to require(denominator != 0 && (y == 0 || x <= type(uint256).max / y))
if iszero(mul(denominator, iszero(mul(y, gt(x, div(MAX_UINT256, y)))))) {
revert(0, 0)
}
// If x * y modulo the denominator is strictly greater than 0,
// 1 is added to round up the division of x * y by the denominator.
z := add(gt(mod(mul(x, y), denominator), 0), div(mul(x, y), denominator))
}
}
function rpow(
uint256 x,
uint256 n,
uint256 scalar
) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
switch x
case 0 {
switch n
case 0 {
// 0 ** 0 = 1
z := scalar
}
default {
// 0 ** n = 0
z := 0
}
}
default {
switch mod(n, 2)
case 0 {
// If n is even, store scalar in z for now.
z := scalar
}
default {
// If n is odd, store x in z for now.
z := x
}
// Shifting right by 1 is like dividing by 2.
let half := shr(1, scalar)
for {
// Shift n right by 1 before looping to halve it.
n := shr(1, n)
} n {
// Shift n right by 1 each iteration to halve it.
n := shr(1, n)
} {
// Revert immediately if x ** 2 would overflow.
// Equivalent to iszero(eq(div(xx, x), x)) here.
if shr(128, x) {
revert(0, 0)
}
// Store x squared.
let xx := mul(x, x)
// Round to the nearest number.
let xxRound := add(xx, half)
// Revert if xx + half overflowed.
if lt(xxRound, xx) {
revert(0, 0)
}
// Set x to scaled xxRound.
x := div(xxRound, scalar)
// If n is even:
if mod(n, 2) {
// Compute z * x.
let zx := mul(z, x)
// If z * x overflowed:
if iszero(eq(div(zx, x), z)) {
// Revert if x is non-zero.
if iszero(iszero(x)) {
revert(0, 0)
}
}
// Round to the nearest number.
let zxRound := add(zx, half)
// Revert if zx + half overflowed.
if lt(zxRound, zx) {
revert(0, 0)
}
// Return properly scaled zxRound.
z := div(zxRound, scalar)
}
}
}
}
}
/*//////////////////////////////////////////////////////////////
GENERAL NUMBER UTILITIES
//////////////////////////////////////////////////////////////*/
function sqrt(uint256 x) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
let y := x // We start y at x, which will help us make our initial estimate.
z := 181 // The "correct" value is 1, but this saves a multiplication later.
// This segment is to get a reasonable initial estimate for the Babylonian method. With a bad
// start, the correct # of bits increases ~linearly each iteration instead of ~quadratically.
// We check y >= 2^(k + 8) but shift right by k bits
// each branch to ensure that if x >= 256, then y >= 256.
if iszero(lt(y, 0x10000000000000000000000000000000000)) {
y := shr(128, y)
z := shl(64, z)
}
if iszero(lt(y, 0x1000000000000000000)) {
y := shr(64, y)
z := shl(32, z)
}
if iszero(lt(y, 0x10000000000)) {
y := shr(32, y)
z := shl(16, z)
}
if iszero(lt(y, 0x1000000)) {
y := shr(16, y)
z := shl(8, z)
}
// Goal was to get z*z*y within a small factor of x. More iterations could
// get y in a tighter range. Currently, we will have y in [256, 256*2^16).
// We ensured y >= 256 so that the relative difference between y and y+1 is small.
// That's not possible if x < 256 but we can just verify those cases exhaustively.
// Now, z*z*y <= x < z*z*(y+1), and y <= 2^(16+8), and either y >= 256, or x < 256.
// Correctness can be checked exhaustively for x < 256, so we assume y >= 256.
// Then z*sqrt(y) is within sqrt(257)/sqrt(256) of sqrt(x), or about 20bps.
// For s in the range [1/256, 256], the estimate f(s) = (181/1024) * (s+1) is in the range
// (1/2.84 * sqrt(s), 2.84 * sqrt(s)), with largest error when s = 1 and when s = 256 or 1/256.
// Since y is in [256, 256*2^16), let a = y/65536, so that a is in [1/256, 256). Then we can estimate
// sqrt(y) using sqrt(65536) * 181/1024 * (a + 1) = 181/4 * (y + 65536)/65536 = 181 * (y + 65536)/2^18.
// There is no overflow risk here since y < 2^136 after the first branch above.
z := shr(18, mul(z, add(y, 65536))) // A mul() is saved from starting z at 181.
// Given the worst case multiplicative error of 2.84 above, 7 iterations should be enough.
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
// If x+1 is a perfect square, the Babylonian method cycles between
// floor(sqrt(x)) and ceil(sqrt(x)). This statement ensures we return floor.
// See: https://en.wikipedia.org/wiki/Integer_square_root#Using_only_integer_division
// Since the ceil is rare, we save gas on the assignment and repeat division in the rare case.
// If you don't care whether the floor or ceil square root is returned, you can remove this statement.
z := sub(z, lt(div(x, z), z))
}
}
function unsafeMod(uint256 x, uint256 y) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
// Mod x by y. Note this will return
// 0 instead of reverting if y is zero.
z := mod(x, y)
}
}
function unsafeDiv(uint256 x, uint256 y) internal pure returns (uint256 r) {
/// @solidity memory-safe-assembly
assembly {
// Divide x by y. Note this will return
// 0 instead of reverting if y is zero.
r := div(x, y)
}
}
function unsafeDivUp(uint256 x, uint256 y) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
// Add 1 to x * y if x % y > 0. Note this will
// return 0 instead of reverting if y is zero.
z := add(gt(mod(x, y), 0), div(x, y))
}
}
}
// https://github.com/Uniswap/v3-core/blob/main/contracts/libraries/FullMath.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity 0.8.22;
/// @title Contains 512-bit math functions
/// @notice Facilitates multiplication and division that can have overflow of an intermediate value without any loss of precision
/// @dev Handles "phantom overflow" i.e., allows multiplication and division where an intermediate value overflows 256 bits
library FullMath {
/// @notice Calculates floor(a×b÷denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
/// @param a The multiplicand
/// @param b The multiplier
/// @param denominator The divisor
/// @return result The 256-bit result
/// @dev Credit to Remco Bloemen under MIT license https://xn--2-umb.com/21/muldiv
function mulDiv(uint256 a, uint256 b, uint256 denominator) internal pure returns (uint256 result) {
unchecked {
// 512-bit multiply [prod1 prod0] = a * b
// Compute the product mod 2**256 and mod 2**256 - 1
// then use the Chinese Remainder Theorem to reconstruct
// the 512 bit result. The result is stored in two 256
// variables such that product = prod1 * 2**256 + prod0
uint256 prod0; // Least significant 256 bits of the product
uint256 prod1; // Most significant 256 bits of the product
assembly {
let mm := mulmod(a, b, not(0))
prod0 := mul(a, b)
prod1 := sub(sub(mm, prod0), lt(mm, prod0))
}
// Handle non-overflow cases, 256 by 256 division
if (prod1 == 0) {
require(denominator > 0);
assembly {
result := div(prod0, denominator)
}
return result;
}
// Make sure the result is less than 2**256.
// Also prevents denominator == 0
require(denominator > prod1);
///////////////////////////////////////////////
// 512 by 256 division.
///////////////////////////////////////////////
// Make division exact by subtracting the remainder from [prod1 prod0]
// Compute remainder using mulmod
uint256 remainder;
assembly {
remainder := mulmod(a, b, denominator)
}
// Subtract 256 bit number from 512 bit number
assembly {
prod1 := sub(prod1, gt(remainder, prod0))
prod0 := sub(prod0, remainder)
}
// Factor powers of two out of denominator
// Compute largest power of two divisor of denominator.
// Always >= 1.
uint256 twos = (type(uint256).max - denominator + 1) & denominator;
// Divide denominator by power of two
assembly {
denominator := div(denominator, twos)
}
// Divide [prod1 prod0] by the factors of two
assembly {
prod0 := div(prod0, twos)
}
// Shift in bits from prod1 into prod0. For this we need
// to flip `twos` such that it is 2**256 / twos.
// If twos is zero, then it becomes one
assembly {
twos := add(div(sub(0, twos), twos), 1)
}
prod0 |= prod1 * twos;
// Invert denominator mod 2**256
// Now that denominator is an odd number, it has an inverse
// modulo 2**256 such that denominator * inv = 1 mod 2**256.
// Compute the inverse by starting with a seed that is correct
// correct for four bits. That is, denominator * inv = 1 mod 2**4
uint256 inv = (3 * denominator) ^ 2;
// Now use Newton-Raphson iteration to improve the precision.
// Thanks to Hensel's lifting lemma, this also works in modular
// arithmetic, doubling the correct bits in each step.
inv *= 2 - denominator * inv; // inverse mod 2**8
inv *= 2 - denominator * inv; // inverse mod 2**16
inv *= 2 - denominator * inv; // inverse mod 2**32
inv *= 2 - denominator * inv; // inverse mod 2**64
inv *= 2 - denominator * inv; // inverse mod 2**128
inv *= 2 - denominator * inv; // inverse mod 2**256
// Because the division is now exact we can divide by multiplying
// with the modular inverse of denominator. This will give us the
// correct result modulo 2**256. Since the precoditions guarantee
// that the outcome is less than 2**256, this is the final result.
// We don't need to compute the high bits of the result and prod1
// is no longer required.
result = prod0 * inv;
}
return result;
}
}
/**
* Created by Arcadia Finance
* https://www.arcadia.finance
*
* SPDX-License-Identifier: MIT
*/
pragma solidity 0.8.22;
interface IAeroVoter {
function isGauge(address) external returns (bool);
}
/**
* Created by Pragma Labs
* SPDX-License-Identifier: MIT
*/
pragma solidity 0.8.22;
interface IAssetModule {
/**
* @notice Checks for a token address and the corresponding Id if it is allowed.
* @param asset The contract address of the asset.
* @param assetId The Id of the asset.
* @return A boolean, indicating if the asset is allowed.
* @dev For assets without Id (ERC20, ERC4626...), the Id should be set to 0.
*/
function isAllowed(address asset, uint256 assetId) external view returns (bool);
/**
* @notice Returns the usd value of an asset.
* @param creditor The contract address of the creditor.
* @param asset The contract address of the asset.
* @param assetId The Id of the asset.
* @param assetAmount The amount of assets.
* @return valueInUsd The value of the asset denominated in USD, with 18 Decimals precision.
* @return collateralFactor The collateral factor of the asset for a given creditor, with 4 decimals precision.
* @return liquidationFactor The liquidation factor of the asset for a given creditor, with 4 decimals precision.
*/
function getValue(address creditor, address asset, uint256 assetId, uint256 assetAmount)
external
view
returns (uint256, uint256, uint256);
/**
* @notice Returns the risk factors of an asset for a creditor.
* @param creditor The contract address of the creditor.
* @param asset The contract address of the asset.
* @param assetId The Id of the asset.
* @return collateralFactor The collateral factor of the asset for the creditor, 4 decimals precision.
* @return liquidationFactor The liquidation factor of the asset for the creditor, 4 decimals precision.
*/
function getRiskFactors(address creditor, address asset, uint256 assetId) external view returns (uint16, uint16);
/**
* @notice Increases the exposure to an asset on a direct deposit.
* @param creditor The contract address of the creditor.
* @param asset The contract address of the asset.
* @param id The Id of the asset.
* @param amount The amount of tokens.
* @return recursiveCalls The number of calls done to different asset modules to process the deposit/withdrawal of the asset.
*/
function processDirectDeposit(address creditor, address asset, uint256 id, uint256 amount)
external
returns (uint256);
/**
* @notice Increases the exposure to an asset on an indirect deposit.
* @param creditor The contract address of the creditor.
* @param asset The contract address of the asset.
* @param id The Id of the asset.
* @param exposureUpperAssetToAsset The amount of exposure of the upper asset to the asset of this Asset Module.
* @param deltaExposureUpperAssetToAsset The increase or decrease in exposure of the upper asset to the asset of this Asset Module since last interaction.
* @return recursiveCalls The number of calls done to different asset modules to process the deposit/withdrawal of the asset.
* @return usdExposureUpperAssetToAsset The Usd value of the exposure of the upper asset to the asset of this Asset Module, 18 decimals precision.
*/
function processIndirectDeposit(
address creditor,
address asset,
uint256 id,
uint256 exposureUpperAssetToAsset,
int256 deltaExposureUpperAssetToAsset
) external returns (uint256, uint256);
/**
* @notice Decreases the exposure to an asset on a direct withdrawal.
* @param creditor The contract address of the creditor.
* @param asset The contract address of the asset.
* @param id The Id of the asset.
* @param amount The amount of tokens.
*/
function processDirectWithdrawal(address creditor, address asset, uint256 id, uint256 amount) external;
/**
* @notice Decreases the exposure to an asset on an indirect withdrawal.
* @param creditor The contract address of the creditor.
* @param asset The contract address of the asset.
* @param id The Id of the asset.
* @param exposureUpperAssetToAsset The amount of exposure of the upper asset to the asset of this Asset Module.
* @param deltaExposureUpperAssetToAsset The increase or decrease in exposure of the upper asset to the asset of this Asset Module since last interaction.
* @return usdExposureUpperAssetToAsset The Usd value of the exposure of the upper asset to the asset of this Asset Module, 18 decimals precision.
*/
function processIndirectWithdrawal(
address creditor,
address asset,
uint256 id,
uint256 exposureUpperAssetToAsset,
int256 deltaExposureUpperAssetToAsset
) external returns (uint256);
}
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity 0.8.22;
interface ICLFactory {
function poolImplementation() external view returns (address);
}
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity 0.8.22;
interface ICLGauge {
function deposit(uint256 tokenId) external;
function earned(address account, uint256 tokenId) external view returns (uint256);
function getReward(uint256 tokenId) external;
function pool() external returns (address);
function rewardToken() external returns (address);
function withdraw(uint256 tokenId) external;
}
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity 0.8.22;
interface ICLPool {
function slot0()
external
view
returns (
uint160 sqrtPriceX96,
int24 tick,
uint16 observationIndex,
uint16 observationCardinality,
uint16 observationCardinalityNext,
bool unlocked
);
function ticks(int24 tick)
external
view
returns (
uint128 liquidityGross,
int128 liquidityNet,
int128 stakedLiquidityNet,
uint256 feeGrowthOutside0X128,
uint256 feeGrowthOutside1X128,
uint256 rewardGrowthOutsideX128,
int56 tickCumulativeOutside,
uint160 secondsPerLiquidityOutsideX128,
uint32 secondsOutside,
bool initialized
);
function feeGrowthGlobal0X128() external view returns (uint256 feeGrowthGlobal0X128);
function feeGrowthGlobal1X128() external view returns (uint256 feeGrowthGlobal1X128);
function token0() external returns (address);
function token1() external returns (address);
}
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity 0.8.22;
/// @title Non-fungible token for positions
/// @notice Wraps Slipstream positions in a non-fungible token interface which allows for them to be transferred
/// and authorized.
interface INonfungiblePositionManager {
function approve(address to, uint256 tokenId) external;
function positions(uint256 tokenId)
external
view
returns (
uint96 nonce,
address operator,
address token0,
address token1,
int24 tickSpacing,
int24 tickLower,
int24 tickUpper,
uint128 liquidity,
uint256 feeGrowthInside0LastX128,
uint256 feeGrowthInside1LastX128,
uint128 tokensOwed0,
uint128 tokensOwed1
);
function factory() external returns (address factory);
function safeTransferFrom(address from, address to, uint256 tokenId) external;
function totalSupply() external view returns (uint256 totalSupply);
}
/**
* Created by Pragma Labs
* SPDX-License-Identifier: MIT
*/
pragma solidity 0.8.22;
import { AssetValueAndRiskFactors } from "../../libraries/AssetValuationLib.sol";
interface IRegistry {
/**
* @notice Checks for a token address and the corresponding Id if it is allowed.
* @param asset The contract address of the asset.
* @param assetId The Id of the asset.
* @return A boolean, indicating if the asset is allowed.
*/
function isAllowed(address asset, uint256 assetId) external view returns (bool);
/**
* @notice Adds a new asset to the Registry.
* @param assetType Identifier for the type of the asset.
* @param asset The contract address of the asset.
*/
function addAsset(uint96 assetType, address asset) external;
/**
* @notice Verifies whether a sequence of oracles complies with a predetermined set of criteria.
* @param oracleSequence The sequence of the oracles to price a certain asset in USD,
* packed in a single bytes32 object.
* @return A boolean, indicating if the sequence complies with the set of criteria.
*/
function checkOracleSequence(bytes32 oracleSequence) external view returns (bool);
/**
* @notice Returns the risk factors per asset for a creditor.
* @param creditor The contract address of the creditor.
* @param assetAddresses Array of the contract addresses of the assets.
* @param assetIds Array of the IDs of the assets.
* @return collateralFactors Array of the collateral factors of the assets for the creditor, 4 decimals precision.
* @return liquidationFactors Array of the liquidation factors of the assets for the creditor, 4 decimals precision.
*/
function getRiskFactors(address creditor, address[] calldata assetAddresses, uint256[] calldata assetIds)
external
view
returns (uint16[] memory, uint16[] memory);
/**
* @notice This function is called by pricing modules of non-primary assets in order to update the exposure of an underlying asset after a deposit.
* @param creditor The contract address of the creditor.
* @param underlyingAsset The underlying asset.
* @param underlyingAssetId The underlying asset ID.
* @param exposureAssetToUnderlyingAsset The amount of exposure of the asset to the underlying asset.
* @param deltaExposureAssetToUnderlyingAsset The increase or decrease in exposure of the asset to the underlying asset since the last interaction.
* @return recursiveCalls The number of calls done to different asset modules to process the deposit/withdrawal of the asset.
* @return usdExposureAssetToUnderlyingAsset The Usd value of the exposure of the asset to the underlying asset, 18 decimals precision.
*/
function getUsdValueExposureToUnderlyingAssetAfterDeposit(
address creditor,
address underlyingAsset,
uint256 underlyingAssetId,
uint256 exposureAssetToUnderlyingAsset,
int256 deltaExposureAssetToUnderlyingAsset
) external returns (uint256, uint256);
/**
* @notice This function is called by pricing modules of non-primary assets in order to update the exposure of an underlying asset after a withdrawal.
* @param creditor The contract address of the creditor.
* @param underlyingAsset The underlying asset.
* @param underlyingAssetId The underlying asset ID.
* @param exposureAssetToUnderlyingAsset The amount of exposure of the asset to the underlying asset.
* @param deltaExposureAssetToUnderlyingAsset The increase or decrease in exposure of the asset to the underlying asset since the last interaction.
* @return usdExposureAssetToUnderlyingAsset The Usd value of the exposure of the asset to the underlying asset, 18 decimals precision.
*/
function getUsdValueExposureToUnderlyingAssetAfterWithdrawal(
address creditor,
address underlyingAsset,
uint256 underlyingAssetId,
uint256 exposureAssetToUnderlyingAsset,
int256 deltaExposureAssetToUnderlyingAsset
) external returns (uint256 usdExposureAssetToUnderlyingAsset);
/**
* @notice Returns the rate of a certain asset in USD.
* @param oracleSequence The sequence of the oracles to price a certain asset in USD,
* packed in a single bytes32 object.
* @return rate The USD rate of an asset with 18 decimals precision.
*/
function getRateInUsd(bytes32 oracleSequence) external view returns (uint256);
/**
* @notice Calculates the USD values of underlying assets.
* @param creditor The contract address of the creditor.
* @param assets Array of the contract addresses of the assets.
* @param assetIds Array of the IDs of the assets.
* @param assetAmounts Array with the amounts of the assets.
* @return valuesAndRiskFactors The value of the asset denominated in USD, with 18 Decimals precision.
*/
function getValuesInUsdRecursive(
address creditor,
address[] calldata assets,
uint256[] calldata assetIds,
uint256[] calldata assetAmounts
) external view returns (AssetValueAndRiskFactors[] memory valuesAndRiskFactors);
}
// https://github.com/Uniswap/v3-periphery/blob/main/contracts/libraries/LiquidityAmounts.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity 0.8.22;
import { FullMath } from "./FullMath.sol";
import { FixedPoint96 } from "./FixedPoint96.sol";
/// @title Liquidity amount functions
/// @notice Provides functions for computing liquidity amounts from token amounts and prices
library LiquidityAmounts {
/// @notice Computes the amount of token0 for a given amount of liquidity and a price range
/// @param sqrtRatioAX96 A sqrt price representing the first tick boundary
/// @param sqrtRatioBX96 A sqrt price representing the second tick boundary
/// @param liquidity The liquidity being valued
/// @return amount0 The amount of token0
function getAmount0ForLiquidity(uint160 sqrtRatioAX96, uint160 sqrtRatioBX96, uint128 liquidity)
internal
pure
returns (uint256 amount0)
{
unchecked {
if (sqrtRatioAX96 > sqrtRatioBX96) (sqrtRatioAX96, sqrtRatioBX96) = (sqrtRatioBX96, sqrtRatioAX96);
return FullMath.mulDiv(
uint256(liquidity) << FixedPoint96.RESOLUTION, sqrtRatioBX96 - sqrtRatioAX96, sqrtRatioBX96
) / sqrtRatioAX96;
}
}
/// @notice Computes the amount of token1 for a given amount of liquidity and a price range
/// @param sqrtRatioAX96 A sqrt price representing the first tick boundary
/// @param sqrtRatioBX96 A sqrt price representing the second tick boundary
/// @param liquidity The liquidity being valued
/// @return amount1 The amount of token1
function getAmount1ForLiquidity(uint160 sqrtRatioAX96, uint160 sqrtRatioBX96, uint128 liquidity)
internal
pure
returns (uint256 amount1)
{
unchecked {
if (sqrtRatioAX96 > sqrtRatioBX96) (sqrtRatioAX96, sqrtRatioBX96) = (sqrtRatioBX96, sqrtRatioAX96);
return FullMath.mulDiv(liquidity, sqrtRatioBX96 - sqrtRatioAX96, FixedPoint96.Q96);
}
}
/// @notice Computes the token0 and token1 value for a given amount of liquidity, the current
/// pool prices and the prices at the tick boundaries
/// @param sqrtRatioX96 A sqrt price representing the current pool prices
/// @param sqrtRatioAX96 A sqrt price representing the first tick boundary
/// @param sqrtRatioBX96 A sqrt price representing the second tick boundary
/// @param liquidity The liquidity being valued
/// @return amount0 The amount of token0
/// @return amount1 The amount of token1
function getAmountsForLiquidity(
uint160 sqrtRatioX96,
uint160 sqrtRatioAX96,
uint160 sqrtRatioBX96,
uint128 liquidity
) internal pure returns (uint256 amount0, uint256 amount1) {
if (sqrtRatioAX96 > sqrtRatioBX96) (sqrtRatioAX96, sqrtRatioBX96) = (sqrtRatioBX96, sqrtRatioAX96);
if (sqrtRatioX96 <= sqrtRatioAX96) {
amount0 = getAmount0ForLiquidity(sqrtRatioAX96, sqrtRatioBX96, liquidity);
} else if (sqrtRatioX96 < sqrtRatioBX96) {
amount0 = getAmount0ForLiquidity(sqrtRatioX96, sqrtRatioBX96, liquidity);
amount1 = getAmount1ForLiquidity(sqrtRatioAX96, sqrtRatioX96, liquidity);
} else {
amount1 = getAmount1ForLiquidity(sqrtRatioAX96, sqrtRatioBX96, liquidity);
}
}
}
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;
/// @notice Simple single owner authorization mixin.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/auth/Owned.sol)
abstract contract Owned {
/*//////////////////////////////////////////////////////////////
EVENTS
//////////////////////////////////////////////////////////////*/
event OwnershipTransferred(address indexed user, address indexed newOwner);
/*//////////////////////////////////////////////////////////////
OWNERSHIP STORAGE
//////////////////////////////////////////////////////////////*/
address public owner;
modifier onlyOwner() virtual {
require(msg.sender == owner, "UNAUTHORIZED");
_;
}
/*//////////////////////////////////////////////////////////////
CONSTRUCTOR
//////////////////////////////////////////////////////////////*/
constructor(address _owner) {
owner = _owner;
emit OwnershipTransferred(address(0), _owner);
}
/*//////////////////////////////////////////////////////////////
OWNERSHIP LOGIC
//////////////////////////////////////////////////////////////*/
function transferOwnership(address newOwner) public virtual onlyOwner {
owner = newOwner;
emit OwnershipTransferred(msg.sender, newOwner);
}
}
// https://github.com/velodrome-finance/slipstream/blob/main/contracts/periphery/libraries/PoolAddress.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity 0.8.22;
import { ICLFactory } from "../interfaces/ICLFactory.sol";
/// @title Provides functions for deriving a pool address from the factory, tokens, and the tickSpacing
library PoolAddress {
/// @notice The identifying key of the pool
struct PoolKey {
address token0;
address token1;
int24 tickSpacing;
}
/// @notice Deterministically computes the pool address given the factory and PoolKey
/// @param factory The Slipstream factory contract address
/// @param token0 Contract address of token0.
/// @param token1 Contract address of token1.
/// @param tickSpacing The tick spacing of the pool
/// @return pool The contract address of the pool
function computeAddress(address factory, address token0, address token1, int24 tickSpacing)
internal
view
returns (address pool)
{
require(token0 < token1);
pool = predictDeterministicAddress({
master: ICLFactory(factory).poolImplementation(),
salt: keccak256(abi.encode(token0, token1, tickSpacing)),
deployer: factory
});
}
/**
* @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}.
*/
function predictDeterministicAddress(address master, bytes32 salt, address deployer)
internal
pure
returns (address predicted)
{
// solhint-disable-next-line no-inline-assembly
assembly {
let ptr := mload(0x40)
mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000)
mstore(add(ptr, 0x14), shl(0x60, master))
mstore(add(ptr, 0x28), 0x5af43d82803e903d91602b57fd5bf3ff00000000000000000000000000000000)
mstore(add(ptr, 0x38), shl(0x60, deployer))
mstore(add(ptr, 0x4c), salt)
mstore(add(ptr, 0x6c), keccak256(ptr, 0x37))
predicted := keccak256(add(ptr, 0x37), 0x55)
}
}
}
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;
/// @notice Gas optimized reentrancy protection for smart contracts.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/ReentrancyGuard.sol)
/// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/security/ReentrancyGuard.sol)
abstract contract ReentrancyGuard {
uint256 private locked = 1;
modifier nonReentrant() virtual {
require(locked == 1, "REENTRANCY");
locked = 2;
_;
locked = 1;
}
}
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;
/// @notice Safe unsigned integer casting library that reverts on overflow.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SafeCastLib.sol)
/// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/math/SafeCast.sol)
library SafeCastLib {
function safeCastTo248(uint256 x) internal pure returns (uint248 y) {
require(x < 1 << 248);
y = uint248(x);
}
function safeCastTo240(uint256 x) internal pure returns (uint240 y) {
require(x < 1 << 240);
y = uint240(x);
}
function safeCastTo232(uint256 x) internal pure returns (uint232 y) {
require(x < 1 << 232);
y = uint232(x);
}
function safeCastTo224(uint256 x) internal pure returns (uint224 y) {
require(x < 1 << 224);
y = uint224(x);
}
function safeCastTo216(uint256 x) internal pure returns (uint216 y) {
require(x < 1 << 216);
y = uint216(x);
}
function safeCastTo208(uint256 x) internal pure returns (uint208 y) {
require(x < 1 << 208);
y = uint208(x);
}
function safeCastTo200(uint256 x) internal pure returns (uint200 y) {
require(x < 1 << 200);
y = uint200(x);
}
function safeCastTo192(uint256 x) internal pure returns (uint192 y) {
require(x < 1 << 192);
y = uint192(x);
}
function safeCastTo184(uint256 x) internal pure returns (uint184 y) {
require(x < 1 << 184);
y = uint184(x);
}
function safeCastTo176(uint256 x) internal pure returns (uint176 y) {
require(x < 1 << 176);
y = uint176(x);
}
function safeCastTo168(uint256 x) internal pure returns (uint168 y) {
require(x < 1 << 168);
y = uint168(x);
}
function safeCastTo160(uint256 x) internal pure returns (uint160 y) {
require(x < 1 << 160);
y = uint160(x);
}
function safeCastTo152(uint256 x) internal pure returns (uint152 y) {
require(x < 1 << 152);
y = uint152(x);
}
function safeCastTo144(uint256 x) internal pure returns (uint144 y) {
require(x < 1 << 144);
y = uint144(x);
}
function safeCastTo136(uint256 x) internal pure returns (uint136 y) {
require(x < 1 << 136);
y = uint136(x);
}
function safeCastTo128(uint256 x) internal pure returns (uint128 y) {
require(x < 1 << 128);
y = uint128(x);
}
function safeCastTo120(uint256 x) internal pure returns (uint120 y) {
require(x < 1 << 120);
y = uint120(x);
}
function safeCastTo112(uint256 x) internal pure returns (uint112 y) {
require(x < 1 << 112);
y = uint112(x);
}
function safeCastTo104(uint256 x) internal pure returns (uint104 y) {
require(x < 1 << 104);
y = uint104(x);
}
function safeCastTo96(uint256 x) internal pure returns (uint96 y) {
require(x < 1 << 96);
y = uint96(x);
}
function safeCastTo88(uint256 x) internal pure returns (uint88 y) {
require(x < 1 << 88);
y = uint88(x);
}
function safeCastTo80(uint256 x) internal pure returns (uint80 y) {
require(x < 1 << 80);
y = uint80(x);
}
function safeCastTo72(uint256 x) internal pure returns (uint72 y) {
require(x < 1 << 72);
y = uint72(x);
}
function safeCastTo64(uint256 x) internal pure returns (uint64 y) {
require(x < 1 << 64);
y = uint64(x);
}
function safeCastTo56(uint256 x) internal pure returns (uint56 y) {
require(x < 1 << 56);
y = uint56(x);
}
function safeCastTo48(uint256 x) internal pure returns (uint48 y) {
require(x < 1 << 48);
y = uint48(x);
}
function safeCastTo40(uint256 x) internal pure returns (uint40 y) {
require(x < 1 << 40);
y = uint40(x);
}
function safeCastTo32(uint256 x) internal pure returns (uint32 y) {
require(x < 1 << 32);
y = uint32(x);
}
function safeCastTo24(uint256 x) internal pure returns (uint24 y) {
require(x < 1 << 24);
y = uint24(x);
}
function safeCastTo16(uint256 x) internal pure returns (uint16 y) {
require(x < 1 << 16);
y = uint16(x);
}
function safeCastTo8(uint256 x) internal pure returns (uint8 y) {
require(x < 1 << 8);
y = uint8(x);
}
}
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;
import {ERC20} from "../tokens/ERC20.sol";
/// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SafeTransferLib.sol)
/// @dev Use with caution! Some functions in this library knowingly create dirty bits at the destination of the free memory pointer.
/// @dev Note that none of the functions in this library check that a token has code at all! That responsibility is delegated to the caller.
library SafeTransferLib {
/*//////////////////////////////////////////////////////////////
ETH OPERATIONS
//////////////////////////////////////////////////////////////*/
function safeTransferETH(address to, uint256 amount) internal {
bool success;
/// @solidity memory-safe-assembly
assembly {
// Transfer the ETH and store if it succeeded or not.
success := call(gas(), to, amount, 0, 0, 0, 0)
}
require(success, "ETH_TRANSFER_FAILED");
}
/*//////////////////////////////////////////////////////////////
ERC20 OPERATIONS
//////////////////////////////////////////////////////////////*/
function safeTransferFrom(
ERC20 token,
address from,
address to,
uint256 amount
) internal {
bool success;
/// @solidity memory-safe-assembly
assembly {
// Get a pointer to some free memory.
let freeMemoryPointer := mload(0x40)
// Write the abi-encoded calldata into memory, beginning with the function selector.
mstore(freeMemoryPointer, 0x23b872dd00000000000000000000000000000000000000000000000000000000)
mstore(add(freeMemoryPointer, 4), and(from, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "from" argument.
mstore(add(freeMemoryPointer, 36), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to" argument.
mstore(add(freeMemoryPointer, 68), amount) // Append the "amount" argument. Masking not required as it's a full 32 byte type.
success := and(
// Set success to whether the call reverted, if not we check it either
// returned exactly 1 (can't just be non-zero data), or had no return data.
or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
// We use 100 because the length of our calldata totals up like so: 4 + 32 * 3.
// We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
// Counterintuitively, this call must be positioned second to the or() call in the
// surrounding and() call or else returndatasize() will be zero during the computation.
call(gas(), token, 0, freeMemoryPointer, 100, 0, 32)
)
}
require(success, "TRANSFER_FROM_FAILED");
}
function safeTransfer(
ERC20 token,
address to,
uint256 amount
) internal {
bool success;
/// @solidity memory-safe-assembly
assembly {
// Get a pointer to some free memory.
let freeMemoryPointer := mload(0x40)
// Write the abi-encoded calldata into memory, beginning with the function selector.
mstore(freeMemoryPointer, 0xa9059cbb00000000000000000000000000000000000000000000000000000000)
mstore(add(freeMemoryPointer, 4), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to" argument.
mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument. Masking not required as it's a full 32 byte type.
success := and(
// Set success to whether the call reverted, if not we check it either
// returned exactly 1 (can't just be non-zero data), or had no return data.
or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
// We use 68 because the length of our calldata totals up like so: 4 + 32 * 2.
// We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
// Counterintuitively, this call must be positioned second to the or() call in the
// surrounding and() call or else returndatasize() will be zero during the computation.
call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)
)
}
require(success, "TRANSFER_FAILED");
}
function safeApprove(
ERC20 token,
address to,
uint256 amount
) internal {
bool success;
/// @solidity memory-safe-assembly
assembly {
// Get a pointer to some free memory.
let freeMemoryPointer := mload(0x40)
// Write the abi-encoded calldata into memory, beginning with the function selector.
mstore(freeMemoryPointer, 0x095ea7b300000000000000000000000000000000000000000000000000000000)
mstore(add(freeMemoryPointer, 4), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to" argument.
mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument. Masking not required as it's a full 32 byte type.
success := and(
// Set success to whether the call reverted, if not we check it either
// returned exactly 1 (can't just be non-zero data), or had no return data.
or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
// We use 68 because the length of our calldata totals up like so: 4 + 32 * 2.
// We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
// Counterintuitively, this call must be positioned second to the or() call in the
// surrounding and() call or else returndatasize() will be zero during the computation.
call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)
)
}
require(success, "APPROVE_FAILED");
}
}
/**
* Created by Pragma Labs
* SPDX-License-Identifier: BUSL-1.1
*/
pragma solidity 0.8.22;
import { AssetValuationLib, AssetValueAndRiskFactors } from "../../libraries/AssetValuationLib.sol";
import { DerivedAM, FixedPointMathLib, IRegistry } from "../abstracts/AbstractDerivedAM.sol";
import { ERC20 } from "../../../lib/solmate/src/tokens/ERC20.sol";
import { ERC721 } from "../../../lib/solmate/src/tokens/ERC721.sol";
import { FixedPoint96 } from "../UniswapV3/libraries/FixedPoint96.sol";
import { IAeroVoter } from "../Aerodrome-Finance/interfaces/IAeroVoter.sol";
import { ICLGauge } from "./interfaces/ICLGauge.sol";
import { ICLPool } from "./interfaces/ICLPool.sol";
import { INonfungiblePositionManager } from "./interfaces/INonfungiblePositionManager.sol";
import { LiquidityAmounts } from "../UniswapV3/libraries/LiquidityAmounts.sol";
import { PoolAddress } from "./libraries/PoolAddress.sol";
import { ReentrancyGuard } from "../../../lib/solmate/src/utils/ReentrancyGuard.sol";
import { SafeTransferLib } from "../../../lib/solmate/src/utils/SafeTransferLib.sol";
import { Strings } from "../../libraries/Strings.sol";
import { TickMath } from "../UniswapV3/libraries/TickMath.sol";
/**
* @title Asset Module for Staked Slipstream Liquidity Positions
* @author Pragma Labs
* @notice The pricing logic and basic information for Staked Slipstream Liquidity Positions.
* @dev The StakedSlipstreamAM will not price the underlying LP tokens via direct price oracles,
* it will break down liquidity positions in the underlying tokens (ERC20s).
* Only LP tokens for which the underlying tokens are allowed as collateral can be priced.
*/
contract StakedSlipstreamAM is DerivedAM, ERC721, ReentrancyGuard {
using FixedPointMathLib for uint256;
using Strings for uint256;
using SafeTransferLib for ERC20;
/* //////////////////////////////////////////////////////////////
CONSTANTS
////////////////////////////////////////////////////////////// */
// The contract address of the Slipstream Factory.
address internal immutable CL_FACTORY;
// The Reward Token.
ERC20 public immutable REWARD_TOKEN;
// The Aerodrome voter contract.
IAeroVoter internal immutable AERO_VOTER;
// The contract address of the NonfungiblePositionManager.
INonfungiblePositionManager internal immutable NON_FUNGIBLE_POSITION_MANAGER;
/* //////////////////////////////////////////////////////////////
STORAGE
////////////////////////////////////////////////////////////// */
// The baseURI of the ERC721 tokens.
string public baseURI;
// The unique identifiers of the Underlying Assets of a Liquidity Position.
mapping(bytes32 assetKey => bytes32[] underlyingAssetKeys) public assetToUnderlyingAssets;
// The allowed Gauges.
mapping(address pool => address gauge) public poolToGauge;
// Map a position id to its corresponding struct with the position state.
mapping(uint256 position => PositionState) public positionState;
// Struct with the Position specific state.
struct PositionState {
// The lower tick of the Liquidity Position.
int24 tickLower;
// The upper tick of the Liquidity Position.
int24 tickUpper;
// The liquidity of the Liquidity Position when it was deposited.
uint128 liquidity;
// The Slipstream Gauge.
address gauge;
}
/* //////////////////////////////////////////////////////////////
EVENTS
////////////////////////////////////////////////////////////// */
event RewardPaid(uint256 indexed positionId, address indexed reward, uint128 amount);
/* //////////////////////////////////////////////////////////////
ERRORS
////////////////////////////////////////////////////////////// */
error AssetNotAllowed();
error GaugeNotValid();
error InvalidId();
error NotOwner();
error RewardTokenNotAllowed();
error RewardTokenNotValid();
error ZeroLiquidity();
/* //////////////////////////////////////////////////////////////
CONSTRUCTOR
////////////////////////////////////////////////////////////// */
/**
* @param registry The contract address of the Arcadia Registry.
* @param nonFungiblePositionManager The contract address of the protocols NonFungiblePositionManager.
* @param aerodromeVoter The contract address of the Aerodrome Finance Voter contract.
* @param rewardToken The contract address of the Reward Token.
* @dev The ASSET_TYPE, necessary for the deposit and withdraw logic in the Accounts, is "2" for Slipstream Liquidity Positions (ERC721).
*/
constructor(address registry, address nonFungiblePositionManager, address aerodromeVoter, address rewardToken)
DerivedAM(registry, 2)
ERC721("Arcadia Staked Slipstream Positions", "aSSLIPP")
{
if (!IRegistry(registry).isAllowed(rewardToken, 0)) revert RewardTokenNotAllowed();
AERO_VOTER = IAeroVoter(aerodromeVoter);
REWARD_TOKEN = ERC20(rewardToken);
NON_FUNGIBLE_POSITION_MANAGER = INonfungiblePositionManager(nonFungiblePositionManager);
CL_FACTORY = NON_FUNGIBLE_POSITION_MANAGER.factory();
}
/* //////////////////////////////////////////////////////////////
INITIALIZE
////////////////////////////////////////////////////////////// */
/**
* @notice This function will add this contract as an asset in the Registry.
* @dev Will revert if called more than once.
*/
function initialize() external onlyOwner {
inAssetModule[address(this)] = true;
IRegistry(REGISTRY).addAsset(uint96(ASSET_TYPE), address(this));
}
/*///////////////////////////////////////////////////////////////
ASSET MANAGEMENT
///////////////////////////////////////////////////////////////*/
/**
* @notice Adds a new Gauge to the StakedSlipstreamAM.
* @param gauge The contract address of the gauge to stake Slipstream LP.
* @dev Killed Gauges can be added, but no positions can be minted.
*/
function addGauge(address gauge) external onlyOwner {
if (AERO_VOTER.isGauge(gauge) != true) revert GaugeNotValid();
if (ICLGauge(gauge).rewardToken() != address(REWARD_TOKEN)) revert RewardTokenNotValid();
address pool = ICLGauge(gauge).pool();
if (!IRegistry(REGISTRY).isAllowed(ICLPool(pool).token0(), 0)) revert AssetNotAllowed();
if (!IRegistry(REGISTRY).isAllowed(ICLPool(pool).token1(), 0)) revert AssetNotAllowed();
poolToGauge[pool] = gauge;
}
/*///////////////////////////////////////////////////////////////
ASSET INFORMATION
///////////////////////////////////////////////////////////////*/
/**
* @notice Checks for a token address and the corresponding id if it is allowed.
* @param asset The contract address of the asset.
* @param assetId The id of the asset.
* @return A boolean, indicating if the asset is allowed.
*/
function isAllowed(address asset, uint256 assetId) public view override returns (bool) {
if (asset == address(this) && _ownerOf[assetId] != address(0)) return true;
else return false;
}
/**
* @notice Returns the unique identifiers of the Underlying Assets.
* @param assetKey The unique identifier of the asset.
* @return underlyingAssetKeys The unique identifiers of the Underlying Assets.
*/
function _getUnderlyingAssets(bytes32 assetKey)
internal
view
override
returns (bytes32[] memory underlyingAssetKeys)
{
underlyingAssetKeys = assetToUnderlyingAssets[assetKey];
}
/**
* @notice Calculates for a given asset the corresponding amount(s) of Underlying Asset(s).
* @param creditor The contract address of the creditor.
* @param assetKey The unique identifier of the asset.
* @param amount The amount of the asset, in the decimal precision of the Asset.
* @param underlyingAssetKeys The unique identifiers of the Underlying Assets.
* @return underlyingAssetsAmounts The corresponding amount(s) of Underlying Asset(s), in the decimal precision of the Underlying Asset.
* @return rateUnderlyingAssetsToUsd The usd rates of 1e18 tokens of Underlying Asset, with 18 decimals precision.
* @dev External price feeds of the Underlying Liquidity Position are used to calculate the flashloan resistant amounts.
* This approach accommodates scenarios where an underlying asset could be
* a derived asset itself (e.g., USDC/aUSDC pool), ensuring more versatile and accurate price calculations.
*/
function _getUnderlyingAssetsAmounts(
address creditor,
bytes32 assetKey,
uint256 amount,
bytes32[] memory underlyingAssetKeys
)
internal
view
override
returns (uint256[] memory underlyingAssetsAmounts, AssetValueAndRiskFactors[] memory rateUnderlyingAssetsToUsd)
{
// Amount of a Staked Slipstream LP can only be either 0 or 1.
if (amount == 0) {
return (new uint256[](3), rateUnderlyingAssetsToUsd);
}
// Get the trusted rates to USD of the Underlying Assets.
rateUnderlyingAssetsToUsd = _getRateUnderlyingAssetsToUsd(creditor, underlyingAssetKeys);
// Calculate amount0 and amount1 of the principal (the actual liquidity position).
// The liquidity of the Liquidity Position is stored in the Asset Module,
// not fetched from the NonfungiblePositionManager.
// Since liquidity of a position can be increased by a non-owner,
// the max exposure checks for the principal of the position could otherwise be circumvented.
(, uint256 assetId) = _getAssetFromKey(assetKey);
underlyingAssetsAmounts = new uint256[](3);
(underlyingAssetsAmounts[0], underlyingAssetsAmounts[1]) = _getPrincipalAmounts(
positionState[assetId].tickLower,
positionState[assetId].tickUpper,
positionState[assetId].liquidity,
rateUnderlyingAssetsToUsd[0].assetValue,
rateUnderlyingAssetsToUsd[1].assetValue
);
// Get the staking rewards.
underlyingAssetsAmounts[2] = rewardOf(assetId);
}
/**
* @notice Calculates the underlying token amounts of a liquidity position, given external trusted prices.
* @param tickLower The lower tick of the liquidity position.
* @param tickUpper The upper tick of the liquidity position.
* @param priceToken0 The price of 1e18 tokens of token0 in USD, with 18 decimals precision.
* @param priceToken1 The price of 1e18 tokens of token1 in USD, with 18 decimals precision.
* @return amount0 The amount of underlying token0 tokens.
* @return amount1 The amount of underlying token1 tokens.
*/
function _getPrincipalAmounts(
int24 tickLower,
int24 tickUpper,
uint128 liquidity,
uint256 priceToken0,
uint256 priceToken1
) internal pure returns (uint256 amount0, uint256 amount1) {
// Calculate the square root of the relative rate sqrt(token1/token0) from the trusted USD price of both tokens.
// sqrtPriceX96 is a binary fixed point number with 96 digits precision.
uint160 sqrtPriceX96 = _getSqrtPriceX96(priceToken0, priceToken1);
// Calculate amount0 and amount1 of the principal (the liquidity position without accumulated fees).
(amount0, amount1) = LiquidityAmounts.getAmountsForLiquidity(
sqrtPriceX96, TickMath.getSqrtRatioAtTick(tickLower), TickMath.getSqrtRatioAtTick(tickUpper), liquidity
);
}
/**
* @notice Calculates the sqrtPriceX96 (token1/token0) from trusted USD prices of both tokens.
* @param priceToken0 The price of 1e18 tokens of token0 in USD, with 18 decimals precision.
* @param priceToken1 The price of 1e18 tokens of token1 in USD, with 18 decimals precision.
* @return sqrtPriceX96 The square root of the price (token1/token0), with 96 binary precision.
* @dev The price in Slipstream is defined as:
* price = amountToken1/amountToken0.
* The usdPriceToken is defined as: usdPriceToken = amountUsd/amountToken.
* => amountToken = amountUsd/usdPriceToken.
* Hence we can derive the Slipstream price as:
* price = (amountUsd/usdPriceToken1)/(amountUsd/usdPriceToken0) = usdPriceToken0/usdPriceToken1.
*/
function _getSqrtPriceX96(uint256 priceToken0, uint256 priceToken1) internal pure returns (uint160 sqrtPriceX96) {
if (priceToken1 == 0) return TickMath.MAX_SQRT_RATIO;
// Both priceTokens have 18 decimals precision and result of division should have 28 decimals precision.
// -> multiply by 1e28
// priceXd28 will overflow if priceToken0 is greater than 1.158e+49.
// For WBTC (which only has 8 decimals) this would require a bitcoin price greater than 115 792 089 237 316 198 989 824 USD/BTC.
uint256 priceXd28 = priceToken0.mulDivDown(1e28, priceToken1);
// Square root of a number with 28 decimals precision has 14 decimals precision.
uint256 sqrtPriceXd14 = FixedPointMathLib.sqrt(priceXd28);
// Change sqrtPrice from a decimal fixed point number with 14 digits to a binary fixed point number with 96 digits.
// Unsafe cast: Cast will only overflow when priceToken0/priceToken1 >= 2^128.
sqrtPriceX96 = uint160((sqrtPriceXd14 << FixedPoint96.RESOLUTION) / 1e14);
}
/*///////////////////////////////////////////////////////////////
RISK VARIABLES MANAGEMENT
///////////////////////////////////////////////////////////////*/
/**
* @notice Returns the risk factors of an asset for a Creditor.
* @param creditor The contract address of the Creditor.
* @param asset The contract address of the asset.
* @param assetId The id of the asset.
* @return collateralFactor The collateral factor of the asset for the Creditor, 4 decimals precision.
* @return liquidationFactor The liquidation factor of the asset for the Creditor, 4 decimals precision.
*/
function getRiskFactors(address creditor, address asset, uint256 assetId)
external
view
override
returns (uint16 collateralFactor, uint16 liquidationFactor)
{
bytes32 assetKey = _getKeyFromAsset(asset, assetId);
bytes32[] memory underlyingAssetKeys = _getUnderlyingAssets(assetKey);
(uint256[] memory underlyingAssetsAmounts,) =
_getUnderlyingAssetsAmounts(creditor, assetKey, 1, underlyingAssetKeys);
AssetValueAndRiskFactors[] memory rateUnderlyingAssetsToUsd =
_getRateUnderlyingAssetsToUsd(creditor, underlyingAssetKeys);
(, uint256 collateralFactor_, uint256 liquidationFactor_) =
_calculateValueAndRiskFactors(creditor, underlyingAssetsAmounts, rateUnderlyingAssetsToUsd);
// Unsafe cast: collateralFactor_ and liquidationFactor_ are smaller than or equal to 1e4.
return (uint16(collateralFactor_), uint16(liquidationFactor_));
}
/*///////////////////////////////////////////////////////////////
PRICING LOGIC
///////////////////////////////////////////////////////////////*/
/**
* @notice Returns the USD value of an asset.
* @param creditor The contract address of the Creditor.
* @param underlyingAssetsAmounts The corresponding amount(s) of Underlying Asset(s), in the decimal precision of the Underlying Asset.
* @param rateUnderlyingAssetsToUsd The USD rates of 10**18 tokens of underlying asset, with 18 decimals precision.
* @return valueInUsd The value of the asset denominated in USD, with 18 Decimals precision.
* @return collateralFactor The collateral factor of the asset for a given Creditor, with 4 decimals precision.
* @return liquidationFactor The liquidation factor of the asset for a given Creditor, with 4 decimals precision.
* @dev We take the most conservative (lowest) risk factor of the principal assets of the Liquidity Position.
* Next we take a USD-value weighted average of the risk factors of the principal and staking rewards.
*/
function _calculateValueAndRiskFactors(
address creditor,
uint256[] memory underlyingAssetsAmounts,
AssetValueAndRiskFactors[] memory rateUnderlyingAssetsToUsd
) internal view override returns (uint256 valueInUsd, uint256 collateralFactor, uint256 liquidationFactor) {
// "rateUnderlyingAssetsToUsd" is the USD value with 18 decimals precision for 10**18 tokens of Underlying Asset.
// To get the USD value (also with 18 decimals) of the actual amount of underlying assets, we have to multiply
// the actual amount with the rate for 10**18 tokens, and divide by 10**18.
uint256 valuePrincipal = underlyingAssetsAmounts[0].mulDivDown(rateUnderlyingAssetsToUsd[0].assetValue, 1e18)
+ underlyingAssetsAmounts[1].mulDivDown(rateUnderlyingAssetsToUsd[1].assetValue, 1e18);
uint256 valueReward = underlyingAssetsAmounts[2].mulDivDown(rateUnderlyingAssetsToUsd[2].assetValue, 1e18);
valueInUsd = valuePrincipal + valueReward;
if (valueInUsd == 0) return (0, 0, 0);
// Keep the lowest risk factor of the principal assets.
collateralFactor = rateUnderlyingAssetsToUsd[0].collateralFactor < rateUnderlyingAssetsToUsd[1].collateralFactor
? rateUnderlyingAssetsToUsd[0].collateralFactor
: rateUnderlyingAssetsToUsd[1].collateralFactor;
liquidationFactor = rateUnderlyingAssetsToUsd[0].liquidationFactor
< rateUnderlyingAssetsToUsd[1].liquidationFactor
? rateUnderlyingAssetsToUsd[0].liquidationFactor
: rateUnderlyingAssetsToUsd[1].liquidationFactor;
// Calculate weighted risk factors of principal and reward.
unchecked {
collateralFactor = (
valuePrincipal * collateralFactor + valueReward * rateUnderlyingAssetsToUsd[2].collateralFactor
) / valueInUsd;
liquidationFactor = (
valuePrincipal * liquidationFactor + valueReward * rateUnderlyingAssetsToUsd[2].liquidationFactor
) / valueInUsd;
}
// Lower risk factors with the protocol wide risk factor.
uint256 riskFactor = riskParams[creditor].riskFactor;
collateralFactor = riskFactor.mulDivDown(collateralFactor, AssetValuationLib.ONE_4);
liquidationFactor = riskFactor.mulDivDown(liquidationFactor, AssetValuationLib.ONE_4);
}
/*///////////////////////////////////////////////////////////////
STAKING MODULE LOGIC
///////////////////////////////////////////////////////////////*/
/**
* @notice Stakes a Slipstream Liquidity Position in its Gauge and mints a new position.
* @param assetId The id of the Liquidity Position.
* @return positionId The id of the Minted Position.
* @dev the Minted Position has the same id as the Liquidity Position.
*/
function mint(uint256 assetId) external nonReentrant returns (uint256 positionId) {
if (assetId > type(uint96).max) revert InvalidId();
NON_FUNGIBLE_POSITION_MANAGER.safeTransferFrom(msg.sender, address(this), assetId);
// Get position.
(,, address token0, address token1, int24 tickSpacing, int24 tickLower, int24 tickUpper, uint128 liquidity,,,,)
= NON_FUNGIBLE_POSITION_MANAGER.positions(assetId);
if (liquidity == 0) revert ZeroLiquidity();
// Get the Gauge.
// If the gauge is not approved, poolToGauge will return the 0 address and deposit will revert.
address gauge = poolToGauge[PoolAddress.computeAddress(CL_FACTORY, token0, token1, tickSpacing)];
// Store the position state.
positionState[assetId] =
PositionState({ tickLower: tickLower, tickUpper: tickUpper, liquidity: liquidity, gauge: gauge });
// Store underlying assets.
bytes32[] memory underlyingAssetKeys = new bytes32[](3);
underlyingAssetKeys[0] = _getKeyFromAsset(token0, 0);
underlyingAssetKeys[1] = _getKeyFromAsset(token1, 0);
underlyingAssetKeys[2] = _getKeyFromAsset(address(REWARD_TOKEN), 0);
assetToUnderlyingAssets[_getKeyFromAsset(address(this), assetId)] = underlyingAssetKeys;
// Stake the Liquidity Position.
NON_FUNGIBLE_POSITION_MANAGER.approve(gauge, assetId);
ICLGauge(gauge).deposit(assetId);
// If the Liquidity Position already collected fees,
// these were claimed during the deposit and send to this contract.
uint256 balance0 = ERC20(token0).balanceOf(address(this));
uint256 balance1 = ERC20(token1).balanceOf(address(this));
if (balance0 > 0) ERC20(token0).safeTransfer(msg.sender, balance0);
if (balance1 > 0) ERC20(token1).safeTransfer(msg.sender, balance1);
// Mint the new position, with same id as the underlying position.
positionId = assetId;
_safeMint(msg.sender, positionId);
}
/**
* @notice Unstakes a staked Slipstream Liquidity Position and claims rewards.
* @param positionId The id of the position.
* @return rewards The amount of reward tokens claimed.
*/
function burn(uint256 positionId) external nonReentrant returns (uint256 rewards) {
if (_ownerOf[positionId] != msg.sender) revert NotOwner();
// Unstake the Liquidity Position.
ICLGauge(positionState[positionId].gauge).withdraw(positionId);
rewards = REWARD_TOKEN.balanceOf(address(this));
// Burn the position.
delete positionState[positionId];
_burn(positionId);
// Pay out the rewards to the position owner.
if (rewards > 0) {
// Transfer reward
REWARD_TOKEN.safeTransfer(msg.sender, rewards);
emit RewardPaid(positionId, address(REWARD_TOKEN), uint128(rewards));
}
// Transfer the asset back to the position owner.
NON_FUNGIBLE_POSITION_MANAGER.safeTransferFrom(address(this), msg.sender, positionId);
}
/**
* @notice Claims and transfers the staking rewards of the position.
* @param positionId The id of the position.
* @return rewards The amount of reward tokens claimed.
*/
function claimReward(uint256 positionId) external nonReentrant returns (uint256 rewards) {
if (_ownerOf[positionId] != msg.sender) revert NotOwner();
// Claim the rewards from the external staking contract.
ICLGauge(positionState[positionId].gauge).getReward(positionId);
rewards = REWARD_TOKEN.balanceOf(address(this));
// Pay out the rewards to the position owner.
if (rewards > 0) {
// Transfer reward
REWARD_TOKEN.safeTransfer(msg.sender, rewards);
emit RewardPaid(positionId, address(REWARD_TOKEN), uint128(rewards));
}
}
/**
* @notice Returns the amount of reward tokens claimable by a position.
* @param positionId The id of the position to check the rewards for.
* @return rewards The current amount of reward tokens claimable by the owner of the position.
*/
function rewardOf(uint256 positionId) public view returns (uint256 rewards) {
rewards = ICLGauge(positionState[positionId].gauge).earned(address(this), positionId);
}
/*///////////////////////////////////////////////////////////////
ERC-721 LOGIC
///////////////////////////////////////////////////////////////*/
/**
* @notice Function that stores a new base URI.
* @param newBaseURI The new base URI to store.
*/
function setBaseURI(string calldata newBaseURI) external onlyOwner {
baseURI = newBaseURI;
}
/**
* @notice Function that returns the token URI as defined in the ERC721 standard.
* @param tokenId The id of the Account.
* @return uri The token URI.
*/
function tokenURI(uint256 tokenId) public view override returns (string memory uri) {
return bytes(baseURI).length > 0 ? string(abi.encodePacked(baseURI, tokenId.toString())) : "";
}
/**
* @notice Returns the onERC721Received selector.
*/
function onERC721Received(address, address, uint256, bytes calldata) public pure returns (bytes4) {
return this.onERC721Received.selector;
}
}
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity 0.8.22;
library Strings {
/**
* @dev Converts a `uint256` to its ASCII `string` decimal representation.
*/
function toString(uint256 value) internal pure returns (string memory) {
// Inspired by OraclizeAPI's implementation - MIT licence
// https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol
if (value == 0) {
return "0";
}
uint256 temp = value;
uint256 digits;
while (temp != 0) {
digits++;
temp /= 10;
}
bytes memory buffer = new bytes(digits);
while (value != 0) {
digits -= 1;
buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));
value /= 10;
}
return string(buffer);
}
}
//https://github.com/Uniswap/v3-core/blob/main/contracts/libraries/TickMath.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity 0.8.22;
library TickMath {
/// @dev The minimum tick that may be passed to #getSqrtRatioAtTick computed from log base 1.0001 of 2**-128
int24 internal constant MIN_TICK = -887_272;
/// @dev The maximum tick that may be passed to #getSqrtRatioAtTick computed from log base 1.0001 of 2**128
int24 internal constant MAX_TICK = -MIN_TICK;
/// @dev The minimum value that can be returned from #getSqrtRatioAtTick. Equivalent to getSqrtRatioAtTick(MIN_TICK)
uint160 internal constant MIN_SQRT_RATIO = 4_295_128_739;
/// @dev The maximum value that can be returned from #getSqrtRatioAtTick. Equivalent to getSqrtRatioAtTick(MAX_TICK)
uint160 internal constant MAX_SQRT_RATIO = 1_461_446_703_485_210_103_287_273_052_203_988_822_378_723_970_342;
/// @notice Calculates sqrt(1.0001^tick) * 2^96
/// @dev Throws if |tick| > max tick
/// @param tick The input tick for the above formula
/// @return sqrtPriceX96 A Fixed point Q64.96 number representing the sqrt of the ratio of the two assets (token1/token0)
/// at the given tick
function getSqrtRatioAtTick(int24 tick) internal pure returns (uint160 sqrtPriceX96) {
unchecked {
uint256 absTick = tick < 0 ? uint256(-int256(tick)) : uint256(int256(tick));
require(absTick <= uint256(uint24(MAX_TICK)), "T");
uint256 ratio =
absTick & 0x1 != 0 ? 0xfffcb933bd6fad37aa2d162d1a594001 : 0x100000000000000000000000000000000;
if (absTick & 0x2 != 0) ratio = (ratio * 0xfff97272373d413259a46990580e213a) >> 128;
if (absTick & 0x4 != 0) ratio = (ratio * 0xfff2e50f5f656932ef12357cf3c7fdcc) >> 128;
if (absTick & 0x8 != 0) ratio = (ratio * 0xffe5caca7e10e4e61c3624eaa0941cd0) >> 128;
if (absTick & 0x10 != 0) ratio = (ratio * 0xffcb9843d60f6159c9db58835c926644) >> 128;
if (absTick & 0x20 != 0) ratio = (ratio * 0xff973b41fa98c081472e6896dfb254c0) >> 128;
if (absTick & 0x40 != 0) ratio = (ratio * 0xff2ea16466c96a3843ec78b326b52861) >> 128;
if (absTick & 0x80 != 0) ratio = (ratio * 0xfe5dee046a99a2a811c461f1969c3053) >> 128;
if (absTick & 0x100 != 0) ratio = (ratio * 0xfcbe86c7900a88aedcffc83b479aa3a4) >> 128;
if (absTick & 0x200 != 0) ratio = (ratio * 0xf987a7253ac413176f2b074cf7815e54) >> 128;
if (absTick & 0x400 != 0) ratio = (ratio * 0xf3392b0822b70005940c7a398e4b70f3) >> 128;
if (absTick & 0x800 != 0) ratio = (ratio * 0xe7159475a2c29b7443b29c7fa6e889d9) >> 128;
if (absTick & 0x1000 != 0) ratio = (ratio * 0xd097f3bdfd2022b8845ad8f792aa5825) >> 128;
if (absTick & 0x2000 != 0) ratio = (ratio * 0xa9f746462d870fdf8a65dc1f90e061e5) >> 128;
if (absTick & 0x4000 != 0) ratio = (ratio * 0x70d869a156d2a1b890bb3df62baf32f7) >> 128;
if (absTick & 0x8000 != 0) ratio = (ratio * 0x31be135f97d08fd981231505542fcfa6) >> 128;
if (absTick & 0x10000 != 0) ratio = (ratio * 0x9aa508b5b7a84e1c677de54f3e99bc9) >> 128;
if (absTick & 0x20000 != 0) ratio = (ratio * 0x5d6af8dedb81196699c329225ee604) >> 128;
if (absTick & 0x40000 != 0) ratio = (ratio * 0x2216e584f5fa1ea926041bedfe98) >> 128;
if (absTick & 0x80000 != 0) ratio = (ratio * 0x48a170391f7dc42444e8fa2) >> 128;
if (tick > 0) ratio = type(uint256).max / ratio;
// this divides by 1<<32 rounding up to go from a Q128.128 to a Q128.96.
// we then downcast because we know the result always fits within 160 bits due to our tick input constraint
// we round up in the division so getTickAtSqrtRatio of the output price is always consistent
sqrtPriceX96 = uint160((ratio >> 32) + (ratio % (1 << 32) == 0 ? 0 : 1));
}
}
/// @notice Calculates the greatest tick value such that getRatioAtTick(tick) <= ratio
/// @dev Throws in case sqrtPriceX96 < MIN_SQRT_RATIO, as MIN_SQRT_RATIO is the lowest value getRatioAtTick may
/// ever return.
/// @param sqrtPriceX96 The sqrt ratio for which to compute the tick as a Q64.96
/// @return tick The greatest tick for which the ratio is less than or equal to the input ratio
function getTickAtSqrtRatio(uint160 sqrtPriceX96) internal pure returns (int24 tick) {
unchecked {
// second inequality must be < because the price can never reach the price at the max tick
require(sqrtPriceX96 >= MIN_SQRT_RATIO && sqrtPriceX96 < MAX_SQRT_RATIO, "R");
uint256 ratio = uint256(sqrtPriceX96) << 32;
uint256 r = ratio;
uint256 msb = 0;
assembly {
let f := shl(7, gt(r, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF))
msb := or(msb, f)
r := shr(f, r)
}
assembly {
let f := shl(6, gt(r, 0xFFFFFFFFFFFFFFFF))
msb := or(msb, f)
r := shr(f, r)
}
assembly {
let f := shl(5, gt(r, 0xFFFFFFFF))
msb := or(msb, f)
r := shr(f, r)
}
assembly {
let f := shl(4, gt(r, 0xFFFF))
msb := or(msb, f)
r := shr(f, r)
}
assembly {
let f := shl(3, gt(r, 0xFF))
msb := or(msb, f)
r := shr(f, r)
}
assembly {
let f := shl(2, gt(r, 0xF))
msb := or(msb, f)
r := shr(f, r)
}
assembly {
let f := shl(1, gt(r, 0x3))
msb := or(msb, f)
r := shr(f, r)
}
assembly {
let f := gt(r, 0x1)
msb := or(msb, f)
}
if (msb >= 128) r = ratio >> (msb - 127);
else r = ratio << (127 - msb);
int256 log_2 = (int256(msb) - 128) << 64;
assembly {
r := shr(127, mul(r, r))
let f := shr(128, r)
log_2 := or(log_2, shl(63, f))
r := shr(f, r)
}
assembly {
r := shr(127, mul(r, r))
let f := shr(128, r)
log_2 := or(log_2, shl(62, f))
r := shr(f, r)
}
assembly {
r := shr(127, mul(r, r))
let f := shr(128, r)
log_2 := or(log_2, shl(61, f))
r := shr(f, r)
}
assembly {
r := shr(127, mul(r, r))
let f := shr(128, r)
log_2 := or(log_2, shl(60, f))
r := shr(f, r)
}
assembly {
r := shr(127, mul(r, r))
let f := shr(128, r)
log_2 := or(log_2, shl(59, f))
r := shr(f, r)
}
assembly {
r := shr(127, mul(r, r))
let f := shr(128, r)
log_2 := or(log_2, shl(58, f))
r := shr(f, r)
}
assembly {
r := shr(127, mul(r, r))
let f := shr(128, r)
log_2 := or(log_2, shl(57, f))
r := shr(f, r)
}
assembly {
r := shr(127, mul(r, r))
let f := shr(128, r)
log_2 := or(log_2, shl(56, f))
r := shr(f, r)
}
assembly {
r := shr(127, mul(r, r))
let f := shr(128, r)
log_2 := or(log_2, shl(55, f))
r := shr(f, r)
}
assembly {
r := shr(127, mul(r, r))
let f := shr(128, r)
log_2 := or(log_2, shl(54, f))
r := shr(f, r)
}
assembly {
r := shr(127, mul(r, r))
let f := shr(128, r)
log_2 := or(log_2, shl(53, f))
r := shr(f, r)
}
assembly {
r := shr(127, mul(r, r))
let f := shr(128, r)
log_2 := or(log_2, shl(52, f))
r := shr(f, r)
}
assembly {
r := shr(127, mul(r, r))
let f := shr(128, r)
log_2 := or(log_2, shl(51, f))
r := shr(f, r)
}
assembly {
r := shr(127, mul(r, r))
let f := shr(128, r)
log_2 := or(log_2, shl(50, f))
}
int256 log_sqrt10001 = log_2 * 255_738_958_999_603_826_347_141; // 128.128 number
int24 tickLow = int24((log_sqrt10001 - 3_402_992_956_809_132_418_596_140_100_660_247_210) >> 128);
int24 tickHi = int24((log_sqrt10001 + 291_339_464_771_989_622_907_027_621_153_398_088_495) >> 128);
tick = tickLow == tickHi ? tickLow : getSqrtRatioAtTick(tickHi) <= sqrtPriceX96 ? tickHi : tickLow;
}
}
}
{
"compilationTarget": {
"src/asset-modules/Slipstream/StakedSlipstreamAM.sol": "StakedSlipstreamAM"
},
"evmVersion": "shanghai",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": [
":@ensdomains/=lib/slipstream/node_modules/@ensdomains/",
":@nomad-xyz/=lib/slipstream/lib/ExcessivelySafeCall/",
":@openzeppelin/=lib/openzeppelin-contracts/",
":@solidity-parser/=lib/slipstream/node_modules/solhint/node_modules/@solidity-parser/",
":@uniswap/v2-core/contracts/=test/utils/fixtures/swap-router-02/",
":@uniswap/v3-core/contracts/=lib/v3-core/contracts/",
":@uniswap/v3-periphery/contracts/=lib/v3-periphery/contracts/",
":ExcessivelySafeCall/=lib/slipstream/lib/ExcessivelySafeCall/src/",
":base64-sol/=lib/slipstream/lib/base64/",
":base64/=lib/slipstream/lib/base64/",
":contracts/=lib/slipstream/contracts/",
":ds-test/=lib/forge-std/lib/ds-test/src/",
":forge-std/=lib/forge-std/src/",
":hardhat/=lib/slipstream/node_modules/hardhat/",
":openzeppelin-contracts/=lib/openzeppelin-contracts/contracts/",
":slipstream/=lib/slipstream/",
":solidity-lib/=lib/slipstream/lib/solidity-lib/contracts/",
":solmate/=lib/solmate/src/",
":swap-router-contracts/=lib/swap-router-contracts/contracts/",
":v3-core/=lib/v3-core/",
":v3-periphery/=lib/v3-periphery/contracts/"
]
}
[{"inputs":[{"internalType":"address","name":"registry","type":"address"},{"internalType":"address","name":"nonFungiblePositionManager","type":"address"},{"internalType":"address","name":"aerodromeVoter","type":"address"},{"internalType":"address","name":"rewardToken","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AssetNotAllowed","type":"error"},{"inputs":[],"name":"ExposureNotInLimits","type":"error"},{"inputs":[],"name":"GaugeNotValid","type":"error"},{"inputs":[],"name":"InvalidId","type":"error"},{"inputs":[],"name":"NotOwner","type":"error"},{"inputs":[],"name":"OnlyRegistry","type":"error"},{"inputs":[],"name":"Overflow","type":"error"},{"inputs":[],"name":"RewardTokenNotAllowed","type":"error"},{"inputs":[],"name":"RewardTokenNotValid","type":"error"},{"inputs":[],"name":"RiskFactorNotInLimits","type":"error"},{"inputs":[],"name":"ZeroLiquidity","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"bool","name":"approved","type":"bool"}],"name":"ApprovalForAll","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"positionId","type":"uint256"},{"indexed":true,"internalType":"address","name":"reward","type":"address"},{"indexed":false,"internalType":"uint128","name":"amount","type":"uint128"}],"name":"RewardPaid","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[],"name":"ASSET_TYPE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"REGISTRY","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"REWARD_TOKEN","outputs":[{"internalType":"contract ERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"gauge","type":"address"}],"name":"addGauge","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"}],"name":"approve","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"assetKey","type":"bytes32"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"assetToUnderlyingAssets","outputs":[{"internalType":"bytes32","name":"underlyingAssetKeys","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"baseURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"positionId","type":"uint256"}],"name":"burn","outputs":[{"internalType":"uint256","name":"rewards","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"positionId","type":"uint256"}],"name":"claimReward","outputs":[{"internalType":"uint256","name":"rewards","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"getApproved","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"creditor","type":"address"},{"internalType":"address","name":"asset","type":"address"},{"internalType":"uint256","name":"assetId","type":"uint256"}],"name":"getRiskFactors","outputs":[{"internalType":"uint16","name":"collateralFactor","type":"uint16"},{"internalType":"uint16","name":"liquidationFactor","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"creditor","type":"address"},{"internalType":"address","name":"asset","type":"address"},{"internalType":"uint256","name":"assetId","type":"uint256"},{"internalType":"uint256","name":"assetAmount","type":"uint256"}],"name":"getValue","outputs":[{"internalType":"uint256","name":"valueInUsd","type":"uint256"},{"internalType":"uint256","name":"collateralFactor","type":"uint256"},{"internalType":"uint256","name":"liquidationFactor","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"inAssetModule","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"asset","type":"address"},{"internalType":"uint256","name":"assetId","type":"uint256"}],"name":"isAllowed","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"isApprovedForAll","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"creditor","type":"address"},{"internalType":"bytes32","name":"assetKey","type":"bytes32"},{"internalType":"bytes32","name":"underlyingAssetKey","type":"bytes32"}],"name":"lastExposureAssetToUnderlyingAsset","outputs":[{"internalType":"uint256","name":"exposure","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"creditor","type":"address"},{"internalType":"bytes32","name":"assetKey","type":"bytes32"}],"name":"lastExposuresAsset","outputs":[{"internalType":"uint112","name":"lastExposureAsset","type":"uint112"},{"internalType":"uint112","name":"lastUsdExposureAsset","type":"uint112"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"assetId","type":"uint256"}],"name":"mint","outputs":[{"internalType":"uint256","name":"positionId","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"onERC721Received","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"ownerOf","outputs":[{"internalType":"address","name":"owner","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"pool","type":"address"}],"name":"poolToGauge","outputs":[{"internalType":"address","name":"gauge","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"position","type":"uint256"}],"name":"positionState","outputs":[{"internalType":"int24","name":"tickLower","type":"int24"},{"internalType":"int24","name":"tickUpper","type":"int24"},{"internalType":"uint128","name":"liquidity","type":"uint128"},{"internalType":"address","name":"gauge","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"creditor","type":"address"},{"internalType":"address","name":"asset","type":"address"},{"internalType":"uint256","name":"assetId","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"processDirectDeposit","outputs":[{"internalType":"uint256","name":"recursiveCalls","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"creditor","type":"address"},{"internalType":"address","name":"asset","type":"address"},{"internalType":"uint256","name":"assetId","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"processDirectWithdrawal","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"creditor","type":"address"},{"internalType":"address","name":"asset","type":"address"},{"internalType":"uint256","name":"assetId","type":"uint256"},{"internalType":"uint256","name":"exposureUpperAssetToAsset","type":"uint256"},{"internalType":"int256","name":"deltaExposureUpperAssetToAsset","type":"int256"}],"name":"processIndirectDeposit","outputs":[{"internalType":"uint256","name":"recursiveCalls","type":"uint256"},{"internalType":"uint256","name":"usdExposureUpperAssetToAsset","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"creditor","type":"address"},{"internalType":"address","name":"asset","type":"address"},{"internalType":"uint256","name":"assetId","type":"uint256"},{"internalType":"uint256","name":"exposureUpperAssetToAsset","type":"uint256"},{"internalType":"int256","name":"deltaExposureUpperAssetToAsset","type":"int256"}],"name":"processIndirectWithdrawal","outputs":[{"internalType":"uint256","name":"usdExposureUpperAssetToAsset","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"positionId","type":"uint256"}],"name":"rewardOf","outputs":[{"internalType":"uint256","name":"rewards","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"creditor","type":"address"}],"name":"riskParams","outputs":[{"internalType":"uint112","name":"lastUsdExposureProtocol","type":"uint112"},{"internalType":"uint112","name":"maxUsdExposureProtocol","type":"uint112"},{"internalType":"uint16","name":"riskFactor","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"bool","name":"approved","type":"bool"}],"name":"setApprovalForAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"newBaseURI","type":"string"}],"name":"setBaseURI","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"creditor","type":"address"},{"internalType":"uint112","name":"maxUsdExposureProtocol_","type":"uint112"},{"internalType":"uint16","name":"riskFactor","type":"uint16"}],"name":"setRiskParameters","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"tokenURI","outputs":[{"internalType":"string","name":"uri","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"}],"name":"transferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"}]