Source Code
Overview
ETH Balance
0 ETH
ETH Value
$0.00View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Cross-Chain Transactions
Loading...
Loading
This contract may be a proxy contract. Click on More Options and select Is this a proxy? to confirm and enable the "Read as Proxy" & "Write as Proxy" tabs.
Contract Name:
StakedSlipstreamAM
Compiler Version
v0.8.30+commit.73712a01
Optimization Enabled:
Yes with 200 runs
Other Settings:
prague EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
/**
* 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;
}
}// 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)
}
}
}// 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);
}
}{
"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
- No Contract Security Audit Submitted- Submit Audit Here
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"}]Contract Creation Code
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
Loading...
Loading
Loading...
Loading
Loading...
Loading
Net Worth in USD
$0.00
Net Worth in ETH
0
Multichain Portfolio | 35 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
Loading...
Loading
Loading...
Loading
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.