ETH Price: $2,972.07 (+1.76%)

Contract

0x1Dc7A0f5336F52724B650E39174cfcbbEdD67bF1

Overview

ETH Balance

0 ETH

ETH Value

$0.00

More Info

Private Name Tags

Multichain Info

No addresses found
Transaction Hash
Block
From
To

There are no matching entries

Please try again later

View more zero value Internal Transactions in Advanced View mode

Advanced mode:

Cross-Chain Transactions
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
StakedSlipstreamAM

Compiler Version
v0.8.30+commit.73712a01

Optimization Enabled:
Yes with 200 runs

Other Settings:
prague EvmVersion
/**
 * Created by Pragma Labs
 * SPDX-License-Identifier: BUSL-1.1
 */
pragma solidity ^0.8.30;

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.
    // forge-lint: disable-next-item(mixed-case-variable)
    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 owner_ The address of the Owner.
     * @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 owner_,
        address registry,
        address nonFungiblePositionManager,
        address aerodromeVoter,
        address rewardToken
    ) DerivedAM(owner_, registry, 2) ERC721("Arcadia Staked Slipstream Positions", "aSSLIPP") {
        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 {
        if (!IRegistry(REGISTRY).isAllowed(address(REWARD_TOKEN), 0)) revert RewardTokenNotAllowed();

        inAssetModule[address(this)] = true;

        // forge-lint: disable-next-line(unsafe-typecast)
        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)) 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.
        // forge-lint: disable-next-line(unsafe-typecast)
        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);
            // forge-lint: disable-next-line(unsafe-typecast)
            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);
            // forge-lint: disable-next-line(unsafe-typecast)
            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.
     */
    // forge-lint: disable-next-item(mixed-case-function,mixed-case-variable)
    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.
     */
    // forge-lint: disable-next-item(mixed-case-function,mixed-case-variable)
    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.
     */
    // forge-lint: disable-next-item(mixed-case-function)
    function onERC721Received(address, address, uint256, bytes calldata) public pure returns (bytes4) {
        return this.onERC721Received.selector;
    }
}

/**
 * Created by Pragma Labs
 * SPDX-License-Identifier: BUSL-1.1
 */
pragma solidity ^0.8.0;

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

/**
 * Created by Pragma Labs
 * SPDX-License-Identifier: BUSL-1.1
 */
pragma solidity ^0.8.0;

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 owner_ The address of the Owner.
     * @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 owner_, address registry_, uint256 assetType_) AssetModule(owner_, 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.
        // forge-lint: disable-next-line(unsafe-typecast)
        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".
        // forge-lint: disable-next-line(unsafe-typecast)
        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.
            // forge-lint: disable-next-line(unsafe-typecast)
            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();
            }
        }
        // unsafe cast: usdExposureProtocol is smaller than maxUsdExposureProtocol, which is a uint112.
        // forge-lint: disable-next-line(unsafe-typecast)
        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.
        // forge-lint: disable-next-line(unsafe-typecast)
        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.
        // forge-lint: disable-next-item(unsafe-typecast)
        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);
    }
}

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

File 6 of 24 : FixedPoint96.sol
// https://github.com/Uniswap/v3-core/blob/main/contracts/libraries/FixedPoint96.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;

/// @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;
}

/**
 * Created by Arcadia Finance
 * https://www.arcadia.finance
 *
 * SPDX-License-Identifier: MIT
 */
pragma solidity ^0.8.0;

interface IAeroVoter {
    function isGauge(address) external view returns (bool);
}

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;

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.0;

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.0;

/// @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_);
}

// https://github.com/Uniswap/v3-periphery/blob/main/contracts/libraries/LiquidityAmounts.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;

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
// forge-lint: disable-next-item(mixed-case-variable)
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);
        }
    }
}

// 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.0;

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

File 13 of 24 : ReentrancyGuard.sol
// 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;

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.
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.

            // 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.
            success := call(gas(), token, 0, freeMemoryPointer, 100, 0, 32)

            // 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 and token has code.
            if and(iszero(and(eq(mload(0), 1), gt(returndatasize(), 31))), success) {
                success := iszero(or(iszero(extcodesize(token)), returndatasize())) 
            }
        }

        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.

            // 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.
            success := call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)

            // 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 and token has code.
            if and(iszero(and(eq(mload(0), 1), gt(returndatasize(), 31))), success) {
                success := iszero(or(iszero(extcodesize(token)), returndatasize())) 
            }
        }

        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.

            // 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.
            success := call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)

            // 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 and token has code.
            if and(iszero(and(eq(mload(0), 1), gt(returndatasize(), 31))), success) {
                success := iszero(or(iszero(extcodesize(token)), returndatasize())) 
            }
        }

        require(success, "APPROVE_FAILED");
    }
}

// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity ^0.8.0;

// forge-lint: disable-next-item(unsafe-typecast)
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 license
        // 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.0;

// forge-lint: disable-next-item(mixed-case-variable,unsafe-typecast)
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;
        }
    }
}

/**
 * Created by Pragma Labs
 * SPDX-License-Identifier: BUSL-1.1
 */
pragma solidity ^0.8.0;

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 owner_ The address of the Owner.
     * @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 owner_, address registry_, uint256 assetType_) Owned(owner_) {
        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);
}

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

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

/**
 * Created by Pragma Labs
 * SPDX-License-Identifier: MIT
 */
pragma solidity ^0.8.0;

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-core/blob/main/contracts/libraries/FullMath.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;

/// @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 preconditions 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;
    }

    /// @notice Calculates ceil(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
    function mulDivRoundingUp(uint256 a, uint256 b, uint256 denominator) internal pure returns (uint256 result) {
        result = mulDiv(a, b, denominator);
        if (mulmod(a, b, denominator) > 0) {
            require(result < type(uint256).max);
            result++;
        }
    }
}

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;

interface ICLFactory {
    function poolImplementation() external view returns (address);
}

/**
 * Created by Pragma Labs
 * SPDX-License-Identifier: MIT
 */
pragma solidity ^0.8.0;

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

Settings
{
  "remappings": [
    "@ensdomains/=lib/lending-v2/lib/accounts-v2/lib/slipstream/node_modules/@ensdomains/",
    "@nomad-xyz/=lib/lending-v2/lib/accounts-v2/lib/slipstream/lib/ExcessivelySafeCall/",
    "@openzeppelin/=lib/lending-v2/lib/accounts-v2/lib/slipstream/lib/openzeppelin-contracts/",
    "@solidity-parser/=lib/lending-v2/lib/accounts-v2/lib/slipstream/node_modules/solhint/node_modules/@solidity-parser/",
    "@uniswap/v2-core/contracts/=lib/lending-v2/lib/accounts-v2/./test/utils/fixtures/swap-router-02/",
    "@uniswap/v3-core/contracts/=lib/lending-v2/lib/accounts-v2/lib/v3-core/contracts/",
    "@uniswap/v3-periphery/contracts/=lib/lending-v2/lib/accounts-v2/lib/v3-periphery/contracts/",
    "@uniswap/v4-core/=lib/lending-v2/lib/accounts-v2/lib/v4-periphery/lib/v4-core/",
    "@utils/=lib/lending-v2/lib/accounts-v2/lib/merkl-contracts/node_modules/utils/src/",
    "ExcessivelySafeCall/=lib/lending-v2/lib/accounts-v2/lib/slipstream/lib/ExcessivelySafeCall/src/",
    "accounts-v2/=lib/lending-v2/lib/accounts-v2/src/",
    "arcadia-periphery/=lib/arcadia-periphery/src/",
    "asset-managers/=lib/arcadia-periphery/lib/asset-managers/src/",
    "base64-sol/=lib/lending-v2/lib/accounts-v2/lib/slipstream/lib/base64/",
    "base64/=lib/lending-v2/lib/accounts-v2/lib/slipstream/lib/base64/",
    "contracts/=lib/lending-v2/lib/accounts-v2/lib/slipstream/contracts/",
    "ds-test/=lib/lending-v2/lib/accounts-v2/lib/solmate/lib/ds-test/src/",
    "erc4626-tests/=lib/lending-v2/lib/accounts-v2/lib/openzeppelin-contracts-v4.9/lib/erc4626-tests/",
    "forge-gas-snapshot/=lib/lending-v2/lib/accounts-v2/lib/v4-periphery/lib/permit2/lib/forge-gas-snapshot/src/",
    "forge-std/=lib/lending-v2/lib/accounts-v2/lib/forge-std/src/",
    "hardhat/=lib/lending-v2/lib/accounts-v2/lib/slipstream/node_modules/hardhat/",
    "lending-v2/=lib/lending-v2/src/",
    "merkl-contracts/=lib/lending-v2/lib/accounts-v2/lib/merkl-contracts/",
    "openzeppelin-contracts-upgradeable-v4.9/=lib/lending-v2/lib/accounts-v2/lib/openzeppelin-contracts-upgradeable-v4.9/",
    "openzeppelin-contracts-v3.4/=lib/lending-v2/lib/accounts-v2/lib/openzeppelin-contracts-v3.4/contracts/",
    "openzeppelin-contracts-v4.9/=lib/lending-v2/lib/accounts-v2/lib/openzeppelin-contracts-v4.9/",
    "openzeppelin-contracts/=lib/lending-v2/lib/accounts-v2/lib/slipstream/lib/openzeppelin-contracts/contracts/",
    "openzeppelin/=lib/lending-v2/lib/accounts-v2/lib/openzeppelin-contracts-v4.9/contracts/",
    "oz/=lib/lending-v2/lib/accounts-v2/lib/merkl-contracts/node_modules/@openzeppelin/contracts/",
    "permit2/=lib/lending-v2/lib/accounts-v2/lib/v4-periphery/lib/permit2/",
    "slipstream/=lib/lending-v2/lib/accounts-v2/lib/slipstream/",
    "solady/=lib/lending-v2/lib/accounts-v2/lib/solady/src/",
    "solidity-lib/=lib/lending-v2/lib/accounts-v2/lib/slipstream/lib/solidity-lib/contracts/",
    "solmate/=lib/lending-v2/lib/accounts-v2/lib/solmate/",
    "swap-router-contracts/=lib/lending-v2/lib/accounts-v2/lib/swap-router-contracts/contracts/",
    "v3-core/=lib/lending-v2/lib/accounts-v2/lib/v3-core/",
    "v3-periphery/=lib/lending-v2/lib/accounts-v2/lib/v3-periphery/contracts/",
    "v4-core/=lib/lending-v2/lib/accounts-v2/lib/v4-periphery/lib/v4-core/src/",
    "v4-periphery/=lib/lending-v2/lib/accounts-v2/lib/v4-periphery/",
    "lib/accounts-v2/lib/merkl-contracts:@openzeppelin/contracts-upgradeable/=lib/arcadia-periphery/lib/asset-managers/lib/accounts-v2/lib/openzeppelin-contracts-upgradeable-v4.9/contracts/",
    "lib/accounts-v2/lib/merkl-contracts:@openzeppelin/contracts/=lib/arcadia-periphery/lib/asset-managers/lib/accounts-v2/lib/openzeppelin-contracts-v4.9/contracts/",
    "lib/accounts-v2/lib/openzeppelin-contracts-upgradeable-v4.9:@openzeppelin/=lib/arcadia-periphery/lib/asset-managers/lib/accounts-v2/lib/openzeppelin-contracts-v4.9/",
    "lib/accounts-v2/lib/slipstream:@openzeppelin/=lib/arcadia-periphery/lib/asset-managers/lib/accounts-v2/lib/slipstream/lib/openzeppelin-contracts/",
    "lib/accounts-v2/lib/swap-router-contracts:@openzeppelin/=lib/arcadia-periphery/lib/asset-managers/lib/accounts-v2/lib/openzeppelin-contracts-v3.4/",
    "lib/accounts-v2/lib/v3-periphery:@openzeppelin/=lib/arcadia-periphery/lib/asset-managers/lib/accounts-v2/lib/openzeppelin-contracts-v3.4/",
    "lib/accounts-v2/lib/v4-periphery:@openzeppelin/=lib/arcadia-periphery/lib/asset-managers/lib/accounts-v2/lib/v4-periphery/lib/v4-core/lib/openzeppelin-contracts/",
    "lib/accounts-v2/lib/v4-periphery/lib/v4-core:@openzeppelin/=lib/arcadia-periphery/lib/asset-managers/lib/accounts-v2/lib/v4-periphery/lib/v4-core/lib/openzeppelin-contracts/",
    "lib/asset-managers/lib/accounts-v2/lib/slipstream:@openzeppelin/=lib/arcadia-periphery/lib/asset-managers/lib/accounts-v2/lib/slipstream/lib/openzeppelin-contracts/",
    "lib/asset-managers/lib/accounts-v2/lib/swap-router-contracts:@openzeppelin/=lib/arcadia-periphery/lib/asset-managers/lib/accounts-v2/lib/openzeppelin-contracts-v3.4/",
    "lib/asset-managers/lib/accounts-v2/lib/v3-periphery:@openzeppelin/=lib/arcadia-periphery/lib/asset-managers/lib/accounts-v2/lib/openzeppelin-contracts-v3.4/",
    "lib/asset-managers/lib/accounts-v2/lib/v4-periphery:@openzeppelin/=lib/arcadia-periphery/lib/asset-managers/lib/accounts-v2/lib/v4-periphery/lib/v4-core/lib/openzeppelin-contracts/",
    "lib/asset-managers/lib/accounts-v2/lib/v4-periphery/lib/v4-core:@openzeppelin/=lib/arcadia-periphery/lib/asset-managers/lib/accounts-v2/lib/v4-periphery/lib/v4-core/lib/openzeppelin-contracts/",
    "lib/merkl-contracts:@openzeppelin/contracts-upgradeable/=lib/lending-v2/lib/accounts-v2/lib/openzeppelin-contracts-upgradeable-v4.9/contracts/",
    "lib/merkl-contracts:@openzeppelin/contracts/=lib/lending-v2/lib/accounts-v2/lib/openzeppelin-contracts-v4.9/contracts/",
    "lib/openzeppelin-contracts-upgradeable-v4.9:@openzeppelin/=lib/lending-v2/lib/accounts-v2/lib/openzeppelin-contracts-v4.9/",
    "lib/slipstream:@openzeppelin/=lib/lending-v2/lib/accounts-v2/lib/slipstream/lib/openzeppelin-contracts/",
    "lib/v3-periphery:@openzeppelin/=lib/lending-v2/lib/accounts-v2/lib/openzeppelin-contracts-v3.4/",
    "lib/v4-periphery:@openzeppelin/=lib/lending-v2/lib/accounts-v2/lib/v4-periphery/lib/v4-core/lib/openzeppelin-contracts/",
    "lib/v4-periphery/lib/v4-core:@openzeppelin/=lib/lending-v2/lib/accounts-v2/lib/v4-periphery/lib/v4-core/lib/openzeppelin-contracts/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "metadata": {
    "useLiteralContent": false,
    "bytecodeHash": "ipfs",
    "appendCBOR": true
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "evmVersion": "prague",
  "viaIR": false
}

Contract Security Audit

Contract ABI

API
[{"inputs":[{"internalType":"address","name":"owner_","type":"address"},{"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"}]

6101406040526001600b55348015610015575f5ffd5b50604051614dcf380380614dcf833981016040819052610034916101a3565b604051806060016040528060238152602001614dac60239139604080518082018252600781526606153534c4950560cc1b60208201525f80546001600160a01b0319166001600160a01b038a169081178255925191928992899260029285928592859285929091907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908290a3506001600160a01b0390911660a05260805250600592506100e691508490508261029c565b5060066100f3828261029c565b5050506001600160a01b038083166101005281811660e05283166101208190526040805163c45a015560e01b8152905163c45a01559160048082019260209290919082900301815f875af115801561014d573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906101719190610356565b6001600160a01b031660c052506103769350505050565b80516001600160a01b038116811461019e575f5ffd5b919050565b5f5f5f5f5f60a086880312156101b7575f5ffd5b6101c086610188565b94506101ce60208701610188565b93506101dc60408701610188565b92506101ea60608701610188565b91506101f860808701610188565b90509295509295909350565b634e487b7160e01b5f52604160045260245ffd5b600181811c9082168061022c57607f821691505b60208210810361024a57634e487b7160e01b5f52602260045260245ffd5b50919050565b601f82111561029757805f5260205f20601f840160051c810160208510156102755750805b601f840160051c820191505b81811015610294575f8155600101610281565b50505b505050565b81516001600160401b038111156102b5576102b5610204565b6102c9816102c38454610218565b84610250565b6020601f8211600181146102fb575f83156102e45750848201515b5f19600385901b1c1916600184901b178455610294565b5f84815260208120601f198516915b8281101561032a578785015182556020948501946001909201910161030a565b508482101561034757868401515f19600387901b60f8161c191681555b50505050600190811b01905550565b5f60208284031215610366575f5ffd5b61036f82610188565b9392505050565b60805160a05160c05160e051610100516101205161494061046c5f395f81816110d2015281816119e701528181611a4e0152611d8401525f61157801525f818161069e01528181610f620152818161101f0152818161105f015281816113ad0152818161160001528181611cef0152818161209f01528181612127015261216701525f611b1e01525f818161029b0152818161082f01528181610a2201528181610b25015281816112a5015281816113db015281816114ca015281816117160152818161182f01528181612290015281816126dc01528181612bd7015261306501525f818161065d015261149301526149405ff3fe608060405234801561000f575f5ffd5b5060043610610255575f3560e01c806370a0823111610140578063a0712d68116100bf578063ba9270ff11610084578063ba9270ff14610747578063c87b56dd1461075a578063daa3770c1461076d578063e985e9c514610780578063f2fde38b146107ad578063f8350ed0146107c0575f5ffd5b8063a0712d68146106d3578063a22cb465146106e6578063aa3f22b8146106f9578063ae169a5014610721578063b88d4fde14610734575f5ffd5b80638815eb39116101055780638815eb39146106585780638da5cb5b1461067f57806395d89b411461069157806399248ea7146106995780639da882ac146106c0575f5ffd5b806370a082311461053957806373aecb981461054c5780638129fc1c1461055f57806382d6b1661461056757806384b2fc50146105c5575f5ffd5b80632341eb86116101d757806342966c681161019c57806342966c681461045f57806349bccc5d1461047257806353b85a50146104a057806355f804b31461050b5780636352211e1461051e5780636c0360eb14610531575f5ffd5b80632341eb86146103ce57806323b872dd146103f65780633a8fe5e7146104095780633c0ac4f61461043957806342842e0e1461044c575f5ffd5b8063095ea7b31161021d578063095ea7b314610312578063150b7a021461032557806315ed93bc1461035d578063172350931461037f5780631ba0408c146103a0575f5ffd5b806301ffc9a714610259578063024874c71461028157806306433b1b1461029657806306fdde03146102d5578063081812fc146102ea575b5f5ffd5b61026c610267366004613e8e565b6107d3565b60405190151581526020015b60405180910390f35b61029461028f366004613ebd565b610824565b005b6102bd7f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b039091168152602001610278565b6102dd6108a6565b6040516102789190613f00565b6102bd6102f8366004613f35565b60096020525f90815260409020546001600160a01b031681565b610294610320366004613f4c565b610932565b610344610333366004613fba565b630a85bd0160e11b95945050505050565b6040516001600160e01b03199091168152602001610278565b61026c61036b366004614027565b60016020525f908152604090205460ff1681565b61039261038d366004614042565b610a16565b604051908152602001610278565b6103b36103ae36600461408f565b610abd565b6040805161ffff938416815292909116602083015201610278565b6103e16103dc366004614042565b610b18565b60408051928352602083019190915201610278565b61029461040436600461408f565b610bcb565b6103926104173660046140cd565b600460209081525f938452604080852082529284528284209052825290205481565b6103926104473660046140ff565b610d8d565b61029461045a36600461408f565b610db8565b61039261046d366004613f35565b610e89565b610485610480366004613ebd565b61113c565b60408051938452602084019290925290820152606001610278565b6104e16104ae366004614027565b60026020525f90815260409020546001600160701b0380821691600160701b810490911690600160e01b900461ffff1683565b604080516001600160701b03948516815293909216602084015261ffff1690820152606001610278565b61029461051936600461411f565b6111a0565b6102bd61052c366004613f35565b6111d6565b6102dd61122c565b610392610547366004614027565b611239565b61029461055a36600461415d565b61129a565b61029461136d565b6105a5610575366004613f4c565b600360209081525f92835260408084209091529082529020546001600160701b0380821691600160701b90041682565b604080516001600160701b03938416815292909116602083015201610278565b61061a6105d3366004613f35565b600f6020525f908152604090208054600190910154600282810b926301000000810490910b9166010000000000009091046001600160801b0316906001600160a01b031684565b60408051600295860b81529390940b60208401526001600160801b03909116928201929092526001600160a01b039091166060820152608001610278565b6103927f000000000000000000000000000000000000000000000000000000000000000081565b5f546102bd906001600160a01b031681565b6102dd611523565b6102bd7f000000000000000000000000000000000000000000000000000000000000000081565b6102946106ce366004614027565b611530565b6103926106e1366004613f35565b611975565b6102946106f43660046141c3565b611f5b565b6102bd610707366004614027565b600e6020525f90815260409020546001600160a01b031681565b61039261072f366004613f35565b611fc6565b610294610742366004613fba565b6121c2565b610392610755366004613ebd565b612284565b6102dd610768366004613f35565b612307565b61039261077b366004613f35565b612363565b61026c61078e3660046141fa565b600a60209081525f928352604080842090915290825290205460ff1681565b6102946107bb366004614027565b6123e2565b61026c6107ce366004613f4c565b612455565b5f6301ffc9a760e01b6001600160e01b03198316148061080357506380ac58cd60e01b6001600160e01b03198316145b8061081e5750635b5e139f60e01b6001600160e01b03198316145b92915050565b336001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000161461086d576040516310f5403960e31b815260040160405180910390fd5b5f61087a848460a01b1790565b90505f610890868361088b8661423a565b612498565b905061089d868383612589565b50505050505050565b600580546108b390614254565b80601f01602080910402602001604051908101604052809291908181526020018280546108df90614254565b801561092a5780601f106109015761010080835404028352916020019161092a565b820191905f5260205f20905b81548152906001019060200180831161090d57829003601f168201915b505050505081565b5f818152600760205260409020546001600160a01b03163381148061097957506001600160a01b0381165f908152600a6020908152604080832033845290915290205460ff165b6109bb5760405162461bcd60e51b815260206004820152600e60248201526d1393d517d055551213d49256915160921b60448201526064015b60405180910390fd5b5f8281526009602052604080822080546001600160a01b0319166001600160a01b0387811691821790925591518593918516917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92591a4505050565b5f336001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614610a60576040516310f5403960e31b815260040160405180910390fd5b5f610a6d868660a01b1790565b90505f610a7b888386612498565b90505f610a89898484612589565b9050811580610a96575080155b15610aa3575f9350610ab1565b610aae81878461288d565b93505b50505095945050505050565b5f5f5f610acc858560a01b1790565b90505f610ad8826128a8565b90505f610ae88884600185612907565b5090505f610af68984612a50565b90505f5f610b058b8585612c5e565b909d909c509a5050505050505050505050565b5f80336001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614610b63576040516310f5403960e31b815260040160405180910390fd5b5f610b70878760a01b1790565b90505f610b7e898387612498565b90505f5f610b8d838c86612f2a565b91509150825f1480610b9d575080155b15610baa575f9450610bb8565b610bb581898561288d565b94505b5060010199929850919650505050505050565b5f818152600760205260409020546001600160a01b03848116911614610c205760405162461bcd60e51b815260206004820152600a60248201526957524f4e475f46524f4d60b01b60448201526064016109b2565b6001600160a01b038216610c6a5760405162461bcd60e51b81526020600482015260116024820152701253959053125117d49150d25412515395607a1b60448201526064016109b2565b336001600160a01b0384161480610ca357506001600160a01b0383165f908152600a6020908152604080832033845290915290205460ff165b80610cc357505f818152600960205260409020546001600160a01b031633145b610d005760405162461bcd60e51b815260206004820152600e60248201526d1393d517d055551213d49256915160921b60448201526064016109b2565b6001600160a01b038084165f81815260086020908152604080832080545f19019055938616808352848320805460010190558583526007825284832080546001600160a01b03199081168317909155600990925284832080549092169091559251849392917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef91a4505050565b600d602052815f5260405f208181548110610da6575f80fd5b905f5260205f20015f91509150505481565b610dc3838383610bcb565b6001600160a01b0382163b1580610e685750604051630a85bd0160e11b8082523360048301526001600160a01b03858116602484015260448301849052608060648401525f608484015290919084169063150b7a029060a4016020604051808303815f875af1158015610e38573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610e5c919061428c565b6001600160e01b031916145b610e845760405162461bcd60e51b81526004016109b2906142a7565b505050565b5f600b54600114610eac5760405162461bcd60e51b81526004016109b2906142d1565b6002600b555f828152600760205260409020546001600160a01b03163314610ee7576040516330cd747160e01b815260040160405180910390fd5b5f828152600f602052604090819020600101549051632e1a7d4d60e01b8152600481018490526001600160a01b0390911690632e1a7d4d906024015f604051808303815f87803b158015610f39575f5ffd5b505af1158015610f4b573d5f5f3e3d5ffd5b50506040516370a0823160e01b81523060048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031692506370a082319150602401602060405180830381865afa158015610fb1573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610fd591906142f5565b5f838152600f6020526040902080546001600160b01b031916815560010180546001600160a01b0319169055905061100c82613262565b80156110b0576110466001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016338361332c565b6040516001600160801b03821681526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169083907f99eb7ece63bef2ec97b991da7cf3763c7c533b1614782a4afd1b7b028db3189f9060200160405180910390a35b604051632142170760e11b8152306004820152336024820152604481018390527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906342842e0e906064015f604051808303815f87803b15801561111b575f5ffd5b505af115801561112d573d5f5f3e3d5ffd5b50506001600b55509092915050565b5f5f5f5f61114c878760a01b1790565b90505f611158826128a8565b90505f5f6111688b858a86612907565b9150915080515f036111815761117e8b84612a50565b90505b61118c8b8383612c5e565b919d909c50909a5098505050505050505050565b5f546001600160a01b031633146111c95760405162461bcd60e51b81526004016109b29061430c565b600c610e8482848361438a565b5f818152600760205260409020546001600160a01b0316806112275760405162461bcd60e51b815260206004820152600a6024820152691393d517d3525395115160b21b60448201526064016109b2565b919050565b600c80546108b390614254565b5f6001600160a01b03821661127f5760405162461bcd60e51b815260206004820152600c60248201526b5a45524f5f4144445245535360a01b60448201526064016109b2565b506001600160a01b03165f9081526008602052604090205490565b336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146112e3576040516310f5403960e31b815260040160405180910390fd5b6127108161ffff16111561130a57604051630b1c8e2560e01b815260040160405180910390fd5b6001600160a01b039092165f908152600260205260409020805461ffff909316600160e01b0261ffff60e01b196001600160701b03909316600160701b02929092166fffffffffffffffffffffffffffffffff60701b1990931692909217179055565b5f546001600160a01b031633146113965760405162461bcd60e51b81526004016109b29061430c565b604051630f8350ed60e41b81526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000811660048301525f60248301527f0000000000000000000000000000000000000000000000000000000000000000169063f8350ed090604401602060405180830381865afa158015611420573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906114449190614443565b6114615760405163c52db36360e01b815260040160405180910390fd5b305f81815260016020819052604091829020805460ff191690911790555163f1751c7b60e01b81526001600160601b037f000000000000000000000000000000000000000000000000000000000000000016600482015260248101919091526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063f1751c7b906044015f604051808303815f87803b15801561150b575f5ffd5b505af115801561151d573d5f5f3e3d5ffd5b50505050565b600680546108b390614254565b5f546001600160a01b031633146115595760405162461bcd60e51b81526004016109b29061430c565b60405163aa79979b60e01b81526001600160a01b0382811660048301527f0000000000000000000000000000000000000000000000000000000000000000169063aa79979b90602401602060405180830381865afa1580156115bd573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906115e19190614443565b6115fe5760405163e1a93ebd60e01b815260040160405180910390fd5b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316816001600160a01b031663f7c618c16040518163ffffffff1660e01b81526004016020604051808303815f875af1158015611665573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906116899190614469565b6001600160a01b0316146116b057604051633616e34b60e11b815260040160405180910390fd5b5f816001600160a01b03166316f0115b6040518163ffffffff1660e01b81526004016020604051808303815f875af11580156116ee573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906117129190614469565b90507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663f8350ed0826001600160a01b0316630dfe16816040518163ffffffff1660e01b81526004016020604051808303815f875af1158015611780573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906117a49190614469565b6040516001600160e01b031960e084901b1681526001600160a01b0390911660048201525f6024820152604401602060405180830381865afa1580156117ec573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906118109190614443565b61182d57604051634847234360e01b815260040160405180910390fd5b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663f8350ed0826001600160a01b031663d21220a76040518163ffffffff1660e01b81526004016020604051808303815f875af1158015611899573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906118bd9190614469565b6040516001600160e01b031960e084901b1681526001600160a01b0390911660048201525f6024820152604401602060405180830381865afa158015611905573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906119299190614443565b61194657604051634847234360e01b815260040160405180910390fd5b6001600160a01b039081165f908152600e602052604090208054919092166001600160a01b0319909116179055565b5f600b546001146119985760405162461bcd60e51b81526004016109b2906142d1565b6002600b556001600160601b038211156119c557604051631bf4348160e31b815260040160405180910390fd5b604051632142170760e11b8152336004820152306024820152604481018390527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906342842e0e906064015f604051808303815f87803b158015611a30575f5ffd5b505af1158015611a42573d5f5f3e3d5ffd5b505050505f5f5f5f5f5f7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166399fbab88896040518263ffffffff1660e01b8152600401611a9a91815260200190565b61018060405180830381865afa158015611ab6573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611ada91906144ab565b505050509750975097509750975097505050806001600160801b03165f03611b1557604051630200e8a960e31b815260040160405180910390fd5b5f600e5f611b457f00000000000000000000000000000000000000000000000000000000000000008a8a8a6133b5565b6001600160a01b03166001600160a01b031681526020019081526020015f205f9054906101000a90046001600160a01b0316905060405180608001604052808560020b81526020018460020b8152602001836001600160801b03168152602001826001600160a01b0316815250600f5f8b81526020019081526020015f205f820151815f015f6101000a81548162ffffff021916908360020b62ffffff1602179055506020820151815f0160036101000a81548162ffffff021916908360020b62ffffff1602179055506040820151815f0160066101000a8154816001600160801b0302191690836001600160801b031602179055506060820151816001015f6101000a8154816001600160a01b0302191690836001600160a01b031602179055509050505f60036001600160401b03811115611c8457611c84614332565b604051908082528060200260200182016040528015611cad578160200160208202803683370190505b50905087815f81518110611cc357611cc3614580565b60209081029190910101528681600181518110611ce257611ce2614580565b60209081029190910101527f000000000000000000000000000000000000000000000000000000000000000081600281518110611d2157611d21614580565b60200260200101818152505080600d5f611d3d308e60a01b1790565b81526020019081526020015f209080519060200190611d5d929190613e22565b5060405163095ea7b360e01b81526001600160a01b038381166004830152602482018c90527f0000000000000000000000000000000000000000000000000000000000000000169063095ea7b3906044015f604051808303815f87803b158015611dc5575f5ffd5b505af1158015611dd7573d5f5f3e3d5ffd5b505060405163b6b55f2560e01b8152600481018d90526001600160a01b038516925063b6b55f2591506024015f604051808303815f87803b158015611e1a575f5ffd5b505af1158015611e2c573d5f5f3e3d5ffd5b50506040516370a0823160e01b81523060048201525f92506001600160a01b038b1691506370a0823190602401602060405180830381865afa158015611e74573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611e9891906142f5565b6040516370a0823160e01b81523060048201529091505f906001600160a01b038a16906370a0823190602401602060405180830381865afa158015611edf573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611f0391906142f5565b90508115611f1f57611f1f6001600160a01b038b16338461332c565b8015611f3957611f396001600160a01b038a16338361332c565b8b9a50611f46338c6134de565b50506001600b55509698975050505050505050565b335f818152600a602090815260408083206001600160a01b03871680855290835292819020805460ff191686151590811790915590519081529192917f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31910160405180910390a35050565b5f600b54600114611fe95760405162461bcd60e51b81526004016109b2906142d1565b6002600b555f828152600760205260409020546001600160a01b03163314612024576040516330cd747160e01b815260040160405180910390fd5b5f828152600f602052604090819020600101549051631c4b774b60e01b8152600481018490526001600160a01b0390911690631c4b774b906024015f604051808303815f87803b158015612076575f5ffd5b505af1158015612088573d5f5f3e3d5ffd5b50506040516370a0823160e01b81523060048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031692506370a082319150602401602060405180830381865afa1580156120ee573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061211291906142f5565b905080156121b85761214e6001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016338361332c565b6040516001600160801b03821681526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169083907f99eb7ece63bef2ec97b991da7cf3763c7c533b1614782a4afd1b7b028db3189f9060200160405180910390a35b6001600b55919050565b6121cd858585610bcb565b6001600160a01b0384163b15806122615750604051630a85bd0160e11b808252906001600160a01b0386169063150b7a02906122159033908a90899089908990600401614594565b6020604051808303815f875af1158015612231573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612255919061428c565b6001600160e01b031916145b61227d5760405162461bcd60e51b81526004016109b2906142a7565b5050505050565b5f336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146122ce576040516310f5403960e31b815260040160405180910390fd5b5f6122db858560a01b1790565b90505f6122e9878386612498565b90505f6122f7828985612f2a565b5060010198975050505050505050565b60605f600c805461231790614254565b9050116123325760405180602001604052805f81525061081e565b600c61233d836135aa565b60405160200161234e9291906145e4565b60405160208183030381529060405292915050565b5f818152600f6020526040808220600101549051633e491d4760e01b8152306004820152602481018490526001600160a01b0390911690633e491d4790604401602060405180830381865afa1580156123be573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061081e91906142f5565b5f546001600160a01b0316331461240b5760405162461bcd60e51b81526004016109b29061430c565b5f80546001600160a01b0319166001600160a01b0383169081178255604051909133917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a350565b5f6001600160a01b0383163014801561248357505f828152600760205260409020546001600160a01b031615155b156124905750600161081e565b505f92915050565b5f5f8213156124df576001600160a01b0384165f9081526003602090815260408083208684529091529020546124d89083906001600160701b0316614664565b9050612537565b6001600160a01b0384165f9081526003602090815260408083208684529091529020546001600160701b03166125148361423a565b8111612520575f612533565b6125298361423a565b6125339082614677565b9150505b612540816136ae565b6001600160a01b03949094165f9081526003602090815260408083209583529490529290922080546001600160701b0319166001600160701b0390941693909317909255919050565b5f5f612594846128a8565b90505f6125a386868685612907565b5090505f5f5f5f5b85518110156127be576001600160a01b038a165f9081526004602090815260408083208c8452909152812087519091908890849081106125ed576125ed614580565b602002602001015181526020019081526020015f205485828151811061261557612615614580565b6020026020010151612627919061468a565b935084818151811061263b5761263b614580565b602002602001015160045f8c6001600160a01b03166001600160a01b031681526020019081526020015f205f8b81526020019081526020015f205f88848151811061268857612688614580565b602002602001015181526020019081526020015f20819055506126d28682815181106126b6576126b6614580565b60200260200101516001600160a01b0381169160a09190911c90565b80935081945050507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316631e3b06628b858589868151811061271e5761271e614580565b60209081029190910101516040516001600160e01b031960e087901b1681526001600160a01b039485166004820152939092166024840152604483015260648201526084810187905260a4016020604051808303815f875af1158015612786573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906127aa91906142f5565b6127b49088614664565b96506001016125ab565b506001600160a01b0389165f8181526003602090815260408083208c8452825280832080546001600160701b038c8116600160701b908102600160701b600160e01b03198416179093559585526002909352908320549104831692169082891061282d5750818803810161283e565b88830382111561283e575087820381035b612847816136ae565b6001600160a01b039c909c165f90815260026020526040902080546001600160701b0319166001600160701b03909d169c909c17909b5550959998505050505050505050565b5f825f1904841183021582026128a1575f5ffd5b5091020490565b5f818152600d60209081526040918290208054835181840281018401909452808452606093928301828280156128fb57602002820191905f5260205f20905b8154815260200190600101908083116128e7575b50505050509050919050565b606080835f0361293757604080516003808252608082019092529060208201606080368337019050509150612a47565b6129418684612a50565b6040805160038082526080820190925291925060a087901c919060208201606080368337019050505f828152600f602052604081205484519295506129e292600282810b936301000000840490910b92660100000000000090046001600160801b0316918791906129b4576129b4614580565b60200260200101515f0151866001815181106129d2576129d2614580565b60200260200101515f01516136c2565b845f815181106129f4576129f4614580565b6020026020010185600181518110612a0e57612a0e614580565b602090810291909101019190915252612a2681612363565b83600281518110612a3957612a39614580565b602002602001018181525050505b94509492505050565b80516060905f816001600160401b03811115612a6e57612a6e614332565b604051908082528060200260200182016040528015612a97578160200160208202803683370190505b5090505f826001600160401b03811115612ab357612ab3614332565b604051908082528060200260200182016040528015612adc578160200160208202803683370190505b5090505f836001600160401b03811115612af857612af8614332565b604051908082528060200260200182016040528015612b21578160200160208202803683370190505b5090505f5b84811015612bbf57612b438782815181106126b6576126b6614580565b858381518110612b5557612b55614580565b60200260200101858481518110612b6e57612b6e614580565b6020026020010182815250826001600160a01b03166001600160a01b03168152505050670de0b6b3a7640000828281518110612bac57612bac614580565b6020908102919091010152600101612b26565b5060405163cbdcbc3b60e01b81526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063cbdcbc3b90612c12908a908790879087906004016146ea565b5f60405180830381865afa158015612c2c573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f19168201604052612c5391908101906147bd565b979650505050505050565b5f5f5f5f612cb685600181518110612c7857612c78614580565b60200260200101515f0151670de0b6b3a764000088600181518110612c9f57612c9f614580565b602002602001015161288d9092919063ffffffff16565b612cf1865f81518110612ccb57612ccb614580565b60200260200101515f0151670de0b6b3a7640000895f81518110612c9f57612c9f614580565b612cfb9190614664565b90505f612d3b86600281518110612d1457612d14614580565b60200260200101515f0151670de0b6b3a764000089600281518110612c9f57612c9f614580565b9050612d478183614664565b9450845f03612d60575f5f5f9450945094505050612f21565b85600181518110612d7357612d73614580565b602002602001015160200151865f81518110612d9157612d91614580565b60200260200101516020015110612dc65785600181518110612db557612db5614580565b602002602001015160200151612de5565b855f81518110612dd857612dd8614580565b6020026020010151602001515b935085600181518110612dfa57612dfa614580565b602002602001015160400151865f81518110612e1857612e18614580565b60200260200101516040015110612e4d5785600181518110612e3c57612e3c614580565b602002602001015160400151612e6c565b855f81518110612e5f57612e5f614580565b6020026020010151604001515b92508486600281518110612e8257612e82614580565b60200260200101516020015182028584020181612ea157612ea1614896565b0493508486600281518110612eb857612eb8614580565b60200260200101516040015182028484020181612ed757612ed7614896565b6001600160a01b038a165f908152600260205260409020549190049350600160e01b900461ffff16612f0c818661271061288d565b9450612f1b818561271061288d565b93505050505b93509350939050565b5f5f5f5f612f37856128a8565b90505f612f4687878a85612907565b5090505f5f5f5f5f5f5b875181101561314f576001600160a01b038d165f9081526004602090815260408083208f8452909152812089519091908a9084908110612f9257612f92614580565b602002602001015181526020019081526020015f2054878281518110612fba57612fba614580565b6020026020010151612fcc919061468a565b9550868181518110612fe057612fe0614580565b602002602001015160045f8f6001600160a01b03166001600160a01b031681526020019081526020015f205f8e81526020019081526020015f205f8a848151811061302d5761302d614580565b602002602001015181526020019081526020015f208190555061305b8882815181106126b6576126b6614580565b80955081965050507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316636a6674a78e87878b86815181106130a7576130a7614580565b60209081029190910101516040516001600160e01b031960e087901b1681526001600160a01b039485166004820152939092166024840152604483015260648201526084810189905260a40160408051808303815f875af115801561310e573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061313291906148aa565b9093509150613141828b614664565b9a83019a9950600101612f50565b506001600160a01b038c165f8181526003602090815260408083208f8452825280832080546001600160701b038f8116600160701b908102600160701b600160e01b0319841617909355958552600290935292205491900482169116818b106131be57818b03810199506131d0565b8a82038111156131d0578a8203810399505b6001600160a01b038e165f90815260026020526040902054600160701b90046001600160701b03168a10613217576040516366b09a5960e01b815260040160405180910390fd5b5050506001600160a01b03909a165f90815260026020526040902080546001600160701b039097166001600160701b0319909716969096179095555094989397509295505050505050565b5f818152600760205260409020546001600160a01b0316806132b35760405162461bcd60e51b815260206004820152600a6024820152691393d517d3525395115160b21b60448201526064016109b2565b6001600160a01b0381165f81815260086020908152604080832080545f190190558583526007825280832080546001600160a01b031990811690915560099092528083208054909216909155518492907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef908390a45050565b5f60405163a9059cbb60e01b81526001600160a01b038416600482015282602482015260205f6044835f895af191505080601f3d1160015f5114161516156133765750823b153d17155b8061151d5760405162461bcd60e51b815260206004820152600f60248201526e1514905394d1915497d19052531151608a1b60448201526064016109b2565b5f826001600160a01b0316846001600160a01b0316106133d3575f5ffd5b6134d5856001600160a01b031663cefa77996040518163ffffffff1660e01b8152600401602060405180830381865afa158015613412573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906134369190614469565b604080516001600160a01b038089166020830152871691810191909152600285900b60608201526080016040516020818303038152906040528051906020012087604051733d602d80600a3d3981f3363d3d373d3d3d363d7360601b8152606093841b60148201526f5af43d82803e903d91602b57fd5bf3ff60801b6028820152921b6038830152604c8201526037808220606c830152605591012090565b95945050505050565b6134e882826136fc565b6001600160a01b0382163b158061358a5750604051630a85bd0160e11b8082523360048301525f6024830181905260448301849052608060648401526084830152906001600160a01b0384169063150b7a029060a4016020604051808303815f875af115801561355a573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061357e919061428c565b6001600160e01b031916145b6135a65760405162461bcd60e51b81526004016109b2906142a7565b5050565b6060815f036135d05750506040805180820190915260018152600360fc1b602082015290565b815f5b81156135f957806135e3816148cc565b91506135f29050600a836148e4565b91506135d3565b5f816001600160401b0381111561361257613612614332565b6040519080825280601f01601f19166020018201604052801561363c576020820181803683370190505b5090505b84156136a657613651600183614677565b915061365e600a866148f7565b613669906030614664565b60f81b81838151811061367e5761367e614580565b60200101906001600160f81b03191690815f1a90535061369f600a866148e4565b9450613640565b949350505050565b5f600160701b82106136be575f5ffd5b5090565b5f5f5f6136cf8585613805565b90506136ed816136de8a613862565b6136e78a613862565b89613b90565b90999098509650505050505050565b6001600160a01b0382166137465760405162461bcd60e51b81526020600482015260116024820152701253959053125117d49150d25412515395607a1b60448201526064016109b2565b5f818152600760205260409020546001600160a01b03161561379b5760405162461bcd60e51b815260206004820152600e60248201526d1053149150511657d3525395115160921b60448201526064016109b2565b6001600160a01b0382165f81815260086020908152604080832080546001019055848352600790915280822080546001600160a01b0319168417905551839291907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef908290a45050565b5f815f03613828575073fffd8963efd1fc6a506488495d951d5263988d2661081e565b5f613840846b204fce5e3e250261100000008561288d565b90505f61384c82613c2a565b90506134d5655af3107a4000606083901b6148e4565b5f5f5f8360020b12613877578260020b61387e565b8260020b5f035b9050620d89e88111156138b75760405162461bcd60e51b81526020600482015260016024820152601560fa1b60448201526064016109b2565b5f816001165f036138cc57600160801b6138de565b6ffffcb933bd6fad37aa2d162d1a5940015b70ffffffffffffffffffffffffffffffffff1690506002821615613912576ffff97272373d413259a46990580e213a0260801c5b6004821615613931576ffff2e50f5f656932ef12357cf3c7fdcc0260801c5b6008821615613950576fffe5caca7e10e4e61c3624eaa0941cd00260801c5b601082161561396f576fffcb9843d60f6159c9db58835c9266440260801c5b602082161561398e576fff973b41fa98c081472e6896dfb254c00260801c5b60408216156139ad576fff2ea16466c96a3843ec78b326b528610260801c5b60808216156139cc576ffe5dee046a99a2a811c461f1969c30530260801c5b6101008216156139ec576ffcbe86c7900a88aedcffc83b479aa3a40260801c5b610200821615613a0c576ff987a7253ac413176f2b074cf7815e540260801c5b610400821615613a2c576ff3392b0822b70005940c7a398e4b70f30260801c5b610800821615613a4c576fe7159475a2c29b7443b29c7fa6e889d90260801c5b611000821615613a6c576fd097f3bdfd2022b8845ad8f792aa58250260801c5b612000821615613a8c576fa9f746462d870fdf8a65dc1f90e061e50260801c5b614000821615613aac576f70d869a156d2a1b890bb3df62baf32f70260801c5b618000821615613acc576f31be135f97d08fd981231505542fcfa60260801c5b62010000821615613aed576f09aa508b5b7a84e1c677de54f3e99bc90260801c5b62020000821615613b0d576e5d6af8dedb81196699c329225ee6040260801c5b62040000821615613b2c576d2216e584f5fa1ea926041bedfe980260801c5b62080000821615613b49576b048a170391f7dc42444e8fa20260801c5b5f8460020b1315613b6857805f1981613b6457613b64614896565b0490505b640100000000810615613b7c576001613b7e565b5f5b60ff16602082901c0192505050919050565b5f5f836001600160a01b0316856001600160a01b03161115613bb0579293925b846001600160a01b0316866001600160a01b031611613bdb57613bd4858585613cce565b9150612a47565b836001600160a01b0316866001600160a01b03161015613c1457613c00868585613cce565b9150613c0d858785613d3e565b9050612a47565b613c1f858585613d3e565b905094509492505050565b60b581600160881b8110613c435760409190911b9060801c5b69010000000000000000008110613c5f5760209190911b9060401c5b650100000000008110613c775760109190911b9060201c5b63010000008110613c8d5760089190911b9060101c5b62010000010260121c80820401600190811c80830401811c80830401811c80830401811c80830401811c80830401811c80830401901c908190048111900390565b5f826001600160a01b0316846001600160a01b03161115613ced579192915b836001600160a01b0316613d26606060ff16846001600160801b0316901b8686036001600160a01b0316866001600160a01b0316613d7c565b81613d3357613d33614896565b0490505b9392505050565b5f826001600160a01b0316846001600160a01b03161115613d5d579192915b6136a6826001600160801b03168585036001600160a01b0316600160601b5b5f80805f19858709858702925082811083820303915050805f03613db0575f8411613da5575f5ffd5b508290049050613d37565b808411613dbb575f5ffd5b5f8486880960026001871981018816978890046003810283188082028403028082028403028082028403028082028403028082028403029081029092039091025f889003889004909101858311909403939093029303949094049190911702949350505050565b828054828255905f5260205f20908101928215613e5b579160200282015b82811115613e5b578251825591602001919060010190613e40565b506136be9291505b808211156136be575f8155600101613e63565b6001600160e01b031981168114613e8b575f5ffd5b50565b5f60208284031215613e9e575f5ffd5b8135613d3781613e76565b6001600160a01b0381168114613e8b575f5ffd5b5f5f5f5f60808587031215613ed0575f5ffd5b8435613edb81613ea9565b93506020850135613eeb81613ea9565b93969395505050506040820135916060013590565b602081525f82518060208401528060208501604085015e5f604082850101526040601f19601f83011684010191505092915050565b5f60208284031215613f45575f5ffd5b5035919050565b5f5f60408385031215613f5d575f5ffd5b8235613f6881613ea9565b946020939093013593505050565b5f5f83601f840112613f86575f5ffd5b5081356001600160401b03811115613f9c575f5ffd5b602083019150836020828501011115613fb3575f5ffd5b9250929050565b5f5f5f5f5f60808688031215613fce575f5ffd5b8535613fd981613ea9565b94506020860135613fe981613ea9565b93506040860135925060608601356001600160401b0381111561400a575f5ffd5b61401688828901613f76565b969995985093965092949392505050565b5f60208284031215614037575f5ffd5b8135613d3781613ea9565b5f5f5f5f5f60a08688031215614056575f5ffd5b853561406181613ea9565b9450602086013561407181613ea9565b94979496505050506040830135926060810135926080909101359150565b5f5f5f606084860312156140a1575f5ffd5b83356140ac81613ea9565b925060208401356140bc81613ea9565b929592945050506040919091013590565b5f5f5f606084860312156140df575f5ffd5b83356140ea81613ea9565b95602085013595506040909401359392505050565b5f5f60408385031215614110575f5ffd5b50508035926020909101359150565b5f5f60208385031215614130575f5ffd5b82356001600160401b03811115614145575f5ffd5b61415185828601613f76565b90969095509350505050565b5f5f5f6060848603121561416f575f5ffd5b833561417a81613ea9565b925060208401356001600160701b0381168114614195575f5ffd5b9150604084013561ffff811681146141ab575f5ffd5b809150509250925092565b8015158114613e8b575f5ffd5b5f5f604083850312156141d4575f5ffd5b82356141df81613ea9565b915060208301356141ef816141b6565b809150509250929050565b5f5f6040838503121561420b575f5ffd5b823561421681613ea9565b915060208301356141ef81613ea9565b634e487b7160e01b5f52601160045260245ffd5b5f600160ff1b820161424e5761424e614226565b505f0390565b600181811c9082168061426857607f821691505b60208210810361428657634e487b7160e01b5f52602260045260245ffd5b50919050565b5f6020828403121561429c575f5ffd5b8151613d3781613e76565b60208082526010908201526f155394d0519157d49150d2541251539560821b604082015260600190565b6020808252600a90820152695245454e5452414e435960b01b604082015260600190565b5f60208284031215614305575f5ffd5b5051919050565b6020808252600c908201526b15539055551213d49256915160a21b604082015260600190565b634e487b7160e01b5f52604160045260245ffd5b601f821115610e8457805f5260205f20601f840160051c8101602085101561436b5750805b601f840160051c820191505b8181101561227d575f8155600101614377565b6001600160401b038311156143a1576143a1614332565b6143b5836143af8354614254565b83614346565b5f601f8411600181146143e6575f85156143cf5750838201355b5f19600387901b1c1916600186901b17835561227d565b5f83815260208120601f198716915b8281101561441557868501358255602094850194600190920191016143f5565b5086821015614431575f1960f88860031b161c19848701351681555b505060018560011b0183555050505050565b5f60208284031215614453575f5ffd5b8151613d37816141b6565b805161122781613ea9565b5f60208284031215614479575f5ffd5b8151613d3781613ea9565b8051600281900b8114611227575f5ffd5b80516001600160801b0381168114611227575f5ffd5b5f5f5f5f5f5f5f5f5f5f5f5f6101808d8f0312156144c7575f5ffd5b8c516001600160601b03811681146144dd575f5ffd5b9b506144eb60208e0161445e565b9a506144f960408e0161445e565b995061450760608e0161445e565b985061451560808e01614484565b975061452360a08e01614484565b965061453160c08e01614484565b955061453f60e08e01614495565b6101008e01516101208f01519196509450925061455f6101408e01614495565b915061456e6101608e01614495565b90509295989b509295989b509295989b565b634e487b7160e01b5f52603260045260245ffd5b6001600160a01b03868116825285166020820152604081018490526080606082018190528101829052818360a08301375f81830160a090810191909152601f909201601f19160101949350505050565b5f5f84546145f181614254565b600182168015614608576001811461461d5761464a565b60ff198316865281151582028601935061464a565b875f5260205f205f5b8381101561464257815488820152600190910190602001614626565b505081860193505b50505083518060208601835e5f9101908152949350505050565b8082018082111561081e5761081e614226565b8181038181111561081e5761081e614226565b8181035f8312801583831316838312821617156146a9576146a9614226565b5092915050565b5f8151808452602084019350602083015f5b828110156146e05781518652602095860195909101906001016146c2565b5093949350505050565b6001600160a01b03851681526080602080830182905285519183018290525f919086019060a0840190835b8181101561473c5783516001600160a01b0316835260209384019390920191600101614715565b5050838103604085015261475081876146b0565b9150508281036060840152612c5381856146b0565b604051606081016001600160401b038111828210171561478757614787614332565b60405290565b604051601f8201601f191681016001600160401b03811182821017156147b5576147b5614332565b604052919050565b5f602082840312156147cd575f5ffd5b81516001600160401b038111156147e2575f5ffd5b8201601f810184136147f2575f5ffd5b80516001600160401b0381111561480b5761480b614332565b61481a60208260051b0161478d565b8082825260208201915060206060840285010192508683111561483b575f5ffd5b6020840193505b8284101561488c5760608488031215614859575f5ffd5b614861614765565b8451815260208086015181830152604080870151908301529083526060909401939190910190614842565b9695505050505050565b634e487b7160e01b5f52601260045260245ffd5b5f5f604083850312156148bb575f5ffd5b505080516020909101519092909150565b5f600182016148dd576148dd614226565b5060010190565b5f826148f2576148f2614896565b500490565b5f8261490557614905614896565b50069056fea26469706673582212200cc02de70e7c562e3ba4c105126a2b7cf8240fc73dd56e3fda8380468e52bcf864736f6c634300081e003341726361646961205374616b656420536c697073747265616d20506f736974696f6e73000000000000000000000000b4d72b1c91e640e4ed7d7397f3244de4d8acc50b000000000000000000000000d0690557600eb8be8391d1d97346e2aab5300d5f000000000000000000000000991d5546c4b442b4c5fdc4c8b8b8d131deb2470200000000000000000000000097cdbce21b6fd0585d29e539b1b99dad328a11230000000000000000000000007f9adfbd38b669f03d1d11000bc76b9aaea28a81

Deployed Bytecode

0x608060405234801561000f575f5ffd5b5060043610610255575f3560e01c806370a0823111610140578063a0712d68116100bf578063ba9270ff11610084578063ba9270ff14610747578063c87b56dd1461075a578063daa3770c1461076d578063e985e9c514610780578063f2fde38b146107ad578063f8350ed0146107c0575f5ffd5b8063a0712d68146106d3578063a22cb465146106e6578063aa3f22b8146106f9578063ae169a5014610721578063b88d4fde14610734575f5ffd5b80638815eb39116101055780638815eb39146106585780638da5cb5b1461067f57806395d89b411461069157806399248ea7146106995780639da882ac146106c0575f5ffd5b806370a082311461053957806373aecb981461054c5780638129fc1c1461055f57806382d6b1661461056757806384b2fc50146105c5575f5ffd5b80632341eb86116101d757806342966c681161019c57806342966c681461045f57806349bccc5d1461047257806353b85a50146104a057806355f804b31461050b5780636352211e1461051e5780636c0360eb14610531575f5ffd5b80632341eb86146103ce57806323b872dd146103f65780633a8fe5e7146104095780633c0ac4f61461043957806342842e0e1461044c575f5ffd5b8063095ea7b31161021d578063095ea7b314610312578063150b7a021461032557806315ed93bc1461035d578063172350931461037f5780631ba0408c146103a0575f5ffd5b806301ffc9a714610259578063024874c71461028157806306433b1b1461029657806306fdde03146102d5578063081812fc146102ea575b5f5ffd5b61026c610267366004613e8e565b6107d3565b60405190151581526020015b60405180910390f35b61029461028f366004613ebd565b610824565b005b6102bd7f000000000000000000000000d0690557600eb8be8391d1d97346e2aab5300d5f81565b6040516001600160a01b039091168152602001610278565b6102dd6108a6565b6040516102789190613f00565b6102bd6102f8366004613f35565b60096020525f90815260409020546001600160a01b031681565b610294610320366004613f4c565b610932565b610344610333366004613fba565b630a85bd0160e11b95945050505050565b6040516001600160e01b03199091168152602001610278565b61026c61036b366004614027565b60016020525f908152604090205460ff1681565b61039261038d366004614042565b610a16565b604051908152602001610278565b6103b36103ae36600461408f565b610abd565b6040805161ffff938416815292909116602083015201610278565b6103e16103dc366004614042565b610b18565b60408051928352602083019190915201610278565b61029461040436600461408f565b610bcb565b6103926104173660046140cd565b600460209081525f938452604080852082529284528284209052825290205481565b6103926104473660046140ff565b610d8d565b61029461045a36600461408f565b610db8565b61039261046d366004613f35565b610e89565b610485610480366004613ebd565b61113c565b60408051938452602084019290925290820152606001610278565b6104e16104ae366004614027565b60026020525f90815260409020546001600160701b0380821691600160701b810490911690600160e01b900461ffff1683565b604080516001600160701b03948516815293909216602084015261ffff1690820152606001610278565b61029461051936600461411f565b6111a0565b6102bd61052c366004613f35565b6111d6565b6102dd61122c565b610392610547366004614027565b611239565b61029461055a36600461415d565b61129a565b61029461136d565b6105a5610575366004613f4c565b600360209081525f92835260408084209091529082529020546001600160701b0380821691600160701b90041682565b604080516001600160701b03938416815292909116602083015201610278565b61061a6105d3366004613f35565b600f6020525f908152604090208054600190910154600282810b926301000000810490910b9166010000000000009091046001600160801b0316906001600160a01b031684565b60408051600295860b81529390940b60208401526001600160801b03909116928201929092526001600160a01b039091166060820152608001610278565b6103927f000000000000000000000000000000000000000000000000000000000000000281565b5f546102bd906001600160a01b031681565b6102dd611523565b6102bd7f0000000000000000000000007f9adfbd38b669f03d1d11000bc76b9aaea28a8181565b6102946106ce366004614027565b611530565b6103926106e1366004613f35565b611975565b6102946106f43660046141c3565b611f5b565b6102bd610707366004614027565b600e6020525f90815260409020546001600160a01b031681565b61039261072f366004613f35565b611fc6565b610294610742366004613fba565b6121c2565b610392610755366004613ebd565b612284565b6102dd610768366004613f35565b612307565b61039261077b366004613f35565b612363565b61026c61078e3660046141fa565b600a60209081525f928352604080842090915290825290205460ff1681565b6102946107bb366004614027565b6123e2565b61026c6107ce366004613f4c565b612455565b5f6301ffc9a760e01b6001600160e01b03198316148061080357506380ac58cd60e01b6001600160e01b03198316145b8061081e5750635b5e139f60e01b6001600160e01b03198316145b92915050565b336001600160a01b037f000000000000000000000000d0690557600eb8be8391d1d97346e2aab5300d5f161461086d576040516310f5403960e31b815260040160405180910390fd5b5f61087a848460a01b1790565b90505f610890868361088b8661423a565b612498565b905061089d868383612589565b50505050505050565b600580546108b390614254565b80601f01602080910402602001604051908101604052809291908181526020018280546108df90614254565b801561092a5780601f106109015761010080835404028352916020019161092a565b820191905f5260205f20905b81548152906001019060200180831161090d57829003601f168201915b505050505081565b5f818152600760205260409020546001600160a01b03163381148061097957506001600160a01b0381165f908152600a6020908152604080832033845290915290205460ff165b6109bb5760405162461bcd60e51b815260206004820152600e60248201526d1393d517d055551213d49256915160921b60448201526064015b60405180910390fd5b5f8281526009602052604080822080546001600160a01b0319166001600160a01b0387811691821790925591518593918516917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92591a4505050565b5f336001600160a01b037f000000000000000000000000d0690557600eb8be8391d1d97346e2aab5300d5f1614610a60576040516310f5403960e31b815260040160405180910390fd5b5f610a6d868660a01b1790565b90505f610a7b888386612498565b90505f610a89898484612589565b9050811580610a96575080155b15610aa3575f9350610ab1565b610aae81878461288d565b93505b50505095945050505050565b5f5f5f610acc858560a01b1790565b90505f610ad8826128a8565b90505f610ae88884600185612907565b5090505f610af68984612a50565b90505f5f610b058b8585612c5e565b909d909c509a5050505050505050505050565b5f80336001600160a01b037f000000000000000000000000d0690557600eb8be8391d1d97346e2aab5300d5f1614610b63576040516310f5403960e31b815260040160405180910390fd5b5f610b70878760a01b1790565b90505f610b7e898387612498565b90505f5f610b8d838c86612f2a565b91509150825f1480610b9d575080155b15610baa575f9450610bb8565b610bb581898561288d565b94505b5060010199929850919650505050505050565b5f818152600760205260409020546001600160a01b03848116911614610c205760405162461bcd60e51b815260206004820152600a60248201526957524f4e475f46524f4d60b01b60448201526064016109b2565b6001600160a01b038216610c6a5760405162461bcd60e51b81526020600482015260116024820152701253959053125117d49150d25412515395607a1b60448201526064016109b2565b336001600160a01b0384161480610ca357506001600160a01b0383165f908152600a6020908152604080832033845290915290205460ff165b80610cc357505f818152600960205260409020546001600160a01b031633145b610d005760405162461bcd60e51b815260206004820152600e60248201526d1393d517d055551213d49256915160921b60448201526064016109b2565b6001600160a01b038084165f81815260086020908152604080832080545f19019055938616808352848320805460010190558583526007825284832080546001600160a01b03199081168317909155600990925284832080549092169091559251849392917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef91a4505050565b600d602052815f5260405f208181548110610da6575f80fd5b905f5260205f20015f91509150505481565b610dc3838383610bcb565b6001600160a01b0382163b1580610e685750604051630a85bd0160e11b8082523360048301526001600160a01b03858116602484015260448301849052608060648401525f608484015290919084169063150b7a029060a4016020604051808303815f875af1158015610e38573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610e5c919061428c565b6001600160e01b031916145b610e845760405162461bcd60e51b81526004016109b2906142a7565b505050565b5f600b54600114610eac5760405162461bcd60e51b81526004016109b2906142d1565b6002600b555f828152600760205260409020546001600160a01b03163314610ee7576040516330cd747160e01b815260040160405180910390fd5b5f828152600f602052604090819020600101549051632e1a7d4d60e01b8152600481018490526001600160a01b0390911690632e1a7d4d906024015f604051808303815f87803b158015610f39575f5ffd5b505af1158015610f4b573d5f5f3e3d5ffd5b50506040516370a0823160e01b81523060048201527f0000000000000000000000007f9adfbd38b669f03d1d11000bc76b9aaea28a816001600160a01b031692506370a082319150602401602060405180830381865afa158015610fb1573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610fd591906142f5565b5f838152600f6020526040902080546001600160b01b031916815560010180546001600160a01b0319169055905061100c82613262565b80156110b0576110466001600160a01b037f0000000000000000000000007f9adfbd38b669f03d1d11000bc76b9aaea28a8116338361332c565b6040516001600160801b03821681526001600160a01b037f0000000000000000000000007f9adfbd38b669f03d1d11000bc76b9aaea28a81169083907f99eb7ece63bef2ec97b991da7cf3763c7c533b1614782a4afd1b7b028db3189f9060200160405180910390a35b604051632142170760e11b8152306004820152336024820152604481018390527f000000000000000000000000991d5546c4b442b4c5fdc4c8b8b8d131deb247026001600160a01b0316906342842e0e906064015f604051808303815f87803b15801561111b575f5ffd5b505af115801561112d573d5f5f3e3d5ffd5b50506001600b55509092915050565b5f5f5f5f61114c878760a01b1790565b90505f611158826128a8565b90505f5f6111688b858a86612907565b9150915080515f036111815761117e8b84612a50565b90505b61118c8b8383612c5e565b919d909c50909a5098505050505050505050565b5f546001600160a01b031633146111c95760405162461bcd60e51b81526004016109b29061430c565b600c610e8482848361438a565b5f818152600760205260409020546001600160a01b0316806112275760405162461bcd60e51b815260206004820152600a6024820152691393d517d3525395115160b21b60448201526064016109b2565b919050565b600c80546108b390614254565b5f6001600160a01b03821661127f5760405162461bcd60e51b815260206004820152600c60248201526b5a45524f5f4144445245535360a01b60448201526064016109b2565b506001600160a01b03165f9081526008602052604090205490565b336001600160a01b037f000000000000000000000000d0690557600eb8be8391d1d97346e2aab5300d5f16146112e3576040516310f5403960e31b815260040160405180910390fd5b6127108161ffff16111561130a57604051630b1c8e2560e01b815260040160405180910390fd5b6001600160a01b039092165f908152600260205260409020805461ffff909316600160e01b0261ffff60e01b196001600160701b03909316600160701b02929092166fffffffffffffffffffffffffffffffff60701b1990931692909217179055565b5f546001600160a01b031633146113965760405162461bcd60e51b81526004016109b29061430c565b604051630f8350ed60e41b81526001600160a01b037f0000000000000000000000007f9adfbd38b669f03d1d11000bc76b9aaea28a81811660048301525f60248301527f000000000000000000000000d0690557600eb8be8391d1d97346e2aab5300d5f169063f8350ed090604401602060405180830381865afa158015611420573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906114449190614443565b6114615760405163c52db36360e01b815260040160405180910390fd5b305f81815260016020819052604091829020805460ff191690911790555163f1751c7b60e01b81526001600160601b037f000000000000000000000000000000000000000000000000000000000000000216600482015260248101919091526001600160a01b037f000000000000000000000000d0690557600eb8be8391d1d97346e2aab5300d5f169063f1751c7b906044015f604051808303815f87803b15801561150b575f5ffd5b505af115801561151d573d5f5f3e3d5ffd5b50505050565b600680546108b390614254565b5f546001600160a01b031633146115595760405162461bcd60e51b81526004016109b29061430c565b60405163aa79979b60e01b81526001600160a01b0382811660048301527f00000000000000000000000097cdbce21b6fd0585d29e539b1b99dad328a1123169063aa79979b90602401602060405180830381865afa1580156115bd573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906115e19190614443565b6115fe5760405163e1a93ebd60e01b815260040160405180910390fd5b7f0000000000000000000000007f9adfbd38b669f03d1d11000bc76b9aaea28a816001600160a01b0316816001600160a01b031663f7c618c16040518163ffffffff1660e01b81526004016020604051808303815f875af1158015611665573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906116899190614469565b6001600160a01b0316146116b057604051633616e34b60e11b815260040160405180910390fd5b5f816001600160a01b03166316f0115b6040518163ffffffff1660e01b81526004016020604051808303815f875af11580156116ee573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906117129190614469565b90507f000000000000000000000000d0690557600eb8be8391d1d97346e2aab5300d5f6001600160a01b031663f8350ed0826001600160a01b0316630dfe16816040518163ffffffff1660e01b81526004016020604051808303815f875af1158015611780573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906117a49190614469565b6040516001600160e01b031960e084901b1681526001600160a01b0390911660048201525f6024820152604401602060405180830381865afa1580156117ec573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906118109190614443565b61182d57604051634847234360e01b815260040160405180910390fd5b7f000000000000000000000000d0690557600eb8be8391d1d97346e2aab5300d5f6001600160a01b031663f8350ed0826001600160a01b031663d21220a76040518163ffffffff1660e01b81526004016020604051808303815f875af1158015611899573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906118bd9190614469565b6040516001600160e01b031960e084901b1681526001600160a01b0390911660048201525f6024820152604401602060405180830381865afa158015611905573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906119299190614443565b61194657604051634847234360e01b815260040160405180910390fd5b6001600160a01b039081165f908152600e602052604090208054919092166001600160a01b0319909116179055565b5f600b546001146119985760405162461bcd60e51b81526004016109b2906142d1565b6002600b556001600160601b038211156119c557604051631bf4348160e31b815260040160405180910390fd5b604051632142170760e11b8152336004820152306024820152604481018390527f000000000000000000000000991d5546c4b442b4c5fdc4c8b8b8d131deb247026001600160a01b0316906342842e0e906064015f604051808303815f87803b158015611a30575f5ffd5b505af1158015611a42573d5f5f3e3d5ffd5b505050505f5f5f5f5f5f7f000000000000000000000000991d5546c4b442b4c5fdc4c8b8b8d131deb247026001600160a01b03166399fbab88896040518263ffffffff1660e01b8152600401611a9a91815260200190565b61018060405180830381865afa158015611ab6573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611ada91906144ab565b505050509750975097509750975097505050806001600160801b03165f03611b1557604051630200e8a960e31b815260040160405180910390fd5b5f600e5f611b457f00000000000000000000000004625b046c69577efc40e6c0bb83cdbafab5a55f8a8a8a6133b5565b6001600160a01b03166001600160a01b031681526020019081526020015f205f9054906101000a90046001600160a01b0316905060405180608001604052808560020b81526020018460020b8152602001836001600160801b03168152602001826001600160a01b0316815250600f5f8b81526020019081526020015f205f820151815f015f6101000a81548162ffffff021916908360020b62ffffff1602179055506020820151815f0160036101000a81548162ffffff021916908360020b62ffffff1602179055506040820151815f0160066101000a8154816001600160801b0302191690836001600160801b031602179055506060820151816001015f6101000a8154816001600160a01b0302191690836001600160a01b031602179055509050505f60036001600160401b03811115611c8457611c84614332565b604051908082528060200260200182016040528015611cad578160200160208202803683370190505b50905087815f81518110611cc357611cc3614580565b60209081029190910101528681600181518110611ce257611ce2614580565b60209081029190910101527f0000000000000000000000007f9adfbd38b669f03d1d11000bc76b9aaea28a8181600281518110611d2157611d21614580565b60200260200101818152505080600d5f611d3d308e60a01b1790565b81526020019081526020015f209080519060200190611d5d929190613e22565b5060405163095ea7b360e01b81526001600160a01b038381166004830152602482018c90527f000000000000000000000000991d5546c4b442b4c5fdc4c8b8b8d131deb24702169063095ea7b3906044015f604051808303815f87803b158015611dc5575f5ffd5b505af1158015611dd7573d5f5f3e3d5ffd5b505060405163b6b55f2560e01b8152600481018d90526001600160a01b038516925063b6b55f2591506024015f604051808303815f87803b158015611e1a575f5ffd5b505af1158015611e2c573d5f5f3e3d5ffd5b50506040516370a0823160e01b81523060048201525f92506001600160a01b038b1691506370a0823190602401602060405180830381865afa158015611e74573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611e9891906142f5565b6040516370a0823160e01b81523060048201529091505f906001600160a01b038a16906370a0823190602401602060405180830381865afa158015611edf573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611f0391906142f5565b90508115611f1f57611f1f6001600160a01b038b16338461332c565b8015611f3957611f396001600160a01b038a16338361332c565b8b9a50611f46338c6134de565b50506001600b55509698975050505050505050565b335f818152600a602090815260408083206001600160a01b03871680855290835292819020805460ff191686151590811790915590519081529192917f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31910160405180910390a35050565b5f600b54600114611fe95760405162461bcd60e51b81526004016109b2906142d1565b6002600b555f828152600760205260409020546001600160a01b03163314612024576040516330cd747160e01b815260040160405180910390fd5b5f828152600f602052604090819020600101549051631c4b774b60e01b8152600481018490526001600160a01b0390911690631c4b774b906024015f604051808303815f87803b158015612076575f5ffd5b505af1158015612088573d5f5f3e3d5ffd5b50506040516370a0823160e01b81523060048201527f0000000000000000000000007f9adfbd38b669f03d1d11000bc76b9aaea28a816001600160a01b031692506370a082319150602401602060405180830381865afa1580156120ee573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061211291906142f5565b905080156121b85761214e6001600160a01b037f0000000000000000000000007f9adfbd38b669f03d1d11000bc76b9aaea28a8116338361332c565b6040516001600160801b03821681526001600160a01b037f0000000000000000000000007f9adfbd38b669f03d1d11000bc76b9aaea28a81169083907f99eb7ece63bef2ec97b991da7cf3763c7c533b1614782a4afd1b7b028db3189f9060200160405180910390a35b6001600b55919050565b6121cd858585610bcb565b6001600160a01b0384163b15806122615750604051630a85bd0160e11b808252906001600160a01b0386169063150b7a02906122159033908a90899089908990600401614594565b6020604051808303815f875af1158015612231573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612255919061428c565b6001600160e01b031916145b61227d5760405162461bcd60e51b81526004016109b2906142a7565b5050505050565b5f336001600160a01b037f000000000000000000000000d0690557600eb8be8391d1d97346e2aab5300d5f16146122ce576040516310f5403960e31b815260040160405180910390fd5b5f6122db858560a01b1790565b90505f6122e9878386612498565b90505f6122f7828985612f2a565b5060010198975050505050505050565b60605f600c805461231790614254565b9050116123325760405180602001604052805f81525061081e565b600c61233d836135aa565b60405160200161234e9291906145e4565b60405160208183030381529060405292915050565b5f818152600f6020526040808220600101549051633e491d4760e01b8152306004820152602481018490526001600160a01b0390911690633e491d4790604401602060405180830381865afa1580156123be573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061081e91906142f5565b5f546001600160a01b0316331461240b5760405162461bcd60e51b81526004016109b29061430c565b5f80546001600160a01b0319166001600160a01b0383169081178255604051909133917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a350565b5f6001600160a01b0383163014801561248357505f828152600760205260409020546001600160a01b031615155b156124905750600161081e565b505f92915050565b5f5f8213156124df576001600160a01b0384165f9081526003602090815260408083208684529091529020546124d89083906001600160701b0316614664565b9050612537565b6001600160a01b0384165f9081526003602090815260408083208684529091529020546001600160701b03166125148361423a565b8111612520575f612533565b6125298361423a565b6125339082614677565b9150505b612540816136ae565b6001600160a01b03949094165f9081526003602090815260408083209583529490529290922080546001600160701b0319166001600160701b0390941693909317909255919050565b5f5f612594846128a8565b90505f6125a386868685612907565b5090505f5f5f5f5b85518110156127be576001600160a01b038a165f9081526004602090815260408083208c8452909152812087519091908890849081106125ed576125ed614580565b602002602001015181526020019081526020015f205485828151811061261557612615614580565b6020026020010151612627919061468a565b935084818151811061263b5761263b614580565b602002602001015160045f8c6001600160a01b03166001600160a01b031681526020019081526020015f205f8b81526020019081526020015f205f88848151811061268857612688614580565b602002602001015181526020019081526020015f20819055506126d28682815181106126b6576126b6614580565b60200260200101516001600160a01b0381169160a09190911c90565b80935081945050507f000000000000000000000000d0690557600eb8be8391d1d97346e2aab5300d5f6001600160a01b0316631e3b06628b858589868151811061271e5761271e614580565b60209081029190910101516040516001600160e01b031960e087901b1681526001600160a01b039485166004820152939092166024840152604483015260648201526084810187905260a4016020604051808303815f875af1158015612786573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906127aa91906142f5565b6127b49088614664565b96506001016125ab565b506001600160a01b0389165f8181526003602090815260408083208c8452825280832080546001600160701b038c8116600160701b908102600160701b600160e01b03198416179093559585526002909352908320549104831692169082891061282d5750818803810161283e565b88830382111561283e575087820381035b612847816136ae565b6001600160a01b039c909c165f90815260026020526040902080546001600160701b0319166001600160701b03909d169c909c17909b5550959998505050505050505050565b5f825f1904841183021582026128a1575f5ffd5b5091020490565b5f818152600d60209081526040918290208054835181840281018401909452808452606093928301828280156128fb57602002820191905f5260205f20905b8154815260200190600101908083116128e7575b50505050509050919050565b606080835f0361293757604080516003808252608082019092529060208201606080368337019050509150612a47565b6129418684612a50565b6040805160038082526080820190925291925060a087901c919060208201606080368337019050505f828152600f602052604081205484519295506129e292600282810b936301000000840490910b92660100000000000090046001600160801b0316918791906129b4576129b4614580565b60200260200101515f0151866001815181106129d2576129d2614580565b60200260200101515f01516136c2565b845f815181106129f4576129f4614580565b6020026020010185600181518110612a0e57612a0e614580565b602090810291909101019190915252612a2681612363565b83600281518110612a3957612a39614580565b602002602001018181525050505b94509492505050565b80516060905f816001600160401b03811115612a6e57612a6e614332565b604051908082528060200260200182016040528015612a97578160200160208202803683370190505b5090505f826001600160401b03811115612ab357612ab3614332565b604051908082528060200260200182016040528015612adc578160200160208202803683370190505b5090505f836001600160401b03811115612af857612af8614332565b604051908082528060200260200182016040528015612b21578160200160208202803683370190505b5090505f5b84811015612bbf57612b438782815181106126b6576126b6614580565b858381518110612b5557612b55614580565b60200260200101858481518110612b6e57612b6e614580565b6020026020010182815250826001600160a01b03166001600160a01b03168152505050670de0b6b3a7640000828281518110612bac57612bac614580565b6020908102919091010152600101612b26565b5060405163cbdcbc3b60e01b81526001600160a01b037f000000000000000000000000d0690557600eb8be8391d1d97346e2aab5300d5f169063cbdcbc3b90612c12908a908790879087906004016146ea565b5f60405180830381865afa158015612c2c573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f19168201604052612c5391908101906147bd565b979650505050505050565b5f5f5f5f612cb685600181518110612c7857612c78614580565b60200260200101515f0151670de0b6b3a764000088600181518110612c9f57612c9f614580565b602002602001015161288d9092919063ffffffff16565b612cf1865f81518110612ccb57612ccb614580565b60200260200101515f0151670de0b6b3a7640000895f81518110612c9f57612c9f614580565b612cfb9190614664565b90505f612d3b86600281518110612d1457612d14614580565b60200260200101515f0151670de0b6b3a764000089600281518110612c9f57612c9f614580565b9050612d478183614664565b9450845f03612d60575f5f5f9450945094505050612f21565b85600181518110612d7357612d73614580565b602002602001015160200151865f81518110612d9157612d91614580565b60200260200101516020015110612dc65785600181518110612db557612db5614580565b602002602001015160200151612de5565b855f81518110612dd857612dd8614580565b6020026020010151602001515b935085600181518110612dfa57612dfa614580565b602002602001015160400151865f81518110612e1857612e18614580565b60200260200101516040015110612e4d5785600181518110612e3c57612e3c614580565b602002602001015160400151612e6c565b855f81518110612e5f57612e5f614580565b6020026020010151604001515b92508486600281518110612e8257612e82614580565b60200260200101516020015182028584020181612ea157612ea1614896565b0493508486600281518110612eb857612eb8614580565b60200260200101516040015182028484020181612ed757612ed7614896565b6001600160a01b038a165f908152600260205260409020549190049350600160e01b900461ffff16612f0c818661271061288d565b9450612f1b818561271061288d565b93505050505b93509350939050565b5f5f5f5f612f37856128a8565b90505f612f4687878a85612907565b5090505f5f5f5f5f5f5b875181101561314f576001600160a01b038d165f9081526004602090815260408083208f8452909152812089519091908a9084908110612f9257612f92614580565b602002602001015181526020019081526020015f2054878281518110612fba57612fba614580565b6020026020010151612fcc919061468a565b9550868181518110612fe057612fe0614580565b602002602001015160045f8f6001600160a01b03166001600160a01b031681526020019081526020015f205f8e81526020019081526020015f205f8a848151811061302d5761302d614580565b602002602001015181526020019081526020015f208190555061305b8882815181106126b6576126b6614580565b80955081965050507f000000000000000000000000d0690557600eb8be8391d1d97346e2aab5300d5f6001600160a01b0316636a6674a78e87878b86815181106130a7576130a7614580565b60209081029190910101516040516001600160e01b031960e087901b1681526001600160a01b039485166004820152939092166024840152604483015260648201526084810189905260a40160408051808303815f875af115801561310e573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061313291906148aa565b9093509150613141828b614664565b9a83019a9950600101612f50565b506001600160a01b038c165f8181526003602090815260408083208f8452825280832080546001600160701b038f8116600160701b908102600160701b600160e01b0319841617909355958552600290935292205491900482169116818b106131be57818b03810199506131d0565b8a82038111156131d0578a8203810399505b6001600160a01b038e165f90815260026020526040902054600160701b90046001600160701b03168a10613217576040516366b09a5960e01b815260040160405180910390fd5b5050506001600160a01b03909a165f90815260026020526040902080546001600160701b039097166001600160701b0319909716969096179095555094989397509295505050505050565b5f818152600760205260409020546001600160a01b0316806132b35760405162461bcd60e51b815260206004820152600a6024820152691393d517d3525395115160b21b60448201526064016109b2565b6001600160a01b0381165f81815260086020908152604080832080545f190190558583526007825280832080546001600160a01b031990811690915560099092528083208054909216909155518492907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef908390a45050565b5f60405163a9059cbb60e01b81526001600160a01b038416600482015282602482015260205f6044835f895af191505080601f3d1160015f5114161516156133765750823b153d17155b8061151d5760405162461bcd60e51b815260206004820152600f60248201526e1514905394d1915497d19052531151608a1b60448201526064016109b2565b5f826001600160a01b0316846001600160a01b0316106133d3575f5ffd5b6134d5856001600160a01b031663cefa77996040518163ffffffff1660e01b8152600401602060405180830381865afa158015613412573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906134369190614469565b604080516001600160a01b038089166020830152871691810191909152600285900b60608201526080016040516020818303038152906040528051906020012087604051733d602d80600a3d3981f3363d3d373d3d3d363d7360601b8152606093841b60148201526f5af43d82803e903d91602b57fd5bf3ff60801b6028820152921b6038830152604c8201526037808220606c830152605591012090565b95945050505050565b6134e882826136fc565b6001600160a01b0382163b158061358a5750604051630a85bd0160e11b8082523360048301525f6024830181905260448301849052608060648401526084830152906001600160a01b0384169063150b7a029060a4016020604051808303815f875af115801561355a573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061357e919061428c565b6001600160e01b031916145b6135a65760405162461bcd60e51b81526004016109b2906142a7565b5050565b6060815f036135d05750506040805180820190915260018152600360fc1b602082015290565b815f5b81156135f957806135e3816148cc565b91506135f29050600a836148e4565b91506135d3565b5f816001600160401b0381111561361257613612614332565b6040519080825280601f01601f19166020018201604052801561363c576020820181803683370190505b5090505b84156136a657613651600183614677565b915061365e600a866148f7565b613669906030614664565b60f81b81838151811061367e5761367e614580565b60200101906001600160f81b03191690815f1a90535061369f600a866148e4565b9450613640565b949350505050565b5f600160701b82106136be575f5ffd5b5090565b5f5f5f6136cf8585613805565b90506136ed816136de8a613862565b6136e78a613862565b89613b90565b90999098509650505050505050565b6001600160a01b0382166137465760405162461bcd60e51b81526020600482015260116024820152701253959053125117d49150d25412515395607a1b60448201526064016109b2565b5f818152600760205260409020546001600160a01b03161561379b5760405162461bcd60e51b815260206004820152600e60248201526d1053149150511657d3525395115160921b60448201526064016109b2565b6001600160a01b0382165f81815260086020908152604080832080546001019055848352600790915280822080546001600160a01b0319168417905551839291907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef908290a45050565b5f815f03613828575073fffd8963efd1fc6a506488495d951d5263988d2661081e565b5f613840846b204fce5e3e250261100000008561288d565b90505f61384c82613c2a565b90506134d5655af3107a4000606083901b6148e4565b5f5f5f8360020b12613877578260020b61387e565b8260020b5f035b9050620d89e88111156138b75760405162461bcd60e51b81526020600482015260016024820152601560fa1b60448201526064016109b2565b5f816001165f036138cc57600160801b6138de565b6ffffcb933bd6fad37aa2d162d1a5940015b70ffffffffffffffffffffffffffffffffff1690506002821615613912576ffff97272373d413259a46990580e213a0260801c5b6004821615613931576ffff2e50f5f656932ef12357cf3c7fdcc0260801c5b6008821615613950576fffe5caca7e10e4e61c3624eaa0941cd00260801c5b601082161561396f576fffcb9843d60f6159c9db58835c9266440260801c5b602082161561398e576fff973b41fa98c081472e6896dfb254c00260801c5b60408216156139ad576fff2ea16466c96a3843ec78b326b528610260801c5b60808216156139cc576ffe5dee046a99a2a811c461f1969c30530260801c5b6101008216156139ec576ffcbe86c7900a88aedcffc83b479aa3a40260801c5b610200821615613a0c576ff987a7253ac413176f2b074cf7815e540260801c5b610400821615613a2c576ff3392b0822b70005940c7a398e4b70f30260801c5b610800821615613a4c576fe7159475a2c29b7443b29c7fa6e889d90260801c5b611000821615613a6c576fd097f3bdfd2022b8845ad8f792aa58250260801c5b612000821615613a8c576fa9f746462d870fdf8a65dc1f90e061e50260801c5b614000821615613aac576f70d869a156d2a1b890bb3df62baf32f70260801c5b618000821615613acc576f31be135f97d08fd981231505542fcfa60260801c5b62010000821615613aed576f09aa508b5b7a84e1c677de54f3e99bc90260801c5b62020000821615613b0d576e5d6af8dedb81196699c329225ee6040260801c5b62040000821615613b2c576d2216e584f5fa1ea926041bedfe980260801c5b62080000821615613b49576b048a170391f7dc42444e8fa20260801c5b5f8460020b1315613b6857805f1981613b6457613b64614896565b0490505b640100000000810615613b7c576001613b7e565b5f5b60ff16602082901c0192505050919050565b5f5f836001600160a01b0316856001600160a01b03161115613bb0579293925b846001600160a01b0316866001600160a01b031611613bdb57613bd4858585613cce565b9150612a47565b836001600160a01b0316866001600160a01b03161015613c1457613c00868585613cce565b9150613c0d858785613d3e565b9050612a47565b613c1f858585613d3e565b905094509492505050565b60b581600160881b8110613c435760409190911b9060801c5b69010000000000000000008110613c5f5760209190911b9060401c5b650100000000008110613c775760109190911b9060201c5b63010000008110613c8d5760089190911b9060101c5b62010000010260121c80820401600190811c80830401811c80830401811c80830401811c80830401811c80830401811c80830401901c908190048111900390565b5f826001600160a01b0316846001600160a01b03161115613ced579192915b836001600160a01b0316613d26606060ff16846001600160801b0316901b8686036001600160a01b0316866001600160a01b0316613d7c565b81613d3357613d33614896565b0490505b9392505050565b5f826001600160a01b0316846001600160a01b03161115613d5d579192915b6136a6826001600160801b03168585036001600160a01b0316600160601b5b5f80805f19858709858702925082811083820303915050805f03613db0575f8411613da5575f5ffd5b508290049050613d37565b808411613dbb575f5ffd5b5f8486880960026001871981018816978890046003810283188082028403028082028403028082028403028082028403028082028403029081029092039091025f889003889004909101858311909403939093029303949094049190911702949350505050565b828054828255905f5260205f20908101928215613e5b579160200282015b82811115613e5b578251825591602001919060010190613e40565b506136be9291505b808211156136be575f8155600101613e63565b6001600160e01b031981168114613e8b575f5ffd5b50565b5f60208284031215613e9e575f5ffd5b8135613d3781613e76565b6001600160a01b0381168114613e8b575f5ffd5b5f5f5f5f60808587031215613ed0575f5ffd5b8435613edb81613ea9565b93506020850135613eeb81613ea9565b93969395505050506040820135916060013590565b602081525f82518060208401528060208501604085015e5f604082850101526040601f19601f83011684010191505092915050565b5f60208284031215613f45575f5ffd5b5035919050565b5f5f60408385031215613f5d575f5ffd5b8235613f6881613ea9565b946020939093013593505050565b5f5f83601f840112613f86575f5ffd5b5081356001600160401b03811115613f9c575f5ffd5b602083019150836020828501011115613fb3575f5ffd5b9250929050565b5f5f5f5f5f60808688031215613fce575f5ffd5b8535613fd981613ea9565b94506020860135613fe981613ea9565b93506040860135925060608601356001600160401b0381111561400a575f5ffd5b61401688828901613f76565b969995985093965092949392505050565b5f60208284031215614037575f5ffd5b8135613d3781613ea9565b5f5f5f5f5f60a08688031215614056575f5ffd5b853561406181613ea9565b9450602086013561407181613ea9565b94979496505050506040830135926060810135926080909101359150565b5f5f5f606084860312156140a1575f5ffd5b83356140ac81613ea9565b925060208401356140bc81613ea9565b929592945050506040919091013590565b5f5f5f606084860312156140df575f5ffd5b83356140ea81613ea9565b95602085013595506040909401359392505050565b5f5f60408385031215614110575f5ffd5b50508035926020909101359150565b5f5f60208385031215614130575f5ffd5b82356001600160401b03811115614145575f5ffd5b61415185828601613f76565b90969095509350505050565b5f5f5f6060848603121561416f575f5ffd5b833561417a81613ea9565b925060208401356001600160701b0381168114614195575f5ffd5b9150604084013561ffff811681146141ab575f5ffd5b809150509250925092565b8015158114613e8b575f5ffd5b5f5f604083850312156141d4575f5ffd5b82356141df81613ea9565b915060208301356141ef816141b6565b809150509250929050565b5f5f6040838503121561420b575f5ffd5b823561421681613ea9565b915060208301356141ef81613ea9565b634e487b7160e01b5f52601160045260245ffd5b5f600160ff1b820161424e5761424e614226565b505f0390565b600181811c9082168061426857607f821691505b60208210810361428657634e487b7160e01b5f52602260045260245ffd5b50919050565b5f6020828403121561429c575f5ffd5b8151613d3781613e76565b60208082526010908201526f155394d0519157d49150d2541251539560821b604082015260600190565b6020808252600a90820152695245454e5452414e435960b01b604082015260600190565b5f60208284031215614305575f5ffd5b5051919050565b6020808252600c908201526b15539055551213d49256915160a21b604082015260600190565b634e487b7160e01b5f52604160045260245ffd5b601f821115610e8457805f5260205f20601f840160051c8101602085101561436b5750805b601f840160051c820191505b8181101561227d575f8155600101614377565b6001600160401b038311156143a1576143a1614332565b6143b5836143af8354614254565b83614346565b5f601f8411600181146143e6575f85156143cf5750838201355b5f19600387901b1c1916600186901b17835561227d565b5f83815260208120601f198716915b8281101561441557868501358255602094850194600190920191016143f5565b5086821015614431575f1960f88860031b161c19848701351681555b505060018560011b0183555050505050565b5f60208284031215614453575f5ffd5b8151613d37816141b6565b805161122781613ea9565b5f60208284031215614479575f5ffd5b8151613d3781613ea9565b8051600281900b8114611227575f5ffd5b80516001600160801b0381168114611227575f5ffd5b5f5f5f5f5f5f5f5f5f5f5f5f6101808d8f0312156144c7575f5ffd5b8c516001600160601b03811681146144dd575f5ffd5b9b506144eb60208e0161445e565b9a506144f960408e0161445e565b995061450760608e0161445e565b985061451560808e01614484565b975061452360a08e01614484565b965061453160c08e01614484565b955061453f60e08e01614495565b6101008e01516101208f01519196509450925061455f6101408e01614495565b915061456e6101608e01614495565b90509295989b509295989b509295989b565b634e487b7160e01b5f52603260045260245ffd5b6001600160a01b03868116825285166020820152604081018490526080606082018190528101829052818360a08301375f81830160a090810191909152601f909201601f19160101949350505050565b5f5f84546145f181614254565b600182168015614608576001811461461d5761464a565b60ff198316865281151582028601935061464a565b875f5260205f205f5b8381101561464257815488820152600190910190602001614626565b505081860193505b50505083518060208601835e5f9101908152949350505050565b8082018082111561081e5761081e614226565b8181038181111561081e5761081e614226565b8181035f8312801583831316838312821617156146a9576146a9614226565b5092915050565b5f8151808452602084019350602083015f5b828110156146e05781518652602095860195909101906001016146c2565b5093949350505050565b6001600160a01b03851681526080602080830182905285519183018290525f919086019060a0840190835b8181101561473c5783516001600160a01b0316835260209384019390920191600101614715565b5050838103604085015261475081876146b0565b9150508281036060840152612c5381856146b0565b604051606081016001600160401b038111828210171561478757614787614332565b60405290565b604051601f8201601f191681016001600160401b03811182821017156147b5576147b5614332565b604052919050565b5f602082840312156147cd575f5ffd5b81516001600160401b038111156147e2575f5ffd5b8201601f810184136147f2575f5ffd5b80516001600160401b0381111561480b5761480b614332565b61481a60208260051b0161478d565b8082825260208201915060206060840285010192508683111561483b575f5ffd5b6020840193505b8284101561488c5760608488031215614859575f5ffd5b614861614765565b8451815260208086015181830152604080870151908301529083526060909401939190910190614842565b9695505050505050565b634e487b7160e01b5f52601260045260245ffd5b5f5f604083850312156148bb575f5ffd5b505080516020909101519092909150565b5f600182016148dd576148dd614226565b5060010190565b5f826148f2576148f2614896565b500490565b5f8261490557614905614896565b50069056fea26469706673582212200cc02de70e7c562e3ba4c105126a2b7cf8240fc73dd56e3fda8380468e52bcf864736f6c634300081e0033

Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)

000000000000000000000000b4d72b1c91e640e4ed7d7397f3244de4d8acc50b000000000000000000000000d0690557600eb8be8391d1d97346e2aab5300d5f000000000000000000000000991d5546c4b442b4c5fdc4c8b8b8d131deb2470200000000000000000000000097cdbce21b6fd0585d29e539b1b99dad328a11230000000000000000000000007f9adfbd38b669f03d1d11000bc76b9aaea28a81

-----Decoded View---------------
Arg [0] : owner_ (address): 0xb4d72B1c91e640e4ED7d7397F3244De4D8ACc50B
Arg [1] : registry (address): 0xd0690557600eb8Be8391D1d97346e2aab5300d5f
Arg [2] : nonFungiblePositionManager (address): 0x991d5546C4B442B4c5fdc4c8B8b8d131DEB24702
Arg [3] : aerodromeVoter (address): 0x97cDBCe21B6fd0585d29E539B1B99dAd328a1123
Arg [4] : rewardToken (address): 0x7f9AdFbd38b669F03d1d11000Bc76b9AaEA28A81

-----Encoded View---------------
5 Constructor Arguments found :
Arg [0] : 000000000000000000000000b4d72b1c91e640e4ed7d7397f3244de4d8acc50b
Arg [1] : 000000000000000000000000d0690557600eb8be8391d1d97346e2aab5300d5f
Arg [2] : 000000000000000000000000991d5546c4b442b4c5fdc4c8b8b8d131deb24702
Arg [3] : 00000000000000000000000097cdbce21b6fd0585d29e539b1b99dad328a1123
Arg [4] : 0000000000000000000000007f9adfbd38b669f03d1d11000bc76b9aaea28a81


Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading
Loading...
Loading
Loading...
Loading

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
Loading...
Loading

A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.