Source Code
Overview
S Balance
S Value
$0.00Cross-Chain Transactions
Loading...
Loading
Contract Name:
OracleFacet
Compiler Version
v0.8.26+commit.8a97fa7a
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/utils/math/Math.sol";
import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import "../../storage/PetalsStorageLayout.sol";
import "../../00_libraries/types.sol";
import "../../00_libraries/RoleChecker.sol";
import "../../../interfaces/routing/IRouterLibrary.sol";
import "../../../interfaces/routing/RouterTypes.sol";
import "../../../interfaces/common/IPriceOracle.sol"; // For IAggregatorV3Interface
/**
* @title OracleFacet
* @notice Multi-source price oracle for all Petals Protocol assets
* @dev Diamond Facet - uses AppStorage and integrates with library-centric routing
*
* Pricing Philosophy:
* - DEX Routing = REQUIRED (protocol's backbone, on-chain truth)
* - Chainlink Feeds = OPTIONAL (convenience for tighter spreads, lower gas)
* - Protocol remains 100% functional without Chainlink
*
* Pricing Hierarchy:
* 1. Chainlink feeds (if available, for tighter pricing)
* 2. LP token calculation (recursive pricing of components)
* 3. DEX routing (REQUIRED - always falls back here)
* 4. Cached price (last resort if recent)
*
* Architecture Benefits:
* - Censorship resistant (no Chainlink dependency)
* - Market truth always visible (depegs shown accurately)
* - Integrated governance (MASTER_ROLE via Diamond)
* - Reuses router libraries (SolidlyLibrary, UniswapV2Library, etc.)
* - Upgradeable via Diamond Cut
*
* Integration with Library-Centric Model:
* - Router libraries registered in ProtocolLibraryManagerFacet
* - Each library implements getPrice() for its router type
* - Oracle delegates to appropriate library based on RouterType
*/
contract OracleFacet is ReentrancyGuard {
using Math for uint256;
// ============ CONSTANTS ============
uint256 private constant PRICE_PRECISION = 1e18;
uint256 private constant MAX_PRICE_AGE = 1 hours;
// ============ ERRORS ============
error Unauthorized();
// ============ MODIFIERS ============
modifier onlyRole(uint64 role) {
if (!RoleChecker.hasRole(role, msg.sender)) {
revert Unauthorized();
}
_;
}
// Liquidity-based confidence tiers for LP tokens
uint256 private constant LIQUIDITY_TIER_EXCELLENT = 1_000_000e18; // $1M+ = 95 confidence
uint256 private constant LIQUIDITY_TIER_GOOD = 250_000e18; // $250K+ = 80 confidence
uint256 private constant LIQUIDITY_TIER_ACCEPTABLE = 50_000e18; // $50K+ = 60 confidence
uint256 private constant LIQUIDITY_TIER_POOR = 10_000e18; // $10K+ = 40 confidence
// ============ ERRORS (additional) ============
error NotAnLPToken();
error UnsupportedLPTokenType();
// ============ EVENTS ============
event ChainlinkFeedAdded(address indexed asset, address feed);
event DEXRouterAdded(address router, RouterType routerType);
event BaseTokenAdded(address token, uint256 priority);
event ManualPriceSet(address indexed asset, uint256 price, string reason);
// ============ CORE PRICING FUNCTIONS ============
/**
* @notice Get USD price with confidence level
* @param asset Asset address to price
* @return price Price in USD (18 decimals)
* @return confidence Confidence level (0-100)
*/
function getPrice(address asset) external view returns (uint256 price, uint8 confidence) {
PriceData memory data = _getPriceData(asset);
return (data.price, data.confidence);
}
/**
* @notice Get detailed price information
* @param asset Asset address
* @return data Complete price data with metadata
*/
function getPriceData(address asset) external view returns (PriceData memory data) {
return _getPriceData(asset);
}
/**
* @notice Check if price is valid and fresh
* @param asset Asset address
* @return isValid Whether price is valid
* @return lastUpdate Last update timestamp
*/
function isPriceValid(address asset) external view returns (bool isValid, uint256 lastUpdate) {
PriceData memory data = _getPriceData(asset);
lastUpdate = data.lastUpdate;
isValid = data.price > 0 && block.timestamp - lastUpdate <= _getMaxPriceAge(asset) && data.confidence >= 50;
}
// ============ LP TOKEN FUNCTIONS ============
/**
* @notice Check if asset is an LP token
* @dev Uses generic library delegation - no DEX-specific code
* @param asset Asset address
* @return isLP Whether asset is an LP token
*/
function isLPToken(address asset) external view returns (bool isLP) {
PetalsStorageLayout.AppStorage storage s = PetalsStorageLayout.getStorage();
// STRICT: Only check ChainRegistry, no fallback
if (s.isRegisteredAsset[asset]) {
AssetMetadata memory assetInfo = s.assetRegistry[asset];
return (assetInfo.assetType == AssetType.LP_TOKEN_2 || assetInfo.assetType == AssetType.LP_TOKEN_MULTI);
}
// Not registered = not an LP token
return false;
}
/**
* @notice Get LP token data and pricing
* @dev Fully generic: Uses ChainRegistry + library delegation, NO DEX-specific code
* @param lpToken LP token address
* @return data Complete LP token data
*/
function getLPTokenData(address lpToken) external view returns (LPTokenData memory data) {
PetalsStorageLayout.AppStorage storage s = PetalsStorageLayout.getStorage();
// STRICT: Asset must be registered in ChainRegistry
if (!s.isRegisteredAsset[lpToken]) {
revert NotAnLPToken();
}
AssetMetadata memory assetInfo = s.assetRegistry[lpToken];
// Must be an LP token type
if (assetInfo.assetType != AssetType.LP_TOKEN_2 && assetInfo.assetType != AssetType.LP_TOKEN_MULTI) {
revert NotAnLPToken();
}
// Get router library
address routerLib = s.routerLibraries[assetInfo.routerType];
if (routerLib == address(0)) {
revert NotAnLPToken(); // Library not registered - should never happen if registration validated
}
// Delegate to library - no try/catch, fail fast
LPTokenData memory lpData = IRouterLibrary(routerLib).getLPData(lpToken);
if (!lpData.isLP) {
revert NotAnLPToken(); // Library didn't recognize it - registration validation failed
}
return lpData;
}
/**
* @notice Calculate LP token price
* @param lpToken LP token address
* @return price LP token price in USD
*/
function calculateLPTokenPrice(address lpToken) external view returns (uint256 price) {
LPTokenData memory data = this.getLPTokenData(lpToken);
if (!data.isLP) return 0;
(uint256 lpPrice,) = _calculateGenericLPPrice(data);
return lpPrice;
}
// ============ INTERNAL PRICING LOGIC ============
/**
* @notice Main pricing logic with fallback hierarchy
* @param asset Asset to price
* @return data Complete price data
*/
function _getPriceData(address asset) internal view returns (PriceData memory data) {
PetalsStorageLayout.AppStorage storage s = PetalsStorageLayout.getStorage();
// 1. Try Chainlink feed (primary - tighter pricing, lower gas)
if (s.chainlinkFeeds[asset] != address(0)) {
(uint256 price, uint8 confidence) = _getChainlinkPrice(asset);
if (price > 0 && confidence >= 80) {
return PriceData({
price: price,
confidence: confidence,
lastUpdate: block.timestamp,
method: bytes32("chainlink")
});
}
// Chainlink failed or low confidence - fall through to DEX
}
// 2. LP token calculation (uses Chainlink or DEX for components)
// _getLPTokenPrice handles LP detection internally via library delegation
(uint256 lpPrice, uint8 lpConfidence) = _getLPTokenPrice(asset);
if (lpPrice > 0) {
return PriceData({
price: lpPrice,
confidence: lpConfidence,
lastUpdate: block.timestamp,
method: bytes32("lp_calculation")
});
}
// 3. DEX routing (REQUIRED backup - chain's source of truth)
(uint256 dexPrice, uint8 dexConfidence) = _getBestDEXPrice(asset);
if (dexPrice > 0) {
return PriceData({
price: dexPrice,
confidence: dexConfidence,
lastUpdate: block.timestamp,
method: bytes32("dex_routing")
});
}
// 4. Use cached price if available and not too stale
PriceData memory cached = s.cachedPrices[asset];
if (cached.price > 0 && block.timestamp - cached.lastUpdate <= _getMaxPriceAge(asset)) {
return cached;
}
// 5. Return zero price if no method works
return PriceData({ price: 0, confidence: 0, lastUpdate: 0, method: bytes32("none") });
}
/**
* @notice Get Chainlink price with confidence scoring
* @param asset Asset address
* @return price Price in USD
* @return confidence Confidence level
*/
function _getChainlinkPrice(address asset) internal view returns (uint256 price, uint8 confidence) {
PetalsStorageLayout.AppStorage storage s = PetalsStorageLayout.getStorage();
address feed = s.chainlinkFeeds[asset];
if (feed == address(0)) return (0, 0);
try IAggregatorV3Interface(feed).latestRoundData() returns (
uint80 roundId, int256 answer, uint256 /* startedAt */, uint256 updatedAt, uint80 answeredInRound
) {
if (answer <= 0) return (0, 0);
// Heartbeat check: Ensure data is actually fresh
if (answeredInRound < roundId) return (0, 0);
// Staleness check: Reject data older than 24 hours
if (block.timestamp - updatedAt > 24 hours) return (0, 0);
// Convert to 18 decimals
uint8 decimals = IAggregatorV3Interface(feed).decimals();
price = uint256(answer) * (10 ** (18 - decimals));
// Calculate confidence based on freshness
uint256 age = block.timestamp - updatedAt;
if (age <= 5 minutes) {
confidence = 95;
} else if (age <= 15 minutes) {
confidence = 90;
} else if (age <= 1 hours) {
confidence = 80;
} else {
confidence = 50;
}
} catch {
return (0, 0);
}
}
/**
* @notice Calculate LP token price from underlying assets
* @dev Fully generic: Uses ChainRegistry + library delegation, NO DEX-specific code
* @param lpToken LP token address
* @return price LP token price
* @return confidence Confidence level
*/
function _getLPTokenPrice(address lpToken) internal view returns (uint256 price, uint8 confidence) {
PetalsStorageLayout.AppStorage storage s = PetalsStorageLayout.getStorage();
// STRICT: Asset must be registered
if (!s.isRegisteredAsset[lpToken]) {
return (0, 0); // Not registered, can't price
}
AssetMetadata memory assetInfo = s.assetRegistry[lpToken];
// Only process LP tokens
if (assetInfo.assetType != AssetType.LP_TOKEN_2 && assetInfo.assetType != AssetType.LP_TOKEN_MULTI) {
return (0, 0); // Not an LP token
}
// Get router library
address routerLib = s.routerLibraries[assetInfo.routerType];
if (routerLib == address(0)) {
return (0, 0); // Library not registered - should never happen if validation worked
}
// Delegate to library - no try/catch, fail fast with revert if library fails
LPTokenData memory data = IRouterLibrary(routerLib).getLPData(lpToken);
if (!data.isLP) {
return (0, 0); // Library didn't recognize it
}
// Calculate price using generic function
return _calculateGenericLPPrice(data);
}
/**
* @notice Calculate LP token price from generic LP data (multi-token support)
* @dev Works for 2-token pairs AND multi-token pools (Balancer, Curve)
* @param data LP token data with token array and reserves
* @return price LP token price
* @return confidence Confidence level
*/
function _calculateGenericLPPrice(LPTokenData memory data) internal view returns (uint256 price, uint8 confidence) {
if (data.totalSupply == 0) return (0, 0);
uint256 totalValue = 0;
uint8 minConfidence = 100;
// Loop through ALL tokens (works for 2, 3, 8+ tokens)
for (uint256 i = 0; i < data.tokens.length; i++) {
// Get price of each component token (recursive)
(uint256 tokenPrice, uint8 tokenConf) = this.getPrice(data.tokens[i]);
if (tokenPrice == 0) return (0, 0); // Can't price if any component unknown
// Calculate value of this token's reserve
uint8 decimals = IERC20Metadata(data.tokens[i]).decimals();
uint256 tokenValue = (data.reserves[i] * tokenPrice) / (10 ** decimals);
totalValue += tokenValue;
// Track minimum confidence
if (tokenConf < minConfidence) {
minConfidence = tokenConf;
}
}
// Price per LP token
price = (totalValue * PRICE_PRECISION) / data.totalSupply;
// Confidence is minimum of all component tokens
confidence = minConfidence;
// Apply liquidity-based adjustment
confidence = _adjustConfidenceForLiquidity(confidence, totalValue);
}
/**
* @notice Get best price from all DEX routers using library delegation
* @param token Token to price
* @return bestPrice Average price from all valid sources
* @return confidence Confidence based on number of sources
*/
function _getBestDEXPrice(address token) internal view returns (uint256 bestPrice, uint8 confidence) {
PetalsStorageLayout.AppStorage storage s = PetalsStorageLayout.getStorage();
uint256 validPrices = 0;
uint256 totalPrice = 0;
// Try all registered DEX routers
for (uint256 i = 0; i < s.dexRouters.length; i++) {
OracleRouterInfo memory routerInfo = s.dexRouters[i];
// Try all base tokens for this router
for (uint256 j = 0; j < s.baseTokens.length; j++) {
address baseToken = s.baseTokens[j];
if (baseToken == token) continue;
uint256 price = _getDEXPriceViaLibrary(token, baseToken, routerInfo);
if (price > 0) {
// Convert to USD using base token price
(uint256 basePrice,) = this.getPrice(baseToken);
if (basePrice > 0) {
uint256 usdPrice = (price * basePrice) / PRICE_PRECISION;
totalPrice += usdPrice;
validPrices++;
}
}
}
}
if (validPrices == 0) return (0, 0);
bestPrice = totalPrice / validPrices;
// Confidence based on number of valid prices
if (validPrices >= 3) {
confidence = 75;
} else if (validPrices >= 2) {
confidence = 60;
} else {
confidence = 40;
}
}
/**
* @notice Get price from specific DEX router via library delegation
* @dev Delegates to appropriate library based on RouterType
* @param token Token to price
* @param baseToken Base token for routing
* @param routerInfo Router information with type
* @return price Price in base token terms (0 if route doesn't exist)
*/
function _getDEXPriceViaLibrary(
address token,
address baseToken,
OracleRouterInfo memory routerInfo
) internal view returns (uint256 price) {
PetalsStorageLayout.AppStorage storage s = PetalsStorageLayout.getStorage();
// Get router library from Diamond
address routerLib = s.routerLibraries[routerInfo.routerType];
if (routerLib == address(0)) return 0;
// Delegate to library's getPrice() implementation
try IRouterLibrary(routerLib).getPrice(
routerInfo.routerAddress,
token,
baseToken,
PRICE_PRECISION
) returns (uint256 amountOut) {
return amountOut;
} catch {
return 0;
}
}
// ============ LP TOKEN DETECTION ============
// All LP detection logic delegated to libraries via IRouterLibrary.getLPData()
// No DEX-specific code remains in OracleFacet
function _getMaxPriceAge(address asset) internal view returns (uint256) {
PetalsStorageLayout.AppStorage storage s = PetalsStorageLayout.getStorage();
uint256 customAge = s.maxPriceAge[asset];
return customAge > 0 ? customAge : MAX_PRICE_AGE;
}
/**
* @notice Adjust confidence based on liquidity depth
* @param baseConfidence Initial confidence
* @param liquidityUSD Total liquidity in USD
* @return adjustedConfidence Final confidence after adjustment
*/
function _adjustConfidenceForLiquidity(
uint8 baseConfidence,
uint256 liquidityUSD
) internal pure returns (uint8 adjustedConfidence) {
adjustedConfidence = baseConfidence;
if (liquidityUSD >= LIQUIDITY_TIER_EXCELLENT) {
uint256 boosted = uint256(adjustedConfidence) * 105 / 100;
adjustedConfidence = boosted > 100 ? 100 : uint8(boosted);
} else if (liquidityUSD >= LIQUIDITY_TIER_GOOD) {
return adjustedConfidence;
} else if (liquidityUSD >= LIQUIDITY_TIER_ACCEPTABLE) {
adjustedConfidence = uint8(uint256(adjustedConfidence) * 90 / 100);
} else if (liquidityUSD >= LIQUIDITY_TIER_POOR) {
adjustedConfidence = uint8(uint256(adjustedConfidence) * 70 / 100);
} else {
adjustedConfidence = uint8(uint256(adjustedConfidence) * 40 / 100);
}
}
// ============ VALIDATION FUNCTIONS (for pre-approval checks) ============
/**
* @notice Validate that an asset can be priced by the Oracle
* @dev CRITICAL: Called by ControllerFacet before vault approval
* @param asset Asset address to validate
* @param minConfidence Minimum confidence threshold required (0-100)
* @return canPrice Whether asset can be priced
* @return actualConfidence Actual confidence level achieved
* @return reason Failure reason if canPrice is false
*/
function canPriceAsset(
address asset,
uint8 minConfidence
) external view returns (bool canPrice, uint8 actualConfidence, string memory reason) {
PetalsStorageLayout.AppStorage storage s = PetalsStorageLayout.getStorage();
// Check 1: Is asset registered?
if (!s.isRegisteredAsset[asset]) {
return (false, 0, "Asset not registered in ChainRegistry");
}
AssetMetadata memory assetInfo = s.assetRegistry[asset];
// Check 2: Is asset active?
if (!assetInfo.isActive) {
return (false, 0, "Asset is deactivated");
}
// Check 3: Try to get price
try this.getPrice(asset) returns (uint256 price, uint8 confidence) {
if (price == 0) {
return (false, 0, "Oracle returned zero price");
}
if (confidence < minConfidence) {
return (false, confidence, "Confidence below threshold");
}
return (true, confidence, "");
} catch Error(string memory err) {
return (false, 0, string(abi.encodePacked("Oracle reverted: ", err)));
} catch {
return (false, 0, "Oracle reverted without reason");
}
}
// ============ ORACLE CONFIGURATION FUNCTIONS ============
/**
* @notice Add single Chainlink feed
* @dev Chainlink is PRIMARY source for tighter pricing, but DEX is required backup
* @param asset Asset address
* @param feed Chainlink aggregator address
*/
function addChainlinkFeed(address asset, address feed) external onlyRole(RoleChecker.MASTER_ROLE) {
PetalsStorageLayout.AppStorage storage s = PetalsStorageLayout.getStorage();
s.chainlinkFeeds[asset] = feed;
emit ChainlinkFeedAdded(asset, feed);
}
/**
* @notice Batch add Chainlink feeds
* @param assets Array of asset addresses
* @param feeds Array of Chainlink aggregator addresses
*/
function batchAddChainlinkFeeds(
address[] calldata assets,
address[] calldata feeds
) external onlyRole(RoleChecker.MASTER_ROLE) {
require(assets.length == feeds.length, "Length mismatch");
PetalsStorageLayout.AppStorage storage s = PetalsStorageLayout.getStorage();
for (uint256 i = 0; i < assets.length; i++) {
s.chainlinkFeeds[assets[i]] = feeds[i];
emit ChainlinkFeedAdded(assets[i], feeds[i]);
}
}
/**
* @notice Add DEX router with its type
* @param router DEX router address
* @param routerType Router type (SOLIDLY, UNISWAP_V2, etc.)
*/
function addDEXRouter(address router, RouterType routerType) external onlyRole(RoleChecker.MASTER_ROLE) {
PetalsStorageLayout.AppStorage storage s = PetalsStorageLayout.getStorage();
if (!s.approvedRouters[router]) {
s.dexRouters.push(OracleRouterInfo({
routerAddress: router,
routerType: routerType
}));
s.approvedRouters[router] = true;
emit DEXRouterAdded(router, routerType);
}
}
/**
* @notice Batch add DEX routers
* @param routers Array of DEX router addresses
* @param routerTypes Array of router types (parallel to routers)
*/
function batchAddDEXRouters(
address[] calldata routers,
RouterType[] calldata routerTypes
) external onlyRole(RoleChecker.MASTER_ROLE) {
require(routers.length == routerTypes.length, "Length mismatch");
PetalsStorageLayout.AppStorage storage s = PetalsStorageLayout.getStorage();
for (uint256 i = 0; i < routers.length; i++) {
if (!s.approvedRouters[routers[i]]) {
s.dexRouters.push(OracleRouterInfo({
routerAddress: routers[i],
routerType: routerTypes[i]
}));
s.approvedRouters[routers[i]] = true;
emit DEXRouterAdded(routers[i], routerTypes[i]);
}
}
}
/**
* @notice Add base token for routing
* @param token Base token address
* @param priority Lower = higher priority
*/
function addBaseToken(address token, uint256 priority) external onlyRole(RoleChecker.MASTER_ROLE) {
PetalsStorageLayout.AppStorage storage s = PetalsStorageLayout.getStorage();
if (!s.isBaseToken[token]) {
s.baseTokens.push(token);
s.isBaseToken[token] = true;
}
s.baseTokenPriority[token] = priority;
emit BaseTokenAdded(token, priority);
}
/**
* @notice Batch add base tokens
* @param tokens Array of base token addresses
* @param priorities Array of priorities (parallel to tokens)
*/
function batchAddBaseTokens(
address[] calldata tokens,
uint256[] calldata priorities
) external onlyRole(RoleChecker.MASTER_ROLE) {
require(tokens.length == priorities.length, "Length mismatch");
PetalsStorageLayout.AppStorage storage s = PetalsStorageLayout.getStorage();
for (uint256 i = 0; i < tokens.length; i++) {
if (!s.isBaseToken[tokens[i]]) {
s.baseTokens.push(tokens[i]);
s.isBaseToken[tokens[i]] = true;
}
s.baseTokenPriority[tokens[i]] = priorities[i];
emit BaseTokenAdded(tokens[i], priorities[i]);
}
}
/**
* @notice Set custom price staleness tolerance
* @dev Allows per-asset configuration (e.g., stablecoins can have longer staleness)
* @param asset Asset address
* @param maxAge Maximum price age in seconds
*/
function setMaxPriceAge(address asset, uint256 maxAge) external onlyRole(RoleChecker.MASTER_ROLE) {
PetalsStorageLayout.AppStorage storage s = PetalsStorageLayout.getStorage();
s.maxPriceAge[asset] = maxAge;
}
// ============ VIEW FUNCTIONS ============
/**
* @notice Get all registered DEX routers
* @return routers Array of router info structs
*/
function getDEXRouters() external view returns (OracleRouterInfo[] memory routers) {
PetalsStorageLayout.AppStorage storage s = PetalsStorageLayout.getStorage();
return s.dexRouters;
}
/**
* @notice Get all base tokens
* @return tokens Array of base token addresses
*/
function getBaseTokens() external view returns (address[] memory tokens) {
PetalsStorageLayout.AppStorage storage s = PetalsStorageLayout.getStorage();
return s.baseTokens;
}
/**
* @notice Get Chainlink feed for asset
* @param asset Asset address
* @return feed Chainlink aggregator address
*/
function getChainlinkFeed(address asset) external view returns (address feed) {
PetalsStorageLayout.AppStorage storage s = PetalsStorageLayout.getStorage();
return s.chainlinkFeeds[asset];
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.3.0) (utils/math/Math.sol)
pragma solidity ^0.8.20;
import {Panic} from "../Panic.sol";
import {SafeCast} from "./SafeCast.sol";
/**
* @dev Standard math utilities missing in the Solidity language.
*/
library Math {
enum Rounding {
Floor, // Toward negative infinity
Ceil, // Toward positive infinity
Trunc, // Toward zero
Expand // Away from zero
}
/**
* @dev Return the 512-bit addition of two uint256.
*
* The result is stored in two 256 variables such that sum = high * 2²⁵⁶ + low.
*/
function add512(uint256 a, uint256 b) internal pure returns (uint256 high, uint256 low) {
assembly ("memory-safe") {
low := add(a, b)
high := lt(low, a)
}
}
/**
* @dev Return the 512-bit multiplication of two uint256.
*
* The result is stored in two 256 variables such that product = high * 2²⁵⁶ + low.
*/
function mul512(uint256 a, uint256 b) internal pure returns (uint256 high, uint256 low) {
// 512-bit multiply [high low] = x * y. Compute the product mod 2²⁵⁶ and mod 2²⁵⁶ - 1, then use
// the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
// variables such that product = high * 2²⁵⁶ + low.
assembly ("memory-safe") {
let mm := mulmod(a, b, not(0))
low := mul(a, b)
high := sub(sub(mm, low), lt(mm, low))
}
}
/**
* @dev Returns the addition of two unsigned integers, with a success flag (no overflow).
*/
function tryAdd(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {
unchecked {
uint256 c = a + b;
success = c >= a;
result = c * SafeCast.toUint(success);
}
}
/**
* @dev Returns the subtraction of two unsigned integers, with a success flag (no overflow).
*/
function trySub(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {
unchecked {
uint256 c = a - b;
success = c <= a;
result = c * SafeCast.toUint(success);
}
}
/**
* @dev Returns the multiplication of two unsigned integers, with a success flag (no overflow).
*/
function tryMul(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {
unchecked {
uint256 c = a * b;
assembly ("memory-safe") {
// Only true when the multiplication doesn't overflow
// (c / a == b) || (a == 0)
success := or(eq(div(c, a), b), iszero(a))
}
// equivalent to: success ? c : 0
result = c * SafeCast.toUint(success);
}
}
/**
* @dev Returns the division of two unsigned integers, with a success flag (no division by zero).
*/
function tryDiv(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {
unchecked {
success = b > 0;
assembly ("memory-safe") {
// The `DIV` opcode returns zero when the denominator is 0.
result := div(a, b)
}
}
}
/**
* @dev Returns the remainder of dividing two unsigned integers, with a success flag (no division by zero).
*/
function tryMod(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {
unchecked {
success = b > 0;
assembly ("memory-safe") {
// The `MOD` opcode returns zero when the denominator is 0.
result := mod(a, b)
}
}
}
/**
* @dev Unsigned saturating addition, bounds to `2²⁵⁶ - 1` instead of overflowing.
*/
function saturatingAdd(uint256 a, uint256 b) internal pure returns (uint256) {
(bool success, uint256 result) = tryAdd(a, b);
return ternary(success, result, type(uint256).max);
}
/**
* @dev Unsigned saturating subtraction, bounds to zero instead of overflowing.
*/
function saturatingSub(uint256 a, uint256 b) internal pure returns (uint256) {
(, uint256 result) = trySub(a, b);
return result;
}
/**
* @dev Unsigned saturating multiplication, bounds to `2²⁵⁶ - 1` instead of overflowing.
*/
function saturatingMul(uint256 a, uint256 b) internal pure returns (uint256) {
(bool success, uint256 result) = tryMul(a, b);
return ternary(success, result, type(uint256).max);
}
/**
* @dev Branchless ternary evaluation for `a ? b : c`. Gas costs are constant.
*
* IMPORTANT: This function may reduce bytecode size and consume less gas when used standalone.
* However, the compiler may optimize Solidity ternary operations (i.e. `a ? b : c`) to only compute
* one branch when needed, making this function more expensive.
*/
function ternary(bool condition, uint256 a, uint256 b) internal pure returns (uint256) {
unchecked {
// branchless ternary works because:
// b ^ (a ^ b) == a
// b ^ 0 == b
return b ^ ((a ^ b) * SafeCast.toUint(condition));
}
}
/**
* @dev Returns the largest of two numbers.
*/
function max(uint256 a, uint256 b) internal pure returns (uint256) {
return ternary(a > b, a, b);
}
/**
* @dev Returns the smallest of two numbers.
*/
function min(uint256 a, uint256 b) internal pure returns (uint256) {
return ternary(a < b, a, b);
}
/**
* @dev Returns the average of two numbers. The result is rounded towards
* zero.
*/
function average(uint256 a, uint256 b) internal pure returns (uint256) {
// (a + b) / 2 can overflow.
return (a & b) + (a ^ b) / 2;
}
/**
* @dev Returns the ceiling of the division of two numbers.
*
* This differs from standard division with `/` in that it rounds towards infinity instead
* of rounding towards zero.
*/
function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
if (b == 0) {
// Guarantee the same behavior as in a regular Solidity division.
Panic.panic(Panic.DIVISION_BY_ZERO);
}
// The following calculation ensures accurate ceiling division without overflow.
// Since a is non-zero, (a - 1) / b will not overflow.
// The largest possible result occurs when (a - 1) / b is type(uint256).max,
// but the largest value we can obtain is type(uint256).max - 1, which happens
// when a = type(uint256).max and b = 1.
unchecked {
return SafeCast.toUint(a > 0) * ((a - 1) / b + 1);
}
}
/**
* @dev Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or
* denominator == 0.
*
* Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) with further edits by
* Uniswap Labs also under MIT license.
*/
function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
unchecked {
(uint256 high, uint256 low) = mul512(x, y);
// Handle non-overflow cases, 256 by 256 division.
if (high == 0) {
// Solidity will revert if denominator == 0, unlike the div opcode on its own.
// The surrounding unchecked block does not change this fact.
// See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
return low / denominator;
}
// Make sure the result is less than 2²⁵⁶. Also prevents denominator == 0.
if (denominator <= high) {
Panic.panic(ternary(denominator == 0, Panic.DIVISION_BY_ZERO, Panic.UNDER_OVERFLOW));
}
///////////////////////////////////////////////
// 512 by 256 division.
///////////////////////////////////////////////
// Make division exact by subtracting the remainder from [high low].
uint256 remainder;
assembly ("memory-safe") {
// Compute remainder using mulmod.
remainder := mulmod(x, y, denominator)
// Subtract 256 bit number from 512 bit number.
high := sub(high, gt(remainder, low))
low := sub(low, remainder)
}
// Factor powers of two out of denominator and compute largest power of two divisor of denominator.
// Always >= 1. See https://cs.stackexchange.com/q/138556/92363.
uint256 twos = denominator & (0 - denominator);
assembly ("memory-safe") {
// Divide denominator by twos.
denominator := div(denominator, twos)
// Divide [high low] by twos.
low := div(low, twos)
// Flip twos such that it is 2²⁵⁶ / twos. If twos is zero, then it becomes one.
twos := add(div(sub(0, twos), twos), 1)
}
// Shift in bits from high into low.
low |= high * twos;
// Invert denominator mod 2²⁵⁶. Now that denominator is an odd number, it has an inverse modulo 2²⁵⁶ such
// that denominator * inv ≡ 1 mod 2²⁵⁶. Compute the inverse by starting with a seed that is correct for
// four bits. That is, denominator * inv ≡ 1 mod 2⁴.
uint256 inverse = (3 * denominator) ^ 2;
// Use the 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.
inverse *= 2 - denominator * inverse; // inverse mod 2⁸
inverse *= 2 - denominator * inverse; // inverse mod 2¹⁶
inverse *= 2 - denominator * inverse; // inverse mod 2³²
inverse *= 2 - denominator * inverse; // inverse mod 2⁶⁴
inverse *= 2 - denominator * inverse; // inverse mod 2¹²⁸
inverse *= 2 - denominator * inverse; // inverse mod 2²⁵⁶
// 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²⁵⁶. Since the preconditions guarantee that the outcome is
// less than 2²⁵⁶, this is the final result. We don't need to compute the high bits of the result and high
// is no longer required.
result = low * inverse;
return result;
}
}
/**
* @dev Calculates x * y / denominator with full precision, following the selected rounding direction.
*/
function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
return mulDiv(x, y, denominator) + SafeCast.toUint(unsignedRoundsUp(rounding) && mulmod(x, y, denominator) > 0);
}
/**
* @dev Calculates floor(x * y >> n) with full precision. Throws if result overflows a uint256.
*/
function mulShr(uint256 x, uint256 y, uint8 n) internal pure returns (uint256 result) {
unchecked {
(uint256 high, uint256 low) = mul512(x, y);
if (high >= 1 << n) {
Panic.panic(Panic.UNDER_OVERFLOW);
}
return (high << (256 - n)) | (low >> n);
}
}
/**
* @dev Calculates x * y >> n with full precision, following the selected rounding direction.
*/
function mulShr(uint256 x, uint256 y, uint8 n, Rounding rounding) internal pure returns (uint256) {
return mulShr(x, y, n) + SafeCast.toUint(unsignedRoundsUp(rounding) && mulmod(x, y, 1 << n) > 0);
}
/**
* @dev Calculate the modular multiplicative inverse of a number in Z/nZ.
*
* If n is a prime, then Z/nZ is a field. In that case all elements are inversible, except 0.
* If n is not a prime, then Z/nZ is not a field, and some elements might not be inversible.
*
* If the input value is not inversible, 0 is returned.
*
* NOTE: If you know for sure that n is (big) a prime, it may be cheaper to use Fermat's little theorem and get the
* inverse using `Math.modExp(a, n - 2, n)`. See {invModPrime}.
*/
function invMod(uint256 a, uint256 n) internal pure returns (uint256) {
unchecked {
if (n == 0) return 0;
// The inverse modulo is calculated using the Extended Euclidean Algorithm (iterative version)
// Used to compute integers x and y such that: ax + ny = gcd(a, n).
// When the gcd is 1, then the inverse of a modulo n exists and it's x.
// ax + ny = 1
// ax = 1 + (-y)n
// ax ≡ 1 (mod n) # x is the inverse of a modulo n
// If the remainder is 0 the gcd is n right away.
uint256 remainder = a % n;
uint256 gcd = n;
// Therefore the initial coefficients are:
// ax + ny = gcd(a, n) = n
// 0a + 1n = n
int256 x = 0;
int256 y = 1;
while (remainder != 0) {
uint256 quotient = gcd / remainder;
(gcd, remainder) = (
// The old remainder is the next gcd to try.
remainder,
// Compute the next remainder.
// Can't overflow given that (a % gcd) * (gcd // (a % gcd)) <= gcd
// where gcd is at most n (capped to type(uint256).max)
gcd - remainder * quotient
);
(x, y) = (
// Increment the coefficient of a.
y,
// Decrement the coefficient of n.
// Can overflow, but the result is casted to uint256 so that the
// next value of y is "wrapped around" to a value between 0 and n - 1.
x - y * int256(quotient)
);
}
if (gcd != 1) return 0; // No inverse exists.
return ternary(x < 0, n - uint256(-x), uint256(x)); // Wrap the result if it's negative.
}
}
/**
* @dev Variant of {invMod}. More efficient, but only works if `p` is known to be a prime greater than `2`.
*
* From https://en.wikipedia.org/wiki/Fermat%27s_little_theorem[Fermat's little theorem], we know that if p is
* prime, then `a**(p-1) ≡ 1 mod p`. As a consequence, we have `a * a**(p-2) ≡ 1 mod p`, which means that
* `a**(p-2)` is the modular multiplicative inverse of a in Fp.
*
* NOTE: this function does NOT check that `p` is a prime greater than `2`.
*/
function invModPrime(uint256 a, uint256 p) internal view returns (uint256) {
unchecked {
return Math.modExp(a, p - 2, p);
}
}
/**
* @dev Returns the modular exponentiation of the specified base, exponent and modulus (b ** e % m)
*
* Requirements:
* - modulus can't be zero
* - underlying staticcall to precompile must succeed
*
* IMPORTANT: The result is only valid if the underlying call succeeds. When using this function, make
* sure the chain you're using it on supports the precompiled contract for modular exponentiation
* at address 0x05 as specified in https://eips.ethereum.org/EIPS/eip-198[EIP-198]. Otherwise,
* the underlying function will succeed given the lack of a revert, but the result may be incorrectly
* interpreted as 0.
*/
function modExp(uint256 b, uint256 e, uint256 m) internal view returns (uint256) {
(bool success, uint256 result) = tryModExp(b, e, m);
if (!success) {
Panic.panic(Panic.DIVISION_BY_ZERO);
}
return result;
}
/**
* @dev Returns the modular exponentiation of the specified base, exponent and modulus (b ** e % m).
* It includes a success flag indicating if the operation succeeded. Operation will be marked as failed if trying
* to operate modulo 0 or if the underlying precompile reverted.
*
* IMPORTANT: The result is only valid if the success flag is true. When using this function, make sure the chain
* you're using it on supports the precompiled contract for modular exponentiation at address 0x05 as specified in
* https://eips.ethereum.org/EIPS/eip-198[EIP-198]. Otherwise, the underlying function will succeed given the lack
* of a revert, but the result may be incorrectly interpreted as 0.
*/
function tryModExp(uint256 b, uint256 e, uint256 m) internal view returns (bool success, uint256 result) {
if (m == 0) return (false, 0);
assembly ("memory-safe") {
let ptr := mload(0x40)
// | Offset | Content | Content (Hex) |
// |-----------|------------|--------------------------------------------------------------------|
// | 0x00:0x1f | size of b | 0x0000000000000000000000000000000000000000000000000000000000000020 |
// | 0x20:0x3f | size of e | 0x0000000000000000000000000000000000000000000000000000000000000020 |
// | 0x40:0x5f | size of m | 0x0000000000000000000000000000000000000000000000000000000000000020 |
// | 0x60:0x7f | value of b | 0x<.............................................................b> |
// | 0x80:0x9f | value of e | 0x<.............................................................e> |
// | 0xa0:0xbf | value of m | 0x<.............................................................m> |
mstore(ptr, 0x20)
mstore(add(ptr, 0x20), 0x20)
mstore(add(ptr, 0x40), 0x20)
mstore(add(ptr, 0x60), b)
mstore(add(ptr, 0x80), e)
mstore(add(ptr, 0xa0), m)
// Given the result < m, it's guaranteed to fit in 32 bytes,
// so we can use the memory scratch space located at offset 0.
success := staticcall(gas(), 0x05, ptr, 0xc0, 0x00, 0x20)
result := mload(0x00)
}
}
/**
* @dev Variant of {modExp} that supports inputs of arbitrary length.
*/
function modExp(bytes memory b, bytes memory e, bytes memory m) internal view returns (bytes memory) {
(bool success, bytes memory result) = tryModExp(b, e, m);
if (!success) {
Panic.panic(Panic.DIVISION_BY_ZERO);
}
return result;
}
/**
* @dev Variant of {tryModExp} that supports inputs of arbitrary length.
*/
function tryModExp(
bytes memory b,
bytes memory e,
bytes memory m
) internal view returns (bool success, bytes memory result) {
if (_zeroBytes(m)) return (false, new bytes(0));
uint256 mLen = m.length;
// Encode call args in result and move the free memory pointer
result = abi.encodePacked(b.length, e.length, mLen, b, e, m);
assembly ("memory-safe") {
let dataPtr := add(result, 0x20)
// Write result on top of args to avoid allocating extra memory.
success := staticcall(gas(), 0x05, dataPtr, mload(result), dataPtr, mLen)
// Overwrite the length.
// result.length > returndatasize() is guaranteed because returndatasize() == m.length
mstore(result, mLen)
// Set the memory pointer after the returned data.
mstore(0x40, add(dataPtr, mLen))
}
}
/**
* @dev Returns whether the provided byte array is zero.
*/
function _zeroBytes(bytes memory byteArray) private pure returns (bool) {
for (uint256 i = 0; i < byteArray.length; ++i) {
if (byteArray[i] != 0) {
return false;
}
}
return true;
}
/**
* @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded
* towards zero.
*
* This method is based on Newton's method for computing square roots; the algorithm is restricted to only
* using integer operations.
*/
function sqrt(uint256 a) internal pure returns (uint256) {
unchecked {
// Take care of easy edge cases when a == 0 or a == 1
if (a <= 1) {
return a;
}
// In this function, we use Newton's method to get a root of `f(x) := x² - a`. It involves building a
// sequence x_n that converges toward sqrt(a). For each iteration x_n, we also define the error between
// the current value as `ε_n = | x_n - sqrt(a) |`.
//
// For our first estimation, we consider `e` the smallest power of 2 which is bigger than the square root
// of the target. (i.e. `2**(e-1) ≤ sqrt(a) < 2**e`). We know that `e ≤ 128` because `(2¹²⁸)² = 2²⁵⁶` is
// bigger than any uint256.
//
// By noticing that
// `2**(e-1) ≤ sqrt(a) < 2**e → (2**(e-1))² ≤ a < (2**e)² → 2**(2*e-2) ≤ a < 2**(2*e)`
// we can deduce that `e - 1` is `log2(a) / 2`. We can thus compute `x_n = 2**(e-1)` using a method similar
// to the msb function.
uint256 aa = a;
uint256 xn = 1;
if (aa >= (1 << 128)) {
aa >>= 128;
xn <<= 64;
}
if (aa >= (1 << 64)) {
aa >>= 64;
xn <<= 32;
}
if (aa >= (1 << 32)) {
aa >>= 32;
xn <<= 16;
}
if (aa >= (1 << 16)) {
aa >>= 16;
xn <<= 8;
}
if (aa >= (1 << 8)) {
aa >>= 8;
xn <<= 4;
}
if (aa >= (1 << 4)) {
aa >>= 4;
xn <<= 2;
}
if (aa >= (1 << 2)) {
xn <<= 1;
}
// We now have x_n such that `x_n = 2**(e-1) ≤ sqrt(a) < 2**e = 2 * x_n`. This implies ε_n ≤ 2**(e-1).
//
// We can refine our estimation by noticing that the middle of that interval minimizes the error.
// If we move x_n to equal 2**(e-1) + 2**(e-2), then we reduce the error to ε_n ≤ 2**(e-2).
// This is going to be our x_0 (and ε_0)
xn = (3 * xn) >> 1; // ε_0 := | x_0 - sqrt(a) | ≤ 2**(e-2)
// From here, Newton's method give us:
// x_{n+1} = (x_n + a / x_n) / 2
//
// One should note that:
// x_{n+1}² - a = ((x_n + a / x_n) / 2)² - a
// = ((x_n² + a) / (2 * x_n))² - a
// = (x_n⁴ + 2 * a * x_n² + a²) / (4 * x_n²) - a
// = (x_n⁴ + 2 * a * x_n² + a² - 4 * a * x_n²) / (4 * x_n²)
// = (x_n⁴ - 2 * a * x_n² + a²) / (4 * x_n²)
// = (x_n² - a)² / (2 * x_n)²
// = ((x_n² - a) / (2 * x_n))²
// ≥ 0
// Which proves that for all n ≥ 1, sqrt(a) ≤ x_n
//
// This gives us the proof of quadratic convergence of the sequence:
// ε_{n+1} = | x_{n+1} - sqrt(a) |
// = | (x_n + a / x_n) / 2 - sqrt(a) |
// = | (x_n² + a - 2*x_n*sqrt(a)) / (2 * x_n) |
// = | (x_n - sqrt(a))² / (2 * x_n) |
// = | ε_n² / (2 * x_n) |
// = ε_n² / | (2 * x_n) |
//
// For the first iteration, we have a special case where x_0 is known:
// ε_1 = ε_0² / | (2 * x_0) |
// ≤ (2**(e-2))² / (2 * (2**(e-1) + 2**(e-2)))
// ≤ 2**(2*e-4) / (3 * 2**(e-1))
// ≤ 2**(e-3) / 3
// ≤ 2**(e-3-log2(3))
// ≤ 2**(e-4.5)
//
// For the following iterations, we use the fact that, 2**(e-1) ≤ sqrt(a) ≤ x_n:
// ε_{n+1} = ε_n² / | (2 * x_n) |
// ≤ (2**(e-k))² / (2 * 2**(e-1))
// ≤ 2**(2*e-2*k) / 2**e
// ≤ 2**(e-2*k)
xn = (xn + a / xn) >> 1; // ε_1 := | x_1 - sqrt(a) | ≤ 2**(e-4.5) -- special case, see above
xn = (xn + a / xn) >> 1; // ε_2 := | x_2 - sqrt(a) | ≤ 2**(e-9) -- general case with k = 4.5
xn = (xn + a / xn) >> 1; // ε_3 := | x_3 - sqrt(a) | ≤ 2**(e-18) -- general case with k = 9
xn = (xn + a / xn) >> 1; // ε_4 := | x_4 - sqrt(a) | ≤ 2**(e-36) -- general case with k = 18
xn = (xn + a / xn) >> 1; // ε_5 := | x_5 - sqrt(a) | ≤ 2**(e-72) -- general case with k = 36
xn = (xn + a / xn) >> 1; // ε_6 := | x_6 - sqrt(a) | ≤ 2**(e-144) -- general case with k = 72
// Because e ≤ 128 (as discussed during the first estimation phase), we know have reached a precision
// ε_6 ≤ 2**(e-144) < 1. Given we're operating on integers, then we can ensure that xn is now either
// sqrt(a) or sqrt(a) + 1.
return xn - SafeCast.toUint(xn > a / xn);
}
}
/**
* @dev Calculates sqrt(a), following the selected rounding direction.
*/
function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = sqrt(a);
return result + SafeCast.toUint(unsignedRoundsUp(rounding) && result * result < a);
}
}
/**
* @dev Return the log in base 2 of a positive value rounded towards zero.
* Returns 0 if given 0.
*/
function log2(uint256 x) internal pure returns (uint256 r) {
// If value has upper 128 bits set, log2 result is at least 128
r = SafeCast.toUint(x > 0xffffffffffffffffffffffffffffffff) << 7;
// If upper 64 bits of 128-bit half set, add 64 to result
r |= SafeCast.toUint((x >> r) > 0xffffffffffffffff) << 6;
// If upper 32 bits of 64-bit half set, add 32 to result
r |= SafeCast.toUint((x >> r) > 0xffffffff) << 5;
// If upper 16 bits of 32-bit half set, add 16 to result
r |= SafeCast.toUint((x >> r) > 0xffff) << 4;
// If upper 8 bits of 16-bit half set, add 8 to result
r |= SafeCast.toUint((x >> r) > 0xff) << 3;
// If upper 4 bits of 8-bit half set, add 4 to result
r |= SafeCast.toUint((x >> r) > 0xf) << 2;
// Shifts value right by the current result and use it as an index into this lookup table:
//
// | x (4 bits) | index | table[index] = MSB position |
// |------------|---------|-----------------------------|
// | 0000 | 0 | table[0] = 0 |
// | 0001 | 1 | table[1] = 0 |
// | 0010 | 2 | table[2] = 1 |
// | 0011 | 3 | table[3] = 1 |
// | 0100 | 4 | table[4] = 2 |
// | 0101 | 5 | table[5] = 2 |
// | 0110 | 6 | table[6] = 2 |
// | 0111 | 7 | table[7] = 2 |
// | 1000 | 8 | table[8] = 3 |
// | 1001 | 9 | table[9] = 3 |
// | 1010 | 10 | table[10] = 3 |
// | 1011 | 11 | table[11] = 3 |
// | 1100 | 12 | table[12] = 3 |
// | 1101 | 13 | table[13] = 3 |
// | 1110 | 14 | table[14] = 3 |
// | 1111 | 15 | table[15] = 3 |
//
// The lookup table is represented as a 32-byte value with the MSB positions for 0-15 in the last 16 bytes.
assembly ("memory-safe") {
r := or(r, byte(shr(r, x), 0x0000010102020202030303030303030300000000000000000000000000000000))
}
}
/**
* @dev Return the log in base 2, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log2(value);
return result + SafeCast.toUint(unsignedRoundsUp(rounding) && 1 << result < value);
}
}
/**
* @dev Return the log in base 10 of a positive value rounded towards zero.
* Returns 0 if given 0.
*/
function log10(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >= 10 ** 64) {
value /= 10 ** 64;
result += 64;
}
if (value >= 10 ** 32) {
value /= 10 ** 32;
result += 32;
}
if (value >= 10 ** 16) {
value /= 10 ** 16;
result += 16;
}
if (value >= 10 ** 8) {
value /= 10 ** 8;
result += 8;
}
if (value >= 10 ** 4) {
value /= 10 ** 4;
result += 4;
}
if (value >= 10 ** 2) {
value /= 10 ** 2;
result += 2;
}
if (value >= 10 ** 1) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 10, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log10(value);
return result + SafeCast.toUint(unsignedRoundsUp(rounding) && 10 ** result < value);
}
}
/**
* @dev Return the log in base 256 of a positive value rounded towards zero.
* Returns 0 if given 0.
*
* Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
*/
function log256(uint256 x) internal pure returns (uint256 r) {
// If value has upper 128 bits set, log2 result is at least 128
r = SafeCast.toUint(x > 0xffffffffffffffffffffffffffffffff) << 7;
// If upper 64 bits of 128-bit half set, add 64 to result
r |= SafeCast.toUint((x >> r) > 0xffffffffffffffff) << 6;
// If upper 32 bits of 64-bit half set, add 32 to result
r |= SafeCast.toUint((x >> r) > 0xffffffff) << 5;
// If upper 16 bits of 32-bit half set, add 16 to result
r |= SafeCast.toUint((x >> r) > 0xffff) << 4;
// Add 1 if upper 8 bits of 16-bit half set, and divide accumulated result by 8
return (r >> 3) | SafeCast.toUint((x >> r) > 0xff);
}
/**
* @dev Return the log in base 256, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log256(value);
return result + SafeCast.toUint(unsignedRoundsUp(rounding) && 1 << (result << 3) < value);
}
}
/**
* @dev Returns whether a provided rounding mode is considered rounding up for unsigned integers.
*/
function unsignedRoundsUp(Rounding rounding) internal pure returns (bool) {
return uint8(rounding) % 2 == 1;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/extensions/IERC20Metadata.sol)
pragma solidity >=0.6.2;
import {IERC20} from "../IERC20.sol";
/**
* @dev Interface for the optional metadata functions from the ERC-20 standard.
*/
interface IERC20Metadata is IERC20 {
/**
* @dev Returns the name of the token.
*/
function name() external view returns (string memory);
/**
* @dev Returns the symbol of the token.
*/
function symbol() external view returns (string memory);
/**
* @dev Returns the decimals places of the token.
*/
function decimals() external view returns (uint8);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/ReentrancyGuard.sol)
pragma solidity ^0.8.20;
/**
* @dev Contract module that helps prevent reentrant calls to a function.
*
* Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
* available, which can be applied to functions to make sure there are no nested
* (reentrant) calls to them.
*
* Note that because there is a single `nonReentrant` guard, functions marked as
* `nonReentrant` may not call one another. This can be worked around by making
* those functions `private`, and then adding `external` `nonReentrant` entry
* points to them.
*
* TIP: If EIP-1153 (transient storage) is available on the chain you're deploying at,
* consider using {ReentrancyGuardTransient} instead.
*
* TIP: If you would like to learn more about reentrancy and alternative ways
* to protect against it, check out our blog post
* https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
*/
abstract contract ReentrancyGuard {
// Booleans are more expensive than uint256 or any type that takes up a full
// word because each write operation emits an extra SLOAD to first read the
// slot's contents, replace the bits taken up by the boolean, and then write
// back. This is the compiler's defense against contract upgrades and
// pointer aliasing, and it cannot be disabled.
// The values being non-zero value makes deployment a bit more expensive,
// but in exchange the refund on every call to nonReentrant will be lower in
// amount. Since refunds are capped to a percentage of the total
// transaction's gas, it is best to keep them low in cases like this one, to
// increase the likelihood of the full refund coming into effect.
uint256 private constant NOT_ENTERED = 1;
uint256 private constant ENTERED = 2;
uint256 private _status;
/**
* @dev Unauthorized reentrant call.
*/
error ReentrancyGuardReentrantCall();
constructor() {
_status = NOT_ENTERED;
}
/**
* @dev Prevents a contract from calling itself, directly or indirectly.
* Calling a `nonReentrant` function from another `nonReentrant`
* function is not supported. It is possible to prevent this from happening
* by making the `nonReentrant` function external, and making it call a
* `private` function that does the actual work.
*/
modifier nonReentrant() {
_nonReentrantBefore();
_;
_nonReentrantAfter();
}
function _nonReentrantBefore() private {
// On the first call to nonReentrant, _status will be NOT_ENTERED
if (_status == ENTERED) {
revert ReentrancyGuardReentrantCall();
}
// Any calls to nonReentrant after this point will fail
_status = ENTERED;
}
function _nonReentrantAfter() private {
// By storing the original value once again, a refund is triggered (see
// https://eips.ethereum.org/EIPS/eip-2200)
_status = NOT_ENTERED;
}
/**
* @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
* `nonReentrant` function in the call stack.
*/
function _reentrancyGuardEntered() internal view returns (bool) {
return _status == ENTERED;
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "../00_libraries/types.sol";
import "../../interfaces/common/IStrategyFactory.sol";
import "../../interfaces/routing/RouterTypes.sol";
/**
* @title PetalsStorageLayout
* @notice Storage layout library for the upgradeable PetalsVaultController
* @dev This library defines the storage layout that will be used by all versions
* of the controller implementation. It uses the diamond storage pattern to
* prevent storage collisions and ensure upgrade compatibility.
*/
library PetalsStorageLayout {
/// @notice Unique storage slot identifier to prevent collisions
/// @dev Using a unique namespace for this protocol's storage
bytes32 constant STORAGE_SLOT = keccak256("com.petals.protocol.storage.main");
/**
* @notice Complete storage layout for the Petals Protocol
* @dev All state variables from PetalsVaultController are defined here
* This struct represents the entire persistent state of the protocol
* that will be preserved across all logic contract upgrades
*/
struct AppStorage {
// ============ Strategy Registry ============
/// @notice Mapping from strategy to vault (reverse lookup)
/// @dev Forward lookup (vault => strategy) is in vaultData[vault].strategy.strategyAddress
mapping(address => address) strategyToVault;
// ============ Access Control & Permission Registry ============
/// @notice Contract-specific role assignments: contractAddr => role => account => hasRole
mapping(address => mapping(uint64 => mapping(address => bool))) contractRoles;
// ============ Core Infrastructure ============
/// @notice The master vault implementation used for all vault clones
address masterVault;
/// @notice The data provider contract for aggregated queries
address dataProvider;
/// @notice Price oracle contract for USD valuations
address priceOracle;
/// @notice Wrapped native token address for this chain
address wrappedNativeToken;
/// @notice Fee manager contract for all fee-related operations
address feeManager;
/// @notice Access manager contract for all permission-related operations
address accessManager;
/// @notice The master address that receives ownership of registered vaults and strategies
address masterAddress;
/// @notice Timelock controller for critical operations (optional, address(0) = no timelock)
address timelockController;
// ============ Vault Registry ============
/// @notice Array of all deployed vault addresses
address[] allVaults;
/// @notice Quick lookup to verify if an address is a registered vault
mapping(address => bool) isVault;
/// @notice Comprehensive data storage for each vault
/// @dev Stores all persistent vault data (identity, metadata, metrics, strategy, UI)
mapping(address => VaultStoredData) vaultData;
// ============ Indexing System ============
/// @notice Vaults organized by underlying asset for efficient discovery
mapping(address => address[]) vaultsByAsset;
/// @notice Vaults organized by fee recipient for management interfaces
mapping(address => address[]) vaultsByFeeRecipient;
/// @notice Array of retired vault addresses for archiving
address[] retiredVaults;
/// @notice Quick check if a vault is retired
mapping(address => bool) isRetired;
// ============ Performance Tracking ============
/// @notice Running count of total vaults deployed through this controller
uint256 totalVaultsDeployed;
// ============ Duplicate Prevention ============
/// @notice Prevents duplicate registrations of the same asset+strategyType combination
/// @dev Key: [asset][strategyType] → vault
/// @dev Allows multiple strategy types per asset (e.g., USDC with Shadow-V1, Shadow-V2, Aave)
mapping(address => mapping(bytes32 => address)) assetStrategyTypeVault;
// ============ Protocol-Wide Operations ============
/// @notice Direct mapping from base protocol to all vaults using it
/// @dev Enables O(1) protocol-wide operations (panic, analytics)
mapping(bytes32 => address[]) vaultsByProtocol;
/// @notice Index mapping for efficient vault removal from protocol array
mapping(bytes32 => mapping(address => uint256)) vaultsByProtocolIndex;
// ============ Deployment Management ============
/// @notice Vault deployments queued for admin approval before registration
/// @dev Mapping-only design (no array) to prevent DOS attacks via queue spam
/// @dev Admin discovers vaults via off-chain events/indexer, approves by direct address lookup
/// @dev Multiple vaults can queue for same asset - admin chooses best during approval
mapping(address => VaultStoredData) queuedVaults;
// ============ Configurable Protocol Constants ============
/// @notice Standard basis points denominator (configurable)
uint256 basisPoints;
/// @notice Maximum slippage tolerance for strategy operations (configurable)
uint256 maxSlippage;
/// @notice Minimum delay between harvests (configurable)
uint256 minHarvestDelay;
/// @notice Default performance fee for new vaults (configurable)
uint256 defaultPerformanceFee;
/// @notice Default slippage tolerance for strategies (configurable)
uint256 defaultSlippageTolerance;
uint256 defaultCollateralFactor; // Default collateral factor for lending strategies in basis points
// ============ Essential Indexing System ============
mapping(address => uint256) allVaultsIndex;
mapping(address => mapping(address => uint256)) vaultsByAssetIndex; // asset => vault => index
// ============ Fee Config Storage ============
/// @notice Vault-specific fee configurations stored in the diamond
mapping(address => VaultFeeConfig) vaultFeeConfigs;
/// @notice Universal fee configuration used as default when no override
UniversalFeeConfig universalFeeConfig;
/// @notice Claimable fees per recipient address
/// @dev Fees accumulate here instead of direct transfer for better composability
mapping(address => uint256) claimableFees;
// ============ Routing System ============
/// @notice Registry of router libraries: RouterType => library address
mapping(RouterType => address) routerLibraries;
/// @notice Centralized fees library for all strategies
address feesLibrary;
/// @notice Last route change timestamp per strategy
mapping(address => uint256) strategyLastRouteChange;
/// @notice Total route updates per strategy
mapping(address => uint256) strategyRouteUpdateCount;
// ============ Metadata Registry (NEW) ============
/// @notice Chain metadata for this deployment (single static struct)
ChainMetadata chainMetadata;
/// @notice Token category information (dynamic registry)
mapping(bytes32 => TokenCategoryInfo) tokenCategories;
bytes32[] registeredCategories;
/// @notice Token metadata registry for UI display
mapping(address => TokenMetadata) tokenMetadata;
address[] registeredTokens;
/// @notice Tokens organized by category for filtering
mapping(bytes32 => address[]) tokensByCategory; // categoryId => tokens[]
// ============ Vault Tag Registry ============
/// @notice Vault tag definitions
mapping(bytes32 => VaultTagInfo) vaultTags;
bytes32[] registeredTags;
/// @notice Reverse index: tagId => vault addresses (for filtering)
mapping(bytes32 => address[]) vaultsByTag;
// ============ Universal Factory Registry ============
/// @notice Protocol registry: baseProtocol => registration data
/// @dev Primary access by bytes32 name (stable, readable API)
mapping(bytes32 => ProtocolRegistration) universalFactoryProtocols;
/// @notice All registered protocols (for enumeration)
/// @dev Array indices match ProtocolRegistration.protocolIndex
bytes32[] universalFactoryRegisteredProtocols;
/// @notice Strategy type registry: baseProtocol => strategyType => registration data
/// @dev Two-level mapping for protocol → type hierarchy
mapping(bytes32 => mapping(bytes32 => StrategyTypeRegistration)) universalFactoryStrategyTypes;
/// @notice Protocol strategy types (for enumeration): baseProtocol => strategyType[]
/// @dev Array indices match StrategyTypeRegistration.typeIndex
mapping(bytes32 => bytes32[]) universalFactoryProtocolStrategyTypes;
/// @notice Tracks all strategies deployed by UniversalFactory for O(1) validation
/// @dev Only UniversalFactory can set this to true, making it unspoofable
mapping(address => bool) universalFactoryDeployedStrategies;
// ============ Oracle Storage ============
/// @notice Chainlink price feeds: asset => feed address
mapping(address => address) chainlinkFeeds;
/// @notice DEX routers with their types for price discovery
OracleRouterInfo[] dexRouters;
mapping(address => bool) approvedRouters;
/// @notice Base tokens for routing (USDC, WETH, etc.)
address[] baseTokens;
mapping(address => bool) isBaseToken;
mapping(address => uint256) baseTokenPriority;
/// @notice Cached price data: asset => PriceData
mapping(address => PriceData) cachedPrices;
/// @notice LP token detection cache
mapping(address => bool) isLPTokenCache;
mapping(address => bool) isLPTokenSet;
/// @notice Manual price overrides (emergency)
mapping(address => PriceData) manualPrices;
mapping(address => bool) useManualPrice;
/// @notice Custom price staleness per asset
mapping(address => uint256) maxPriceAge;
// ============ Asset Registry Storage ============
/// @notice Asset metadata registry (LP tokens, complex assets)
/// @dev Populated by ChainRegistryFacet, queried by OracleFacet and ControllerFacet
mapping(address => AssetMetadata) assetRegistry;
/// @notice Quick lookup: Is this address a registered asset?
mapping(address => bool) isRegisteredAsset;
/// @notice Enumeration: All registered assets
address[] registeredAssets;
/// @notice Index by RouterType (for Oracle optimization)
/// @dev Allows O(1) lookup of all assets for a given DEX type
mapping(RouterType => address[]) assetsByRouterType;
// ============ Storage Gap ============
/// @notice Storage gap for future upgrades - reserves 100 slots
/// @dev This prevents storage collisions when adding new state variables in future versions
/// @dev Increased to 100 for better long-term flexibility
uint256[100] __gap;
}
/**
* @notice Get a storage pointer to the AppStorage struct
* @dev Uses assembly to return a storage pointer at the designated slot
* This is the standard diamond storage pattern for upgradeable contracts
* @return s Storage pointer to the AppStorage struct
*/
function getStorage() internal pure returns (AppStorage storage s) {
bytes32 slot = STORAGE_SLOT;
assembly {
s.slot := slot
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "../../interfaces/common/IStrategyFactory.sol";
import "../../interfaces/routing/RouterTypes.sol";
/**
* ╔══════════════════════════════════════════════════════════════════════════════╗
* ║ PETALS PROTOCOL DATA ARCHITECTURE ║
* ╚══════════════════════════════════════════════════════════════════════════════╝
*
* This file defines the complete data structure hierarchy for the Petals Protocol.
*
* ┌─────────────────────────────────────────────────────────────────────────────┐
* │ TWO MAIN DATA STRUCTURES │
* └─────────────────────────────────────────────────────────────────────────────┘
*
* 1. VaultRowData
* ├─ Purpose: Lightweight data for vault list display (/vaults page)
* ├─ Usage: Batch queries of 500+ vaults
* ├─ Contains: Essential metrics + UI branding
* └─ Returned by: DataProviderFacet.getVaultRowsBatch()
*
* 2. VaultCompleteData
* ├─ Purpose: Complete vault data for detail pages (/vaults/[id])
* ├─ Usage: Single vault detailed view
* ├─ Contains: ALL vault data in one call (stored + computed + derived)
* └─ Returned by: DataProviderFacet.getVaultData()
*
* ┌─────────────────────────────────────────────────────────────────────────────┐
* │ DATA LAYER ORGANIZATION │
* └─────────────────────────────────────────────────────────────────────────────┘
*
* LAYER 1: STORED DATA (Persisted in Diamond Storage)
* ────────────────────────────────────────────────────
* VaultStoredData
* ├─ VaultIdentity → Who/what/when (addresses, timestamps)
* ├─ VaultMetadata → Name, protocol, category, branding
* ├─ VaultMetrics → Performance history (APY, harvests)
* ├─ StrategyData → Strategy configuration
* └─ VaultUIMetadata → Logos, links, LP token info
*
* LAYER 2: COMPUTED DATA (Calculated On-Demand from Contracts)
* ────────────────────────────────────────────────────────────
* ├─ VaultStateData → Real-time vault state (totalAssets, sharePrice)
* ├─ UserPortfolioData → User-specific balances and allowances
* ├─ VaultFeeData → Fee configuration from FeeManager
* └─ HarvestData → Harvest readiness and pending rewards
*
* LAYER 3: DERIVED DATA (Computed from Logic)
* ────────────────────────────────────────────
* └─ VaultStatus → Active/Paused/Retired (enum)
*
* ┌─────────────────────────────────────────────────────────────────────────────┐
* │ DATA FLOW DIAGRAM │
* └─────────────────────────────────────────────────────────────────────────────┘
*
* Diamond Storage: vaultData[address] → VaultStoredData
* │
* ├─► VaultIdentity
* ├─► VaultMetadata
* ├─► VaultMetrics
* ├─► StrategyData
* └─► VaultUIMetadata
*
* DataProviderFacet._getVaultCompleteData() reads storage + computes real-time:
*
* VaultCompleteData
* ├─ [STORED] identity, metadata, metrics, strategy, ui
* ├─ [COMPUTED] state ──────► Query vault.totalAssets(), vault.totalSupply()
* ├─ [COMPUTED] user ───────► Query vault.balanceOf(user), asset.balanceOf(user)
* ├─ [COMPUTED] fees ───────► Query feeManager.getVaultFeeConfig(vault)
* ├─ [COMPUTED] harvest ────► Query strategy.totalRewardsAvailable()
* └─ [DERIVED] status ──────► Compute from vault.active() + strategy.paused()
*
* ┌─────────────────────────────────────────────────────────────────────────────┐
* │ FRONTEND USAGE GUIDE │
* └─────────────────────────────────────────────────────────────────────────────┘
*
* For Vault List Page (/vaults):
* ────────────────────────────────
* const rows = await dataProvider.getVaultRowsBatch(vaults, user);
* // Access: row.name, row.protocol, row.estAPY, row.vaultLogoURI
*
* For Vault Detail Page (/vaults/[id]):
* ───────────────────────────────────────
* const data = await dataProvider.getVaultData(vault, user);
* // Access all fields:
* // - data.identity.vaultAddress
* // - data.metadata.name
* // - data.ui.vaultLogoURI, data.ui.websiteURI
* // - data.state.totalAssets, data.state.sharePrice
* // - data.user.userBalance
* // - data.harvest.canHarvest
*/
// ════════════════════════════════════════════════════════════════════════════════
// MAIN DATA STRUCTURES
// ════════════════════════════════════════════════════════════════════════════════
/**
* @notice Lightweight vault data optimized for list display
* @dev Used by: Frontend /vaults page for displaying 500+ vaults efficiently
* @dev Returned by: DataProviderFacet.getVaultRowsBatch()
*
* Contains:
* - Essential identification (address, name, protocol)
* - Key metrics (APY, TVL, risk level)
* - User portfolio (balances)
* - UI branding (logos)
* - Status indicator
*/
struct VaultRowData {
// === IDENTITY ===
address vaultAddress; // Vault contract address
bytes name; // Vault name (full length, no truncation)
bytes32 protocol; // Protocol name (e.g., "Shadow", "Aave")
bytes32 category; // Category (e.g., "Stable", "Volatile")
// === KEY METRICS ===
uint256 estAPY; // Last APY in basis points (10000 = 100%)
uint256 tvlUSD; // TVL estimate in USD (6 decimals)
uint8 riskLevel; // Risk level 1-10
uint8 status; // 0=Active, 1=Paused, 2=Retired
// === USER PORTFOLIO ===
uint256 userAvailable; // User's underlying asset balance
uint256 userHoldings; // User's vault share balance
uint256 userPositionUSD; // User's vault position in USD (6 decimals) - 0 if unavailable
uint256 userAvailableUSD; // User's available balance in USD (6 decimals) - 0 if unavailable
// === USD PRICING ===
uint256 assetPriceUSD; // Asset price in USD (18 decimals) - 0 if unavailable
uint8 priceConfidence; // Oracle confidence (0-100) - 0 if no oracle
// === UI METADATA ===
bytes vaultLogoURI; // Vault logo (IPFS/HTTP) - bytes for gas efficiency
bytes protocolLogoURI; // Protocol logo (IPFS/HTTP) - bytes for gas efficiency
}
/**
* @notice Complete vault data with all information in a single structure
* @dev Used by: Frontend /vaults/[id] detail page
* @dev Returned by: DataProviderFacet.getVaultData()
*
* Architecture:
* ├─ STORED DATA (from Diamond storage - VaultStoredData)
* │ ├─ identity: Basic vault identity
* │ ├─ metadata: Name, description, branding
* │ ├─ metrics: Performance history
* │ ├─ strategy: Strategy configuration
* │ └─ ui: Logos, links, LP token info
* │
* ├─ COMPUTED DATA (from contracts - calculated on-demand)
* │ ├─ state: Real-time vault state
* │ ├─ user: User-specific portfolio data
* │ ├─ fees: Fee configuration
* │ └─ harvest: Harvest readiness
* │
* └─ DERIVED DATA (from logic)
* └─ status: Active/Paused/Retired
*/
struct VaultCompleteData {
// ═══════════════════════════════════════════════════════════════════════════
// STORED DATA (Persisted in Diamond Storage)
// ═══════════════════════════════════════════════════════════════════════════
/// @notice Basic vault identity (addresses, IDs, timestamps)
VaultIdentity identity;
/// @notice Vault metadata (name, protocol, category, descriptions)
VaultMetadata metadata;
/// @notice Performance metrics (APY history, harvest counts)
VaultMetrics metrics;
/// @notice Strategy configuration
StrategyData strategy;
/// @notice UI metadata (logos, links, LP token info)
VaultUIMetadata ui;
// ═══════════════════════════════════════════════════════════════════════════
// COMPUTED DATA (Calculated On-Demand from Contracts)
// ═══════════════════════════════════════════════════════════════════════════
/// @notice Real-time vault state (totalAssets, sharePrice, etc.)
VaultStateData state;
/// @notice User-specific portfolio data (balances, allowances)
UserPortfolioData user;
/// @notice Fee configuration (performance, withdrawal, deposit fees)
VaultFeeData fees;
/// @notice Harvest data (pending rewards, readiness)
HarvestData harvest;
// ═══════════════════════════════════════════════════════════════════════════
// DERIVED DATA (Computed from Logic)
// ═══════════════════════════════════════════════════════════════════════════
/// @notice Computed vault status
VaultStatus status;
}
// ════════════════════════════════════════════════════════════════════════════════
// STORED SUB-STRUCTURES (Layer 1: Persisted in Diamond Storage)
// ════════════════════════════════════════════════════════════════════════════════
/**
* @notice Complete stored vault data (what's actually in Diamond storage)
* @dev This is what gets stored in: PetalsStorageLayout.vaultData[address]
* @dev Contains all persistent vault information that survives between transactions
*
* Used internally by:
* - ControllerFacet (for registration and updates)
* - DataProviderFacet (for reading and building VaultCompleteData)
*
* ┌──────────────────────────────────────────────────────────────────────────────┐
* │ STORAGE OWNERSHIP - CRITICAL: DO NOT VIOLATE THIS SEPARATION │
* ├──────────────┬───────────────────────┬────────────────────────────────────────┤
* │ Field │ Written By │ When │
* ├──────────────┼───────────────────────┼────────────────────────────────────────┤
* │ identity │ ControllerFacet │ Registration (once) │
* │ metadata │ VaultMetadataFacet │ Initialization & updates │
* │ metrics │ ControllerFacet │ Registration + after harvests │
* │ strategy │ ControllerFacet │ Registration (once) │
* │ ui │ VaultMetadataFacet │ Initialization & updates │
* └──────────────┴───────────────────────┴────────────────────────────────────────┘
*
* IMPORTANT RULES:
* 1. ControllerFacet writes: identity, strategy (immutable after registration)
* 2. ControllerFacet writes: metrics (updated after harvests)
* 3. VaultMetadataFacet writes: metadata, ui (mutable, admin-controlled)
* 4. NO OTHER FACETS should write to these fields
* 5. Initialization order: ControllerFacet (core) → VaultMetadataFacet (properties)
*
* Violating this can cause:
* - Data corruption (two facets writing same field)
* - Broken initialization (properties set before core data)
* - Audit failures (unclear ownership boundaries)
*/
struct VaultStoredData {
VaultIdentity identity; // ControllerFacet ONLY - immutable after registration
VaultMetadata metadata; // VaultMetadataFacet ONLY - mutable
VaultMetrics metrics; // ControllerFacet ONLY - updated after harvests
StrategyData strategy; // ControllerFacet ONLY - immutable after registration
VaultUIMetadata ui; // VaultMetadataFacet ONLY - mutable
}
/**
* @notice Basic vault identity information
* @dev Core identification fields for vault tracking
*/
struct VaultIdentity {
address vaultAddress; // Vault contract address
bytes32 vaultId; // Unique vault identifier
uint256 createdAt; // Vault creation timestamp
uint256 registeredAt; // Registration timestamp in controller
address deployer; // Address that deployed the vault
}
/**
* @notice Vault metadata for display and categorization
* @dev Human-readable information about the vault
*/
struct VaultMetadata {
bytes name; // Vault name - dynamic length for complex multi-token pools (e.g., "BeethovenX USDC-DAI-USDT Stable Pool")
bytes32 symbol; // Vault symbol (e.g., "P-wS-SHADOW") - always short, keep bytes32
bytes32 protocol; // Protocol name (e.g., "Shadow", "Aave") - always short, keep bytes32
bytes32 category; // Category (e.g., "Stable", "Volatile", "Blue-chip") - always short, keep bytes32
uint8 riskLevel; // Risk level 1-10
bytes description; // Vault description
bytes strategyDescription; // Strategy description
}
/**
* @notice Performance metrics tracked over time
* @dev Updated after each harvest to maintain historical performance data
*/
struct VaultMetrics {
uint256 lastAPY; // APY from most recent harvest (basis points)
uint256 totalHarvests; // Total number of harvests executed
uint64 lastHarvest; // Timestamp of last harvest
uint256 supplyApy; // Supply APY for lending strategies
uint256 borrowApy; // Borrow APY for lending strategies
uint256 _reserved1; // Reserved for future metrics
uint256 _reserved2; // Reserved for future metrics
}
/**
* @notice Strategy configuration and deployment info
* @dev Static information about the strategy contract
*/
struct StrategyData {
address strategyAddress; // Strategy contract address
address underlyingAsset; // Asset the strategy manages
bytes32 strategyType; // Strategy type identifier (e.g., "Shadow-Volatile-V1")
uint64 registeredAt; // When strategy was registered
address deployer; // Who deployed the strategy
uint256 collateralFactor; // Collateral factor for lending (basis points)
uint16 slippageTolerance; // Slippage tolerance in basis points (0-1000 = 0-10%)
}
/**
* @notice UI metadata for frontend display
* @dev All data needed for rich vault UI (logos, links, LP token info)
* @dev Uses bytes instead of string for gas efficiency (~50% savings)
* @dev Frontend decodes with ethers.toUtf8String(bytesData)
*
* Contains:
* - Branding: Vault and protocol logos
* - Links: Website, docs, social media (in socials array)
* - LP Info: Underlying token addresses and metadata (supports N tokens for Curve/Balancer/BeethovenX)
*
* Use Cases:
* - Display vault branding on list and detail pages
* - Provide direct links to protocol websites/docs
* - Show LP token composition without additional RPC calls
* - Enable token selection dropdowns with proper symbols and logos
* - Support multi-token pools (2+ tokens)
*
* Socials Array Convention:
* - socials[0] = website
* - socials[1] = docs
* - socials[2] = discord
* - socials[3] = twitter
*
* Multi-Token Pool Support:
* - For UniV2/Solidly pairs: lpTokens.length = 2
* - For Curve 3pool: lpTokens.length = 3
* - For Balancer weighted pools: lpTokens.length = 2-8
* - Arrays are parallel: lpTokens[i], lpTokenSymbols[i], lpTokenLogos[i]
*/
struct VaultUIMetadata {
// === BRANDING ===
bytes vaultLogoURI; // Vault-specific logo (IPFS/HTTP)
bytes protocolLogoURI; // Protocol logo
// === LINKS (Array for gas efficiency) ===
bytes[] socials; // [website, docs, discord, twitter]
// === LP TOKEN INFO (flexible for multi-token pools) ===
address[] lpTokens; // Underlying token addresses (2+ for multi-token pools)
bytes32[] lpTokenSymbols; // Cached symbols (parallel to lpTokens)
bytes[] lpTokenLogos; // Logo URIs (parallel to lpTokens)
// === TAGS (for filtering and discovery) ===
bytes32[] tags; // Tag IDs applied to this vault (e.g., [TAG_LP_STRATEGY, TAG_AUDITED])
}
// ════════════════════════════════════════════════════════════════════════════════
// COMPUTED SUB-STRUCTURES (Layer 2: Calculated On-Demand from Contracts)
// ════════════════════════════════════════════════════════════════════════════════
/**
* @notice Real-time vault state data
* @dev Computed by querying vault and strategy contracts
* @dev NOT stored - calculated fresh on each DataProviderFacet.getVaultData() call
*
* Data Sources:
* - vault.totalAssets()
* - vault.totalSupply()
* - strategy.totalAssets()
* - vault.maxDeposit(user)
* - oracle.getPrice(asset)
*/
struct VaultStateData {
uint256 totalAssets; // Total assets in vault (underlying asset)
uint256 totalSupply; // Total vault token supply
uint256 strategyBalance; // Assets deployed in strategy
uint256 sharePrice; // Current share price (18 decimals)
uint256 tvlEstimateUSD; // Oracle-based TVL estimate (6 decimals)
uint256 maxDeposit; // Maximum deposit allowed (ERC4626)
uint256 maxWithdraw; // Maximum withdrawal allowed (ERC4626)
uint256 maxRedeem; // Maximum redemption allowed (ERC4626)
bytes32 assetSymbol; // Asset symbol (gas-efficient bytes32)
uint8 assetDecimals; // Asset decimal places
bool vaultActive; // Vault operational status
bool strategyPaused; // Strategy pause status
uint256 assetPriceUSD; // Asset price in USD (18 decimals) - 0 if unavailable
uint8 priceConfidence; // Oracle confidence level (0-100) - 0 if no oracle
}
/**
* @notice User-specific portfolio data
* @dev Computed by querying vault and asset contracts for user balances
* @dev Pass address(0) to skip user-specific data
*
* Data Sources:
* - vault.balanceOf(user)
* - asset.balanceOf(user)
* - asset.allowance(user, vault)
*/
struct UserPortfolioData {
uint256 userBalance; // User's vault share balance
uint256 userAssetBalance; // User's underlying asset balance
uint256 userAllowance; // User's asset allowance to vault
uint256 positionValueUSD; // User's vault position value in USD (6 decimals) - 0 if unavailable
uint256 availableValueUSD; // User's available asset balance in USD (6 decimals) - 0 if unavailable
}
/**
* @notice Fee configuration data
* @dev Computed by querying fee configuration from Diamond storage
* @dev Only performance fees are charged - deposit/withdrawal fees removed
*
* Data Sources:
* - s.vaultFeeConfigs[vault] or s.universalFeeConfig
* - controller.defaultPerformanceFee (fallback)
*/
struct VaultFeeData {
uint256 performanceFee; // Performance fee in basis points (only fee charged)
uint256 supplyApy; // Supply APY for lending strategies
uint256 borrowApy; // Borrow APY for lending strategies
uint256 collateralFactor; // Collateral factor for lending (basis points)
}
/**
* @notice Harvest readiness and reward data
* @dev Computed by querying strategy contract for pending rewards
* @dev Used to determine if harvest() can be called profitably
*
* Data Sources:
* - strategy.totalRewardsAvailable()
* - strategy.lastHarvest()
* - strategy.paused()
*/
struct HarvestData {
uint256 pendingRewards; // Total pending rewards available
uint256 lastHarvestTime; // Timestamp of last harvest
uint256 timeSinceLastHarvest; // Calculated time since last harvest
uint256 totalHarvests; // Total number of harvests executed
address[] rewardTokens; // Array of reward token addresses
uint256[] rewardAmounts; // Array of pending reward amounts
bool canHarvest; // Whether harvest can be called now
uint256 callRewardValueUSD; // Harvester reward value in USD (6 decimals) - 0 if unavailable
uint256 pendingRewardsValueUSD; // Total pending rewards value in USD (6 decimals) - 0 if unavailable
}
// ════════════════════════════════════════════════════════════════════════════════
// DERIVED DATA (Layer 3: Computed from Logic)
// ════════════════════════════════════════════════════════════════════════════════
/**
* @notice Vault status enumeration
* @dev Computed from vault.active() and strategy.paused()
*/
enum VaultStatus {
Active, // Vault is active and strategy is not paused
Paused, // Strategy is paused (deposits/withdrawals may be restricted)
Retired // Vault is retired (no longer operational)
}
// ════════════════════════════════════════════════════════════════════════════════
// METADATA REGISTRY STRUCTURES
// ════════════════════════════════════════════════════════════════════════════════
/**
* @notice Chain metadata for the current deployment
* @dev Each Diamond deployment is chain-specific, so only one ChainMetadata per deployment
* @dev Stored once per chain, provides UI with chain-specific information
*
* Use Cases:
* - Display chain name and logo in UI
* - Provide explorer links for contract addresses
* - Enable wallet RPC configuration
*/
struct ChainMetadata {
uint256 chainId; // Chain ID (e.g., 146 for Sonic)
bytes32 name; // Chain name (e.g., "Sonic")
bytes32 nativeCurrency; // Native currency symbol (e.g., "S")
bytes logoURI; // Chain logo (IPFS/HTTP)
bytes explorerBaseURL; // Block explorer base URL (e.g., "https://sonicscan.org")
bytes rpcURL; // RPC endpoint for wallet connections
bool isSet; // Whether metadata has been configured
}
/**
* @notice Dynamic token category information
* @dev Replaces enum to allow runtime category management
* @dev Categories can be added/updated without contract redeployment
*
* Use Cases:
* - Filter tokens by category in UI
* - Organize token selection dropdowns
* - Display category-specific metadata
*/
struct TokenCategoryInfo {
bytes32 categoryId; // Unique identifier (e.g., keccak256("STABLECOIN"))
bytes32 displayName; // UI display name (e.g., "Stablecoin")
bytes description; // Category description for UI
bool isActive; // Whether category is active
uint256 createdAt; // Creation timestamp
}
/**
* @notice Complete token metadata for UI display
* @dev Stored per token address in MetadataRegistry
* @dev Uses bytes instead of string for gas efficiency
*
* Use Cases:
* - Display token symbols and logos in vault list
* - Enable "Create Vault" UI with token dropdowns
* - Show rich token information in AssetsTab
* - Filter tokens by category
*/
struct TokenMetadata {
address tokenAddress; // Token contract address
bytes32 symbol; // Token symbol (e.g., "USDC") - bytes32 for gas efficiency
bytes32 name; // Token name (e.g., "USD Coin")
uint8 decimals; // Token decimals
bytes32 category; // References TokenCategoryInfo.categoryId
bytes logoURI; // Token logo (IPFS/HTTP) - bytes for flexibility
bytes description; // Rich description for AssetsTab (supports markdown)
bool isActive; // Whether token is active for new vaults
uint256 addedAt; // Timestamp when token was registered
}
// Default category constants for initialization
bytes32 constant CATEGORY_STABLECOIN = keccak256("STABLECOIN");
bytes32 constant CATEGORY_GOVERNANCE = keccak256("GOVERNANCE");
bytes32 constant CATEGORY_LP_TOKEN = keccak256("LP_TOKEN");
bytes32 constant CATEGORY_WRAPPED_NATIVE = keccak256("WRAPPED_NATIVE");
bytes32 constant CATEGORY_YIELD_BEARING = keccak256("YIELD_BEARING");
bytes32 constant CATEGORY_BRIDGE_TOKEN = keccak256("BRIDGE_TOKEN");
/**
* @notice Vault tag information for filtering and discovery
* @dev Tags are dynamic, reusable labels that can be applied to multiple vaults
* @dev Examples: "LP Strategy", "Single Asset", "Lending"
*
* Use Cases:
* - Filter vaults by strategy type
* - Display strategy badges in UI
* - Group vaults by characteristics
* - Enable advanced search and discovery
*/
struct VaultTagInfo {
bytes32 tagId; // Unique identifier (e.g., keccak256("LP_STRATEGY"))
bytes32 displayName; // UI display name (e.g., "LP Strategy")
bytes description; // Tag description for tooltips
bool isActive; // Whether tag is active
}
// Minimal tag constants for auto-tagging logic (used by ControllerFacet)
bytes32 constant TAG_LP_STRATEGY = keccak256("LP_STRATEGY");
bytes32 constant TAG_SINGLE_ASSET = keccak256("SINGLE_ASSET");
bytes32 constant TAG_LENDING = keccak256("LENDING");
// ════════════════════════════════════════════════════════════════════════════════
// FEE SYSTEM STRUCTURES
// ════════════════════════════════════════════════════════════════════════════════
/**
* @notice Fee configuration for a specific vault
* @dev Simplified to only performance fees - deposit/withdrawal fees removed (unused)
*/
struct VaultFeeConfig {
uint16 performanceFee; // Performance fee in basis points (only fee charged)
address[] recipients; // Fee recipients
uint256[] ratios; // Fee ratios (basis points, should sum to 10000)
bytes32[] labels; // Labels for identification
uint256 lastUpdated; // Last update timestamp
bool isOverride; // Whether this overrides universal config
}
/**
* @notice Universal fee configuration (default for all vaults)
* @dev Used when vault doesn't have a specific fee override
*/
struct UniversalFeeConfig {
address[] recipients; // Fee recipients
uint256[] ratios; // Fee ratios (basis points, should sum to 10000)
bytes32[] labels; // Labels for identification
uint256 lastUpdated; // Last update timestamp
}
// Note: ProtocolInfo struct removed - use UniversalFactory instead
// Migration: UniversalFactoryFacet.getProtocolRegistration(baseProtocol)
// ════════════════════════════════════════════════════════════════════════════════
// SHARED EVENTS
// ════════════════════════════════════════════════════════════════════════════════
/// @notice Vault operations
event VaultDeployed(
address indexed vault,
address indexed strategy,
address indexed asset,
bytes name, // Full-length name (no truncation)
bytes32 symbol,
bytes32 strategyType,
bytes32 baseProtocol
);
event VaultUpdated(address indexed vault, uint256 indexed updateType, bytes data);
event HarvestExecuted(address indexed vault, address indexed harvester, uint256 yield, uint256 lastAPY, uint256 totalAssets);
event FeeConfigUpdated(address indexed vault, VaultFeeConfig config, bool isUniversal);
event ProtocolConfigured(
bytes32 indexed protocolType, address indexed factory, address indexed implementation, bool isActive
);
event ProtocolConstantsUpdated(
uint256 basisPoints, uint256 maxSlippage, uint256 minHarvestDelay, uint256 defaultPerformanceFee, uint256 defaultSlippageTolerance
);
event RolesTransferredOnRegistration(
address indexed vault, address indexed strategy, bool managerRole, bool emergencyRole
);
event VaultUIMetadataUpdated(address indexed vault, VaultUIMetadata uiMetadata);
event VaultMetadataUpdated(
address indexed vault,
bytes name,
bytes32 protocol,
bytes32 category,
uint8 riskLevel
);
event VaultTagsUpdated(address indexed vault, bytes32[] oldTags, bytes32[] newTags);
/// @notice Strategy operations
event StrategyUpdated(address indexed newStrategy);
event StrategyConfigUpdated(uint8 indexed updateType, bytes data);
event StrategyDeployed(address indexed strategy, bytes32 indexed protocolType, address indexed deployer);
event StrategyDeployedDetailed(
address indexed strategy,
address indexed vault,
bytes32 indexed strategyType,
address deployer,
address asset,
uint256 timestamp
);
/// @notice Deployment queue operations
event DeploymentQueued(address indexed vault, address indexed strategy, address indexed asset, address deployer);
event DeploymentConfirmed(address indexed vault, bool managerRoleTransferred, bool emergencyRoleTransferred);
event DeploymentRejected(address indexed vault, address indexed strategy, address indexed asset);
/// @notice Permission system
event RoleCacheUpdated(bytes32 indexed role, address indexed account, bool granted);
event FeeRecipientUpdated(address indexed previousFeeRecipient, address indexed newFeeRecipient);
/// @notice Rewards and routing
event RewardTokensSet(address[] rewardTokens);
event RewardConfigurationUpdated(address[] rewardTokens, address[][] routes);
event NativeToLp0RouteSet(address[] route);
event NativeToLp1RouteSet(address[] route);
event EmergencyWithdrawFailed(bytes reason);
// ════════════════════════════════════════════════════════════════════════════════
// SHARED ERRORS
// ════════════════════════════════════════════════════════════════════════════════
/// @notice General errors
error ZeroAddress();
error InvalidAsset();
error InvalidRouter();
error InvalidStrategy(address strategy);
error InvalidVault(address vault);
error InvalidShadowToken();
error InvalidRecipient();
error OnlyVault();
error VaultNotFound(address vault);
error StrategyNotFound();
error ProtocolNotFound(bytes32 protocol);
error ProtocolInactive(bytes32 protocol);
error AlreadyInitialized();
error UnauthorizedUpdate();
error EmptyArray();
error NoRewardTokens();
error NativeTokenNotConfigured();
/// @notice Fee system errors
error FeeTooHigh(uint256 fee, uint256 maxFee);
error TotalFeesExceeded(uint256 totalFees, uint256 maxFees);
error InvalidConfiguration();
/// @notice Registration errors
error VaultAlreadyRegistered();
error VaultNotConnectedToStrategy();
error StrategyOwnershipTransferFailed();
error DuplicateRegistration(address asset, bytes32 strategyType);
/// @notice Queue system errors
error DeploymentAlreadyQueued();
error DeploymentNotQueued();
error DeploymentAlreadyRegistered();
error NotValidDeployment();
// ============================================================================
// UNIVERSAL FACTORY TYPES
// ============================================================================
/// @notice Protocol registration in UniversalFactory
struct ProtocolRegistration {
bytes32 baseProtocol; // Protocol identifier (e.g., "Shadow")
uint256 protocolIndex; // Index in enumeration array
bytes description; // Protocol description
bool isActive; // Whether protocol accepts new deployments
bool exists; // Whether protocol is registered
uint256 registeredAt; // Registration timestamp
uint256 totalDeployments; // Total strategies deployed across all types
bytes logoURI; // Protocol logo (IPFS/HTTP) for UI display
bytes[] socials; // Social links [website, docs, discord, twitter]
}
/// @notice Strategy type registration in UniversalFactory
struct StrategyTypeRegistration {
bytes32 strategyType; // Strategy type identifier (e.g., "Shadow-Volatile")
uint256 typeIndex; // Index within protocol's strategy types
address implementation; // Master implementation address
bytes name; // Human-readable name
bytes description; // Strategy description
bool isActive; // Whether type accepts new deployments
bool exists; // Whether type is registered
uint256 registeredAt; // Registration timestamp
uint256 deployCount; // Number of strategies deployed
}
// ============================================================================
// ORACLE TYPES
// ============================================================================
/// @notice Price data with metadata
struct PriceData {
uint256 price; // Price in USD (18 decimals)
uint8 confidence; // Confidence level (0-100)
uint256 lastUpdate; // Last update timestamp
bytes32 method; // Pricing method used
}
/// @notice Router info for Oracle price queries
struct OracleRouterInfo {
address routerAddress; // DEX router address
RouterType routerType; // Router type (SOLIDLY, UNISWAP_V2, etc.)
}
/// @notice LP token data structure (supports multi-token pools)
/// @dev Uses arrays to support 2-token pairs AND multi-token pools (Balancer, Curve)
struct LPTokenData {
bool isLP; // Is this an LP token?
address[] tokens; // ALL tokens (2 for UniV2/Solidly, 3+ for Balancer/Curve)
uint256[] reserves; // ALL reserves (parallel to tokens array)
uint256 totalSupply; // Total LP token supply
bool isStable; // Stable vs volatile pool
bytes metadata; // Protocol-specific data (poolId, fees, etc.)
}
/// @notice Asset type classification
enum AssetType {
STANDALONE_TOKEN, // Regular ERC20 (USDC, WETH, SHADOW)
LP_TOKEN_2, // 2-token LP (UniswapV2, Solidly pairs)
LP_TOKEN_MULTI, // Multi-token LP (Balancer, Curve pools with 3+ tokens)
YIELD_BEARING // Yield-bearing tokens (aUSDC, cDAI - future support)
}
/// @notice Complete asset metadata for registry
/// @dev Used by ChainRegistryFacet for explicit asset registration
struct AssetMetadata {
address assetAddress; // Asset contract address
AssetType assetType; // Type classification
RouterType routerType; // For LP tokens: which DEX/router type
// Composition (for LP tokens)
address[] componentTokens; // Underlying tokens (e.g., [SHADOW, wSONIC])
// Display metadata
bytes32 symbol; // Asset symbol (e.g., "SHADOW-wSONIC-LP")
bytes name; // Full name for UI display
bytes logoURI; // Asset logo URI (IPFS/HTTP)
// Categorization & Status
bytes32 category; // Category ID (references TokenCategoryInfo)
bool isActive; // Whether asset can be used for new vaults
uint256 registeredAt; // Registration timestamp
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "../storage/PetalsStorageLayout.sol";
/**
* @title RoleChecker
* @author Petals Protocol
* @notice Shared access control library for all Diamond facets
* @dev Provides centralized role checking logic to avoid duplication across facets
*
* Named RoleChecker (not AccessControl) to avoid conflicts with OpenZeppelin's AccessControl contract
*
* Usage:
* import "../../00_libraries/RoleChecker.sol";
*
* modifier onlyRole(uint64 role) {
* if (!RoleChecker.hasRole(role, msg.sender)) {
* revert UnauthorizedUpdate();
* }
* _;
* }
*/
library RoleChecker {
/// @notice Master role constant (admin/owner role)
uint64 internal constant MASTER_ROLE = 0;
/// @notice Strategist role constant
uint64 internal constant STRATEGIST_ROLE = 1;
/// @notice Guardian role constant (protocol-wide emergency powers)
uint64 internal constant GUARDIAN_ROLE = 2;
/**
* @notice Check if an account has a specific role
* @dev Checks in order:
* 1. Is account the master address for MASTER_ROLE?
* 2. Does account have role on this contract (address(this))?
* 3. Does account have global role (address(0))?
* @param role Role ID to check
* @param account Account address to check
* @return Whether account has the role
*/
function hasRole(uint64 role, address account) internal view returns (bool) {
PetalsStorageLayout.AppStorage storage s = PetalsStorageLayout.getStorage();
// Master address always has MASTER_ROLE
if (role == MASTER_ROLE && account == s.masterAddress) return true;
// Check contract-specific role
if (s.contractRoles[address(this)][role][account]) return true;
// Check global role (address(0) = all contracts)
if (s.contractRoles[address(0)][role][account]) return true;
return false;
}
// Note: requireRole() function removed - use hasRole() with custom errors instead
// Example: if (!RoleChecker.hasRole(role, msg.sender)) revert UnauthorizedUpdate();
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "./RouterTypes.sol";
import "../../vaults/00_libraries/types.sol";
/**
* @title IRouterLibrary
* @notice Standard interface for router-specific encoding/decoding libraries
* @dev Each router type (Solidly, UniV2, UniV3, etc.) implements this interface
*
* Architecture:
* - Libraries are external contracts registered in RoutingFacet
* - Each library handles encoding/decoding for its specific router type
* - Validation, encoding, and decoding are type-safe and router-specific
*/
interface IRouterLibrary {
/**
* @notice Get the router type this library handles
* @return The RouterType enum value
*/
function getRouterType() external pure returns (RouterType);
/**
* @notice Validate route structure before encoding
* @dev Called by RoutingFacet before applying routes
* @param routeData ABI-encoded route data (library-specific format)
* @return valid Whether the routes are valid
* @return reason Error message if invalid
*/
function validateRoutes(
bytes calldata routeData
) external view returns (bool valid, string memory reason);
/**
* @notice Encode routes from library-specific format to bytes for storage
* @dev Called by RoutingFacet after validation
* @param routeData ABI-encoded route data (library-specific format)
* @return rewardRoutes Array of encoded reward token routes
* @return nativeToLp0 Encoded route from native token to LP token0
* @return nativeToLp1 Encoded route from native token to LP token1
*/
function encodeRoutes(
bytes calldata routeData
) external pure returns (
bytes[] memory rewardRoutes,
bytes memory nativeToLp0,
bytes memory nativeToLp1
);
/**
* @notice Decode routes from storage bytes to library-specific format
* @dev Called by view functions to return type-safe data to frontend
* @param rewardRoutes Array of encoded reward routes
* @param nativeToLp0 Encoded native->LP0 route
* @param nativeToLp1 Encoded native->LP1 route
* @return routeData ABI-encoded decoded data (library-specific format)
*/
function decodeRoutes(
bytes[] calldata rewardRoutes,
bytes calldata nativeToLp0,
bytes calldata nativeToLp1
) external pure returns (bytes memory routeData);
/**
* @notice Simulate a route to estimate output (optional, for advanced validation)
* @dev May not be implemented by all libraries
* @param route Single encoded route to simulate
* @param amountIn Input amount to simulate
* @param router Router address to query
* @return expectedOutput Expected output amount
*/
function simulateRoute(
bytes calldata route,
uint256 amountIn,
address router
) external view returns (uint256 expectedOutput);
// ============ EXECUTION FUNCTIONS ============
/**
* @notice Execute a swap operation through the router
* @dev Library handles approvals, route conversion, and execution
* @param router DEX router address (e.g., Shadow, Velodrome, Aerodrome for Solidly type)
* @param route Encoded route data (library-specific format)
* @param amountIn Amount of input tokens to swap
* @param amountOutMin Minimum amount of output tokens (slippage protection)
* @param to Recipient address for output tokens
* @return amountOut Actual amount of output tokens received
*/
function swap(
address router,
bytes calldata route,
uint256 amountIn,
uint256 amountOutMin,
address to
) external returns (uint256 amountOut);
/**
* @notice Add liquidity to a pool through the router
* @dev Library handles approvals, optimal amounts, and execution
* @param router DEX router address
* @param liquidityData Encoded liquidity parameters (library-specific format)
* @param amount0 Amount of token0 to add
* @param amount1 Amount of token1 to add
* @param minAmount0 Minimum amount of token0 (slippage protection)
* @param minAmount1 Minimum amount of token1 (slippage protection)
* @param to Recipient address for LP tokens
* @return lpReceived Amount of LP tokens received
*/
function addLiquidity(
address router,
bytes calldata liquidityData,
uint256 amount0,
uint256 amount1,
uint256 minAmount0,
uint256 minAmount1,
address to
) external returns (uint256 lpReceived);
/**
* @notice Remove liquidity from a pool through the router
* @dev Library handles approvals and execution
* @param router DEX router address
* @param liquidityData Encoded liquidity parameters (library-specific format)
* @param lpAmount Amount of LP tokens to remove
* @param minAmount0 Minimum amount of token0 to receive
* @param minAmount1 Minimum amount of token1 to receive
* @param to Recipient address for tokens
* @return amount0 Amount of token0 received
* @return amount1 Amount of token1 received
*/
function removeLiquidity(
address router,
bytes calldata liquidityData,
uint256 lpAmount,
uint256 minAmount0,
uint256 minAmount1,
address to
) external returns (uint256 amount0, uint256 amount1);
// ============ PRICE QUERY FUNCTIONS (For Oracle Integration) ============
/**
* @notice Query price from router (view only, for Oracle)
* @dev Each library implements router-specific price query logic
* @param router DEX router address
* @param tokenIn Input token address
* @param tokenOut Output token address
* @param amountIn Amount of input tokens
* @return amountOut Expected output amount (0 if route doesn't exist)
*/
function getPrice(
address router,
address tokenIn,
address tokenOut,
uint256 amountIn
) external view returns (uint256 amountOut);
/**
* @notice Get complete LP token data for Oracle pricing
* @dev Detects and returns LP token composition with reserves
* @dev Returns empty/false data if asset is not an LP token for this router type
* @param asset Asset address to query
* @return data Complete LP token data (isLP=false if not an LP)
*/
function getLPData(address asset) external view returns (LPTokenData memory data);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
* @title RouterTypes
* @notice Shared type definitions for the extensible routing system
* @dev Enums and structs used across routing libraries and facets
*/
/// @notice Supported router types for different DEX protocols
enum RouterType {
SOLIDLY, // Shadow, Velodrome, Aerodrome, Thena (Solidly forks)
UNIV2, // UniswapV2, SushiSwap, PancakeSwap
UNIV3, // UniswapV3, PancakeSwap V3
UNIVERSAL, // Uniswap Universal Router
CURVE, // Curve Finance
BALANCER // Balancer
}
/// @notice Metadata about a route configuration update
struct RouteUpdateMetadata {
uint256 timestamp; // When the routes were last updated
address updatedBy; // Who updated the routes
uint256 updateCount; // Number of times routes have been updated
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "../../vaults/00_libraries/types.sol";
/// @title IPriceOracle
/// @notice Comprehensive price oracle interface for LP tokens and exotic assets
interface IPriceOracle {
/// @notice Get USD price for any asset (ERC20, LP token, exotic)
/// @param asset Asset address
/// @return price Price in USD (18 decimals)
/// @return confidence Confidence level (0-100, 100 = highest confidence)
function getPrice(address asset) external view returns (uint256 price, uint8 confidence);
/// @notice Check if price data is fresh and reliable
/// @param asset Asset address
/// @return isValid Whether price is fresh and valid
/// @return lastUpdate Timestamp of last price update
function isPriceValid(address asset) external view returns (bool isValid, uint256 lastUpdate);
/// @notice Get detailed price information
/// @param asset Asset address
/// @return data Complete price data structure
function getPriceData(address asset) external view returns (PriceData memory data);
/// @notice Check if asset is an LP token
/// @param asset Asset address
/// @return isLP Whether the asset is an LP token
function isLPToken(address asset) external view returns (bool isLP);
/// @notice Get LP token data and pricing
/// @param lpToken LP token address
/// @return data LP token data structure
function getLPTokenData(address lpToken) external view returns (LPTokenData memory data);
/// @notice Calculate LP token price
/// @param lpToken LP token address
/// @return price LP token price in USD
function calculateLPTokenPrice(address lpToken) external view returns (uint256 price);
/// @notice Get price from DEX router
/// @param token Token address
/// @param baseToken Base token address
/// @param dexRouter DEX router address
/// @return price Token price in base token
function getDEXPrice(address token, address baseToken, address dexRouter) external view returns (uint256 price);
/// @notice Get TWAP price
/// @param token Token address
/// @param baseToken Base token address
/// @param dexRouter DEX router address
/// @param period TWAP period in seconds
/// @return price TWAP price
function getTWAPPrice(
address token,
address baseToken,
address dexRouter,
uint256 period
)
external
view
returns (uint256 price);
/// @notice Get prices from all available sources
/// @param asset Asset address
/// @return prices Array of prices from different sources
/// @return methods Array of pricing methods used
/// @return confidences Array of confidence levels
function getMultiSourcePrice(address asset)
external
view
returns (uint256[] memory prices, string[] memory methods, uint8[] memory confidences);
/// @notice Get supported pricing methods for an asset
/// @param asset Asset address
/// @return methods Array of supported methods
function getSupportedMethods(address asset) external view returns (string[] memory methods);
}
/// @title IUniswapV2Pair - Interface for Uniswap V2 style pairs
interface IUniswapV2Pair {
function token0() external view returns (address);
function token1() external view returns (address);
function getReserves() external view returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast);
function totalSupply() external view returns (uint256);
function factory() external view returns (address);
}
/// @title IUniswapV2Router - Interface for Uniswap V2 style routers
interface IUniswapV2Router {
function getAmountsOut(
uint256 amountIn,
address[] calldata path
)
external
view
returns (uint256[] memory amounts);
function getAmountsIn(
uint256 amountOut,
address[] calldata path
)
external
view
returns (uint256[] memory amounts);
function WETH() external pure returns (address);
}
/// @title ISolidlyPair - Interface for Solidly style pairs (Velodrome, Aerodrome, etc.)
interface ISolidlyPair {
function token0() external view returns (address);
function token1() external view returns (address);
function getReserves() external view returns (uint256 reserve0, uint256 reserve1, uint256 blockTimestampLast);
function totalSupply() external view returns (uint256);
function stable() external view returns (bool);
function metadata()
external
view
returns (uint256 dec0, uint256 dec1, uint256 r0, uint256 r1, bool st, address t0, address t1);
}
/// @title IAggregatorV3Interface - Chainlink price feed interface
interface IAggregatorV3Interface {
function decimals() external view returns (uint8);
function description() external view returns (string memory);
function version() external view returns (uint256);
function latestRoundData()
external
view
returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound);
}
/// @title IPriceOracleForDataProvider - Simplified interface for DataProviderFacet integration
/// @notice Used by DataProviderFacet to fetch USD prices without full oracle complexity
interface IPriceOracleForDataProvider {
/// @notice Get USD price and confidence for any asset
function getPrice(address asset) external view returns (uint256 price, uint8 confidence);
/// @notice Get detailed price data including method used
function getPriceData(address asset) external view returns (PriceData memory);
/// @notice Check if asset is an LP token
function isLPToken(address asset) external view returns (bool);
/// @notice Calculate LP token price from underlying reserves
function calculateLPTokenPrice(address lpToken) external view returns (uint256 price);
/// @notice Check if price is valid and fresh
function isPriceValid(address asset) external view returns (bool isValid, uint256 lastUpdate);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/Panic.sol)
pragma solidity ^0.8.20;
/**
* @dev Helper library for emitting standardized panic codes.
*
* ```solidity
* contract Example {
* using Panic for uint256;
*
* // Use any of the declared internal constants
* function foo() { Panic.GENERIC.panic(); }
*
* // Alternatively
* function foo() { Panic.panic(Panic.GENERIC); }
* }
* ```
*
* Follows the list from https://github.com/ethereum/solidity/blob/v0.8.24/libsolutil/ErrorCodes.h[libsolutil].
*
* _Available since v5.1._
*/
// slither-disable-next-line unused-state
library Panic {
/// @dev generic / unspecified error
uint256 internal constant GENERIC = 0x00;
/// @dev used by the assert() builtin
uint256 internal constant ASSERT = 0x01;
/// @dev arithmetic underflow or overflow
uint256 internal constant UNDER_OVERFLOW = 0x11;
/// @dev division or modulo by zero
uint256 internal constant DIVISION_BY_ZERO = 0x12;
/// @dev enum conversion error
uint256 internal constant ENUM_CONVERSION_ERROR = 0x21;
/// @dev invalid encoding in storage
uint256 internal constant STORAGE_ENCODING_ERROR = 0x22;
/// @dev empty array pop
uint256 internal constant EMPTY_ARRAY_POP = 0x31;
/// @dev array out of bounds access
uint256 internal constant ARRAY_OUT_OF_BOUNDS = 0x32;
/// @dev resource error (too large allocation or too large array)
uint256 internal constant RESOURCE_ERROR = 0x41;
/// @dev calling invalid internal function
uint256 internal constant INVALID_INTERNAL_FUNCTION = 0x51;
/// @dev Reverts with a panic code. Recommended to use with
/// the internal constants with predefined codes.
function panic(uint256 code) internal pure {
assembly ("memory-safe") {
mstore(0x00, 0x4e487b71)
mstore(0x20, code)
revert(0x1c, 0x24)
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/math/SafeCast.sol)
// This file was procedurally generated from scripts/generate/templates/SafeCast.js.
pragma solidity ^0.8.20;
/**
* @dev Wrappers over Solidity's uintXX/intXX/bool casting operators with added overflow
* checks.
*
* Downcasting from uint256/int256 in Solidity does not revert on overflow. This can
* easily result in undesired exploitation or bugs, since developers usually
* assume that overflows raise errors. `SafeCast` restores this intuition by
* reverting the transaction when such an operation overflows.
*
* Using this library instead of the unchecked operations eliminates an entire
* class of bugs, so it's recommended to use it always.
*/
library SafeCast {
/**
* @dev Value doesn't fit in an uint of `bits` size.
*/
error SafeCastOverflowedUintDowncast(uint8 bits, uint256 value);
/**
* @dev An int value doesn't fit in an uint of `bits` size.
*/
error SafeCastOverflowedIntToUint(int256 value);
/**
* @dev Value doesn't fit in an int of `bits` size.
*/
error SafeCastOverflowedIntDowncast(uint8 bits, int256 value);
/**
* @dev An uint value doesn't fit in an int of `bits` size.
*/
error SafeCastOverflowedUintToInt(uint256 value);
/**
* @dev Returns the downcasted uint248 from uint256, reverting on
* overflow (when the input is greater than largest uint248).
*
* Counterpart to Solidity's `uint248` operator.
*
* Requirements:
*
* - input must fit into 248 bits
*/
function toUint248(uint256 value) internal pure returns (uint248) {
if (value > type(uint248).max) {
revert SafeCastOverflowedUintDowncast(248, value);
}
return uint248(value);
}
/**
* @dev Returns the downcasted uint240 from uint256, reverting on
* overflow (when the input is greater than largest uint240).
*
* Counterpart to Solidity's `uint240` operator.
*
* Requirements:
*
* - input must fit into 240 bits
*/
function toUint240(uint256 value) internal pure returns (uint240) {
if (value > type(uint240).max) {
revert SafeCastOverflowedUintDowncast(240, value);
}
return uint240(value);
}
/**
* @dev Returns the downcasted uint232 from uint256, reverting on
* overflow (when the input is greater than largest uint232).
*
* Counterpart to Solidity's `uint232` operator.
*
* Requirements:
*
* - input must fit into 232 bits
*/
function toUint232(uint256 value) internal pure returns (uint232) {
if (value > type(uint232).max) {
revert SafeCastOverflowedUintDowncast(232, value);
}
return uint232(value);
}
/**
* @dev Returns the downcasted uint224 from uint256, reverting on
* overflow (when the input is greater than largest uint224).
*
* Counterpart to Solidity's `uint224` operator.
*
* Requirements:
*
* - input must fit into 224 bits
*/
function toUint224(uint256 value) internal pure returns (uint224) {
if (value > type(uint224).max) {
revert SafeCastOverflowedUintDowncast(224, value);
}
return uint224(value);
}
/**
* @dev Returns the downcasted uint216 from uint256, reverting on
* overflow (when the input is greater than largest uint216).
*
* Counterpart to Solidity's `uint216` operator.
*
* Requirements:
*
* - input must fit into 216 bits
*/
function toUint216(uint256 value) internal pure returns (uint216) {
if (value > type(uint216).max) {
revert SafeCastOverflowedUintDowncast(216, value);
}
return uint216(value);
}
/**
* @dev Returns the downcasted uint208 from uint256, reverting on
* overflow (when the input is greater than largest uint208).
*
* Counterpart to Solidity's `uint208` operator.
*
* Requirements:
*
* - input must fit into 208 bits
*/
function toUint208(uint256 value) internal pure returns (uint208) {
if (value > type(uint208).max) {
revert SafeCastOverflowedUintDowncast(208, value);
}
return uint208(value);
}
/**
* @dev Returns the downcasted uint200 from uint256, reverting on
* overflow (when the input is greater than largest uint200).
*
* Counterpart to Solidity's `uint200` operator.
*
* Requirements:
*
* - input must fit into 200 bits
*/
function toUint200(uint256 value) internal pure returns (uint200) {
if (value > type(uint200).max) {
revert SafeCastOverflowedUintDowncast(200, value);
}
return uint200(value);
}
/**
* @dev Returns the downcasted uint192 from uint256, reverting on
* overflow (when the input is greater than largest uint192).
*
* Counterpart to Solidity's `uint192` operator.
*
* Requirements:
*
* - input must fit into 192 bits
*/
function toUint192(uint256 value) internal pure returns (uint192) {
if (value > type(uint192).max) {
revert SafeCastOverflowedUintDowncast(192, value);
}
return uint192(value);
}
/**
* @dev Returns the downcasted uint184 from uint256, reverting on
* overflow (when the input is greater than largest uint184).
*
* Counterpart to Solidity's `uint184` operator.
*
* Requirements:
*
* - input must fit into 184 bits
*/
function toUint184(uint256 value) internal pure returns (uint184) {
if (value > type(uint184).max) {
revert SafeCastOverflowedUintDowncast(184, value);
}
return uint184(value);
}
/**
* @dev Returns the downcasted uint176 from uint256, reverting on
* overflow (when the input is greater than largest uint176).
*
* Counterpart to Solidity's `uint176` operator.
*
* Requirements:
*
* - input must fit into 176 bits
*/
function toUint176(uint256 value) internal pure returns (uint176) {
if (value > type(uint176).max) {
revert SafeCastOverflowedUintDowncast(176, value);
}
return uint176(value);
}
/**
* @dev Returns the downcasted uint168 from uint256, reverting on
* overflow (when the input is greater than largest uint168).
*
* Counterpart to Solidity's `uint168` operator.
*
* Requirements:
*
* - input must fit into 168 bits
*/
function toUint168(uint256 value) internal pure returns (uint168) {
if (value > type(uint168).max) {
revert SafeCastOverflowedUintDowncast(168, value);
}
return uint168(value);
}
/**
* @dev Returns the downcasted uint160 from uint256, reverting on
* overflow (when the input is greater than largest uint160).
*
* Counterpart to Solidity's `uint160` operator.
*
* Requirements:
*
* - input must fit into 160 bits
*/
function toUint160(uint256 value) internal pure returns (uint160) {
if (value > type(uint160).max) {
revert SafeCastOverflowedUintDowncast(160, value);
}
return uint160(value);
}
/**
* @dev Returns the downcasted uint152 from uint256, reverting on
* overflow (when the input is greater than largest uint152).
*
* Counterpart to Solidity's `uint152` operator.
*
* Requirements:
*
* - input must fit into 152 bits
*/
function toUint152(uint256 value) internal pure returns (uint152) {
if (value > type(uint152).max) {
revert SafeCastOverflowedUintDowncast(152, value);
}
return uint152(value);
}
/**
* @dev Returns the downcasted uint144 from uint256, reverting on
* overflow (when the input is greater than largest uint144).
*
* Counterpart to Solidity's `uint144` operator.
*
* Requirements:
*
* - input must fit into 144 bits
*/
function toUint144(uint256 value) internal pure returns (uint144) {
if (value > type(uint144).max) {
revert SafeCastOverflowedUintDowncast(144, value);
}
return uint144(value);
}
/**
* @dev Returns the downcasted uint136 from uint256, reverting on
* overflow (when the input is greater than largest uint136).
*
* Counterpart to Solidity's `uint136` operator.
*
* Requirements:
*
* - input must fit into 136 bits
*/
function toUint136(uint256 value) internal pure returns (uint136) {
if (value > type(uint136).max) {
revert SafeCastOverflowedUintDowncast(136, value);
}
return uint136(value);
}
/**
* @dev Returns the downcasted uint128 from uint256, reverting on
* overflow (when the input is greater than largest uint128).
*
* Counterpart to Solidity's `uint128` operator.
*
* Requirements:
*
* - input must fit into 128 bits
*/
function toUint128(uint256 value) internal pure returns (uint128) {
if (value > type(uint128).max) {
revert SafeCastOverflowedUintDowncast(128, value);
}
return uint128(value);
}
/**
* @dev Returns the downcasted uint120 from uint256, reverting on
* overflow (when the input is greater than largest uint120).
*
* Counterpart to Solidity's `uint120` operator.
*
* Requirements:
*
* - input must fit into 120 bits
*/
function toUint120(uint256 value) internal pure returns (uint120) {
if (value > type(uint120).max) {
revert SafeCastOverflowedUintDowncast(120, value);
}
return uint120(value);
}
/**
* @dev Returns the downcasted uint112 from uint256, reverting on
* overflow (when the input is greater than largest uint112).
*
* Counterpart to Solidity's `uint112` operator.
*
* Requirements:
*
* - input must fit into 112 bits
*/
function toUint112(uint256 value) internal pure returns (uint112) {
if (value > type(uint112).max) {
revert SafeCastOverflowedUintDowncast(112, value);
}
return uint112(value);
}
/**
* @dev Returns the downcasted uint104 from uint256, reverting on
* overflow (when the input is greater than largest uint104).
*
* Counterpart to Solidity's `uint104` operator.
*
* Requirements:
*
* - input must fit into 104 bits
*/
function toUint104(uint256 value) internal pure returns (uint104) {
if (value > type(uint104).max) {
revert SafeCastOverflowedUintDowncast(104, value);
}
return uint104(value);
}
/**
* @dev Returns the downcasted uint96 from uint256, reverting on
* overflow (when the input is greater than largest uint96).
*
* Counterpart to Solidity's `uint96` operator.
*
* Requirements:
*
* - input must fit into 96 bits
*/
function toUint96(uint256 value) internal pure returns (uint96) {
if (value > type(uint96).max) {
revert SafeCastOverflowedUintDowncast(96, value);
}
return uint96(value);
}
/**
* @dev Returns the downcasted uint88 from uint256, reverting on
* overflow (when the input is greater than largest uint88).
*
* Counterpart to Solidity's `uint88` operator.
*
* Requirements:
*
* - input must fit into 88 bits
*/
function toUint88(uint256 value) internal pure returns (uint88) {
if (value > type(uint88).max) {
revert SafeCastOverflowedUintDowncast(88, value);
}
return uint88(value);
}
/**
* @dev Returns the downcasted uint80 from uint256, reverting on
* overflow (when the input is greater than largest uint80).
*
* Counterpart to Solidity's `uint80` operator.
*
* Requirements:
*
* - input must fit into 80 bits
*/
function toUint80(uint256 value) internal pure returns (uint80) {
if (value > type(uint80).max) {
revert SafeCastOverflowedUintDowncast(80, value);
}
return uint80(value);
}
/**
* @dev Returns the downcasted uint72 from uint256, reverting on
* overflow (when the input is greater than largest uint72).
*
* Counterpart to Solidity's `uint72` operator.
*
* Requirements:
*
* - input must fit into 72 bits
*/
function toUint72(uint256 value) internal pure returns (uint72) {
if (value > type(uint72).max) {
revert SafeCastOverflowedUintDowncast(72, value);
}
return uint72(value);
}
/**
* @dev Returns the downcasted uint64 from uint256, reverting on
* overflow (when the input is greater than largest uint64).
*
* Counterpart to Solidity's `uint64` operator.
*
* Requirements:
*
* - input must fit into 64 bits
*/
function toUint64(uint256 value) internal pure returns (uint64) {
if (value > type(uint64).max) {
revert SafeCastOverflowedUintDowncast(64, value);
}
return uint64(value);
}
/**
* @dev Returns the downcasted uint56 from uint256, reverting on
* overflow (when the input is greater than largest uint56).
*
* Counterpart to Solidity's `uint56` operator.
*
* Requirements:
*
* - input must fit into 56 bits
*/
function toUint56(uint256 value) internal pure returns (uint56) {
if (value > type(uint56).max) {
revert SafeCastOverflowedUintDowncast(56, value);
}
return uint56(value);
}
/**
* @dev Returns the downcasted uint48 from uint256, reverting on
* overflow (when the input is greater than largest uint48).
*
* Counterpart to Solidity's `uint48` operator.
*
* Requirements:
*
* - input must fit into 48 bits
*/
function toUint48(uint256 value) internal pure returns (uint48) {
if (value > type(uint48).max) {
revert SafeCastOverflowedUintDowncast(48, value);
}
return uint48(value);
}
/**
* @dev Returns the downcasted uint40 from uint256, reverting on
* overflow (when the input is greater than largest uint40).
*
* Counterpart to Solidity's `uint40` operator.
*
* Requirements:
*
* - input must fit into 40 bits
*/
function toUint40(uint256 value) internal pure returns (uint40) {
if (value > type(uint40).max) {
revert SafeCastOverflowedUintDowncast(40, value);
}
return uint40(value);
}
/**
* @dev Returns the downcasted uint32 from uint256, reverting on
* overflow (when the input is greater than largest uint32).
*
* Counterpart to Solidity's `uint32` operator.
*
* Requirements:
*
* - input must fit into 32 bits
*/
function toUint32(uint256 value) internal pure returns (uint32) {
if (value > type(uint32).max) {
revert SafeCastOverflowedUintDowncast(32, value);
}
return uint32(value);
}
/**
* @dev Returns the downcasted uint24 from uint256, reverting on
* overflow (when the input is greater than largest uint24).
*
* Counterpart to Solidity's `uint24` operator.
*
* Requirements:
*
* - input must fit into 24 bits
*/
function toUint24(uint256 value) internal pure returns (uint24) {
if (value > type(uint24).max) {
revert SafeCastOverflowedUintDowncast(24, value);
}
return uint24(value);
}
/**
* @dev Returns the downcasted uint16 from uint256, reverting on
* overflow (when the input is greater than largest uint16).
*
* Counterpart to Solidity's `uint16` operator.
*
* Requirements:
*
* - input must fit into 16 bits
*/
function toUint16(uint256 value) internal pure returns (uint16) {
if (value > type(uint16).max) {
revert SafeCastOverflowedUintDowncast(16, value);
}
return uint16(value);
}
/**
* @dev Returns the downcasted uint8 from uint256, reverting on
* overflow (when the input is greater than largest uint8).
*
* Counterpart to Solidity's `uint8` operator.
*
* Requirements:
*
* - input must fit into 8 bits
*/
function toUint8(uint256 value) internal pure returns (uint8) {
if (value > type(uint8).max) {
revert SafeCastOverflowedUintDowncast(8, value);
}
return uint8(value);
}
/**
* @dev Converts a signed int256 into an unsigned uint256.
*
* Requirements:
*
* - input must be greater than or equal to 0.
*/
function toUint256(int256 value) internal pure returns (uint256) {
if (value < 0) {
revert SafeCastOverflowedIntToUint(value);
}
return uint256(value);
}
/**
* @dev Returns the downcasted int248 from int256, reverting on
* overflow (when the input is less than smallest int248 or
* greater than largest int248).
*
* Counterpart to Solidity's `int248` operator.
*
* Requirements:
*
* - input must fit into 248 bits
*/
function toInt248(int256 value) internal pure returns (int248 downcasted) {
downcasted = int248(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(248, value);
}
}
/**
* @dev Returns the downcasted int240 from int256, reverting on
* overflow (when the input is less than smallest int240 or
* greater than largest int240).
*
* Counterpart to Solidity's `int240` operator.
*
* Requirements:
*
* - input must fit into 240 bits
*/
function toInt240(int256 value) internal pure returns (int240 downcasted) {
downcasted = int240(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(240, value);
}
}
/**
* @dev Returns the downcasted int232 from int256, reverting on
* overflow (when the input is less than smallest int232 or
* greater than largest int232).
*
* Counterpart to Solidity's `int232` operator.
*
* Requirements:
*
* - input must fit into 232 bits
*/
function toInt232(int256 value) internal pure returns (int232 downcasted) {
downcasted = int232(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(232, value);
}
}
/**
* @dev Returns the downcasted int224 from int256, reverting on
* overflow (when the input is less than smallest int224 or
* greater than largest int224).
*
* Counterpart to Solidity's `int224` operator.
*
* Requirements:
*
* - input must fit into 224 bits
*/
function toInt224(int256 value) internal pure returns (int224 downcasted) {
downcasted = int224(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(224, value);
}
}
/**
* @dev Returns the downcasted int216 from int256, reverting on
* overflow (when the input is less than smallest int216 or
* greater than largest int216).
*
* Counterpart to Solidity's `int216` operator.
*
* Requirements:
*
* - input must fit into 216 bits
*/
function toInt216(int256 value) internal pure returns (int216 downcasted) {
downcasted = int216(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(216, value);
}
}
/**
* @dev Returns the downcasted int208 from int256, reverting on
* overflow (when the input is less than smallest int208 or
* greater than largest int208).
*
* Counterpart to Solidity's `int208` operator.
*
* Requirements:
*
* - input must fit into 208 bits
*/
function toInt208(int256 value) internal pure returns (int208 downcasted) {
downcasted = int208(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(208, value);
}
}
/**
* @dev Returns the downcasted int200 from int256, reverting on
* overflow (when the input is less than smallest int200 or
* greater than largest int200).
*
* Counterpart to Solidity's `int200` operator.
*
* Requirements:
*
* - input must fit into 200 bits
*/
function toInt200(int256 value) internal pure returns (int200 downcasted) {
downcasted = int200(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(200, value);
}
}
/**
* @dev Returns the downcasted int192 from int256, reverting on
* overflow (when the input is less than smallest int192 or
* greater than largest int192).
*
* Counterpart to Solidity's `int192` operator.
*
* Requirements:
*
* - input must fit into 192 bits
*/
function toInt192(int256 value) internal pure returns (int192 downcasted) {
downcasted = int192(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(192, value);
}
}
/**
* @dev Returns the downcasted int184 from int256, reverting on
* overflow (when the input is less than smallest int184 or
* greater than largest int184).
*
* Counterpart to Solidity's `int184` operator.
*
* Requirements:
*
* - input must fit into 184 bits
*/
function toInt184(int256 value) internal pure returns (int184 downcasted) {
downcasted = int184(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(184, value);
}
}
/**
* @dev Returns the downcasted int176 from int256, reverting on
* overflow (when the input is less than smallest int176 or
* greater than largest int176).
*
* Counterpart to Solidity's `int176` operator.
*
* Requirements:
*
* - input must fit into 176 bits
*/
function toInt176(int256 value) internal pure returns (int176 downcasted) {
downcasted = int176(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(176, value);
}
}
/**
* @dev Returns the downcasted int168 from int256, reverting on
* overflow (when the input is less than smallest int168 or
* greater than largest int168).
*
* Counterpart to Solidity's `int168` operator.
*
* Requirements:
*
* - input must fit into 168 bits
*/
function toInt168(int256 value) internal pure returns (int168 downcasted) {
downcasted = int168(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(168, value);
}
}
/**
* @dev Returns the downcasted int160 from int256, reverting on
* overflow (when the input is less than smallest int160 or
* greater than largest int160).
*
* Counterpart to Solidity's `int160` operator.
*
* Requirements:
*
* - input must fit into 160 bits
*/
function toInt160(int256 value) internal pure returns (int160 downcasted) {
downcasted = int160(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(160, value);
}
}
/**
* @dev Returns the downcasted int152 from int256, reverting on
* overflow (when the input is less than smallest int152 or
* greater than largest int152).
*
* Counterpart to Solidity's `int152` operator.
*
* Requirements:
*
* - input must fit into 152 bits
*/
function toInt152(int256 value) internal pure returns (int152 downcasted) {
downcasted = int152(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(152, value);
}
}
/**
* @dev Returns the downcasted int144 from int256, reverting on
* overflow (when the input is less than smallest int144 or
* greater than largest int144).
*
* Counterpart to Solidity's `int144` operator.
*
* Requirements:
*
* - input must fit into 144 bits
*/
function toInt144(int256 value) internal pure returns (int144 downcasted) {
downcasted = int144(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(144, value);
}
}
/**
* @dev Returns the downcasted int136 from int256, reverting on
* overflow (when the input is less than smallest int136 or
* greater than largest int136).
*
* Counterpart to Solidity's `int136` operator.
*
* Requirements:
*
* - input must fit into 136 bits
*/
function toInt136(int256 value) internal pure returns (int136 downcasted) {
downcasted = int136(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(136, value);
}
}
/**
* @dev Returns the downcasted int128 from int256, reverting on
* overflow (when the input is less than smallest int128 or
* greater than largest int128).
*
* Counterpart to Solidity's `int128` operator.
*
* Requirements:
*
* - input must fit into 128 bits
*/
function toInt128(int256 value) internal pure returns (int128 downcasted) {
downcasted = int128(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(128, value);
}
}
/**
* @dev Returns the downcasted int120 from int256, reverting on
* overflow (when the input is less than smallest int120 or
* greater than largest int120).
*
* Counterpart to Solidity's `int120` operator.
*
* Requirements:
*
* - input must fit into 120 bits
*/
function toInt120(int256 value) internal pure returns (int120 downcasted) {
downcasted = int120(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(120, value);
}
}
/**
* @dev Returns the downcasted int112 from int256, reverting on
* overflow (when the input is less than smallest int112 or
* greater than largest int112).
*
* Counterpart to Solidity's `int112` operator.
*
* Requirements:
*
* - input must fit into 112 bits
*/
function toInt112(int256 value) internal pure returns (int112 downcasted) {
downcasted = int112(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(112, value);
}
}
/**
* @dev Returns the downcasted int104 from int256, reverting on
* overflow (when the input is less than smallest int104 or
* greater than largest int104).
*
* Counterpart to Solidity's `int104` operator.
*
* Requirements:
*
* - input must fit into 104 bits
*/
function toInt104(int256 value) internal pure returns (int104 downcasted) {
downcasted = int104(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(104, value);
}
}
/**
* @dev Returns the downcasted int96 from int256, reverting on
* overflow (when the input is less than smallest int96 or
* greater than largest int96).
*
* Counterpart to Solidity's `int96` operator.
*
* Requirements:
*
* - input must fit into 96 bits
*/
function toInt96(int256 value) internal pure returns (int96 downcasted) {
downcasted = int96(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(96, value);
}
}
/**
* @dev Returns the downcasted int88 from int256, reverting on
* overflow (when the input is less than smallest int88 or
* greater than largest int88).
*
* Counterpart to Solidity's `int88` operator.
*
* Requirements:
*
* - input must fit into 88 bits
*/
function toInt88(int256 value) internal pure returns (int88 downcasted) {
downcasted = int88(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(88, value);
}
}
/**
* @dev Returns the downcasted int80 from int256, reverting on
* overflow (when the input is less than smallest int80 or
* greater than largest int80).
*
* Counterpart to Solidity's `int80` operator.
*
* Requirements:
*
* - input must fit into 80 bits
*/
function toInt80(int256 value) internal pure returns (int80 downcasted) {
downcasted = int80(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(80, value);
}
}
/**
* @dev Returns the downcasted int72 from int256, reverting on
* overflow (when the input is less than smallest int72 or
* greater than largest int72).
*
* Counterpart to Solidity's `int72` operator.
*
* Requirements:
*
* - input must fit into 72 bits
*/
function toInt72(int256 value) internal pure returns (int72 downcasted) {
downcasted = int72(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(72, value);
}
}
/**
* @dev Returns the downcasted int64 from int256, reverting on
* overflow (when the input is less than smallest int64 or
* greater than largest int64).
*
* Counterpart to Solidity's `int64` operator.
*
* Requirements:
*
* - input must fit into 64 bits
*/
function toInt64(int256 value) internal pure returns (int64 downcasted) {
downcasted = int64(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(64, value);
}
}
/**
* @dev Returns the downcasted int56 from int256, reverting on
* overflow (when the input is less than smallest int56 or
* greater than largest int56).
*
* Counterpart to Solidity's `int56` operator.
*
* Requirements:
*
* - input must fit into 56 bits
*/
function toInt56(int256 value) internal pure returns (int56 downcasted) {
downcasted = int56(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(56, value);
}
}
/**
* @dev Returns the downcasted int48 from int256, reverting on
* overflow (when the input is less than smallest int48 or
* greater than largest int48).
*
* Counterpart to Solidity's `int48` operator.
*
* Requirements:
*
* - input must fit into 48 bits
*/
function toInt48(int256 value) internal pure returns (int48 downcasted) {
downcasted = int48(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(48, value);
}
}
/**
* @dev Returns the downcasted int40 from int256, reverting on
* overflow (when the input is less than smallest int40 or
* greater than largest int40).
*
* Counterpart to Solidity's `int40` operator.
*
* Requirements:
*
* - input must fit into 40 bits
*/
function toInt40(int256 value) internal pure returns (int40 downcasted) {
downcasted = int40(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(40, value);
}
}
/**
* @dev Returns the downcasted int32 from int256, reverting on
* overflow (when the input is less than smallest int32 or
* greater than largest int32).
*
* Counterpart to Solidity's `int32` operator.
*
* Requirements:
*
* - input must fit into 32 bits
*/
function toInt32(int256 value) internal pure returns (int32 downcasted) {
downcasted = int32(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(32, value);
}
}
/**
* @dev Returns the downcasted int24 from int256, reverting on
* overflow (when the input is less than smallest int24 or
* greater than largest int24).
*
* Counterpart to Solidity's `int24` operator.
*
* Requirements:
*
* - input must fit into 24 bits
*/
function toInt24(int256 value) internal pure returns (int24 downcasted) {
downcasted = int24(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(24, value);
}
}
/**
* @dev Returns the downcasted int16 from int256, reverting on
* overflow (when the input is less than smallest int16 or
* greater than largest int16).
*
* Counterpart to Solidity's `int16` operator.
*
* Requirements:
*
* - input must fit into 16 bits
*/
function toInt16(int256 value) internal pure returns (int16 downcasted) {
downcasted = int16(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(16, value);
}
}
/**
* @dev Returns the downcasted int8 from int256, reverting on
* overflow (when the input is less than smallest int8 or
* greater than largest int8).
*
* Counterpart to Solidity's `int8` operator.
*
* Requirements:
*
* - input must fit into 8 bits
*/
function toInt8(int256 value) internal pure returns (int8 downcasted) {
downcasted = int8(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(8, value);
}
}
/**
* @dev Converts an unsigned uint256 into a signed int256.
*
* Requirements:
*
* - input must be less than or equal to maxInt256.
*/
function toInt256(uint256 value) internal pure returns (int256) {
// Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive
if (value > uint256(type(int256).max)) {
revert SafeCastOverflowedUintToInt(value);
}
return int256(value);
}
/**
* @dev Cast a boolean (false or true) to a uint256 (0 or 1) with no jump.
*/
function toUint(bool b) internal pure returns (uint256 u) {
assembly ("memory-safe") {
u := iszero(iszero(b))
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/IERC20.sol)
pragma solidity >=0.4.16;
/**
* @dev Interface of the ERC-20 standard as defined in the ERC.
*/
interface IERC20 {
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
/**
* @dev Returns the value of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the value of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves a `value` amount of tokens from the caller's account to `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address to, uint256 value) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets a `value` amount of tokens as the allowance of `spender` over the
* caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 value) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from `from` to `to` using the
* allowance mechanism. `value` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(address from, address to, uint256 value) external returns (bool);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/// @notice Strategy factory interface for clone deployment
/// @dev Extended to support multi-type factories and Universal Factory pattern
interface IStrategyFactory {
// ============ DEPLOYMENT ============
/**
* @notice Deploy a strategy clone with custom configuration
* @param config ABI-encoded configuration (format depends on factory implementation)
* @param owner Strategy owner address
* @return strategy Deployed strategy address
*/
function createStrategy(bytes calldata config, address owner) external returns (address strategy);
// ============ LEGACY FUNCTIONS (for backward compatibility) ============
/**
* @notice Get master implementation address
* @dev For single-type factories, returns the implementation
* @dev For multi-type factories, may return address(0) or primary implementation
* @return Implementation address
*/
function implementation() external view returns (address);
/**
* @notice Get estimated gas cost for strategy deployment
* @return Estimated gas cost
*/
function getStrategyCreationGas() external view returns (uint256);
// ============ MULTI-TYPE FACTORY SUPPORT (NEW) ============
/**
* @notice Get base protocol this factory serves
* @dev Used by Universal Factory pattern to identify factory type
* @return baseProtocol Base protocol identifier (e.g., bytes32("Shadow"))
*/
function getBaseProtocol() external view returns (bytes32 baseProtocol);
/**
* @notice Get all strategy types this factory can deploy
* @dev Used for enumeration and UI display
* @return strategyTypes Array of strategy type identifiers
*/
function getSupportedStrategyTypes() external view returns (bytes32[] memory strategyTypes);
}{
"remappings": [
"@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/",
"@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/",
"ds-test/=lib/openzeppelin-contracts-upgradeable/lib/forge-std/lib/ds-test/src/",
"erc4626-tests/=lib/openzeppelin-contracts-upgradeable/lib/erc4626-tests/",
"forge-std/=lib/forge-std/src/",
"halmos-cheatcodes/=lib/openzeppelin-contracts-upgradeable/lib/halmos-cheatcodes/src/",
"openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/",
"openzeppelin-contracts/=lib/openzeppelin-contracts/"
],
"optimizer": {
"enabled": true,
"runs": 200
},
"metadata": {
"useLiteralContent": false,
"bytecodeHash": "ipfs",
"appendCBOR": true
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"abi"
]
}
},
"evmVersion": "cancun",
"viaIR": true
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[],"name":"NotAnLPToken","type":"error"},{"inputs":[],"name":"ReentrancyGuardReentrantCall","type":"error"},{"inputs":[],"name":"Unauthorized","type":"error"},{"inputs":[],"name":"UnsupportedLPTokenType","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"priority","type":"uint256"}],"name":"BaseTokenAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"asset","type":"address"},{"indexed":false,"internalType":"address","name":"feed","type":"address"}],"name":"ChainlinkFeedAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"router","type":"address"},{"indexed":false,"internalType":"enum RouterType","name":"routerType","type":"uint8"}],"name":"DEXRouterAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"asset","type":"address"},{"indexed":false,"internalType":"uint256","name":"price","type":"uint256"},{"indexed":false,"internalType":"string","name":"reason","type":"string"}],"name":"ManualPriceSet","type":"event"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"priority","type":"uint256"}],"name":"addBaseToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"asset","type":"address"},{"internalType":"address","name":"feed","type":"address"}],"name":"addChainlinkFeed","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"router","type":"address"},{"internalType":"enum RouterType","name":"routerType","type":"uint8"}],"name":"addDEXRouter","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"tokens","type":"address[]"},{"internalType":"uint256[]","name":"priorities","type":"uint256[]"}],"name":"batchAddBaseTokens","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"assets","type":"address[]"},{"internalType":"address[]","name":"feeds","type":"address[]"}],"name":"batchAddChainlinkFeeds","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"routers","type":"address[]"},{"internalType":"enum RouterType[]","name":"routerTypes","type":"uint8[]"}],"name":"batchAddDEXRouters","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"lpToken","type":"address"}],"name":"calculateLPTokenPrice","outputs":[{"internalType":"uint256","name":"price","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"asset","type":"address"},{"internalType":"uint8","name":"minConfidence","type":"uint8"}],"name":"canPriceAsset","outputs":[{"internalType":"bool","name":"canPrice","type":"bool"},{"internalType":"uint8","name":"actualConfidence","type":"uint8"},{"internalType":"string","name":"reason","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getBaseTokens","outputs":[{"internalType":"address[]","name":"tokens","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"asset","type":"address"}],"name":"getChainlinkFeed","outputs":[{"internalType":"address","name":"feed","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getDEXRouters","outputs":[{"components":[{"internalType":"address","name":"routerAddress","type":"address"},{"internalType":"enum RouterType","name":"routerType","type":"uint8"}],"internalType":"struct OracleRouterInfo[]","name":"routers","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"lpToken","type":"address"}],"name":"getLPTokenData","outputs":[{"components":[{"internalType":"bool","name":"isLP","type":"bool"},{"internalType":"address[]","name":"tokens","type":"address[]"},{"internalType":"uint256[]","name":"reserves","type":"uint256[]"},{"internalType":"uint256","name":"totalSupply","type":"uint256"},{"internalType":"bool","name":"isStable","type":"bool"},{"internalType":"bytes","name":"metadata","type":"bytes"}],"internalType":"struct LPTokenData","name":"data","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"asset","type":"address"}],"name":"getPrice","outputs":[{"internalType":"uint256","name":"price","type":"uint256"},{"internalType":"uint8","name":"confidence","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"asset","type":"address"}],"name":"getPriceData","outputs":[{"components":[{"internalType":"uint256","name":"price","type":"uint256"},{"internalType":"uint8","name":"confidence","type":"uint8"},{"internalType":"uint256","name":"lastUpdate","type":"uint256"},{"internalType":"bytes32","name":"method","type":"bytes32"}],"internalType":"struct PriceData","name":"data","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"asset","type":"address"}],"name":"isLPToken","outputs":[{"internalType":"bool","name":"isLP","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"asset","type":"address"}],"name":"isPriceValid","outputs":[{"internalType":"bool","name":"isValid","type":"bool"},{"internalType":"uint256","name":"lastUpdate","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"asset","type":"address"},{"internalType":"uint256","name":"maxAge","type":"uint256"}],"name":"setMaxPriceAge","outputs":[],"stateMutability":"nonpayable","type":"function"}]Contract Creation Code
6080806040523460195760015f55612669908161001e8239f35b5f80fdfe60806040526004361015610011575f80fd5b5f3560e01c80630aa67e7f14610c3d5780631b23c9c414610b845780632777d2c6146108c95780632a6b69751461079b57806341976e09146107625780635dd64e5e146107045780636497587a146106d757806372279ba11461068157806381cab8c6146105f45780638a50a15f146104b45780638ba4e33d14610435578063981f88f414610364578063bed64c2f146102a1578063ca088bfa146101a9578063d62601691461017e578063ddac7f1b1461010d5763ea0129ca146100d4575f80fd5b346101095760203660031901126101095760206001600160a01b036100ff6100fa610d0f565b610eb6565b5416604051908152f35b5f80fd5b3461010957604036600319011261010957610126610d0f565b61012f33611a21565b15610170576001600160a01b03165f9081527f4a095d6cb9b93a0f83522807df8d2f3070c21f3a0a4ca97d86925974cbbfd3d9602052604090206024359055005b6282b42960e81b5f5260045ffd5b346101095760203660031901126101095760206101a161019c610d0f565b6119ba565b604051908152f35b34610109575f366003190112610109575f805160206125f4833981519152546101d181611251565b906101df6040519283611001565b8082525f805160206125f48339815191525f9081526020830191907f5d1bdf9a615b7d017c7b2ce54ea3d1f29cd5221336315a192b14e65eb53e2e05835b838310610284578486604051918291602083019060208452518091526040830191905f5b81811061024f575050500390f35b9193509160206040600192610276838851868060a01b038151168452015184830190610dc9565b019401910191849392610241565b6001602081926102938561198d565b81520192019201919061021d565b34610109575f366003190112610109576040515f8051602061261483398151915254908181526020810180925f805160206126148339815191525f5260205f20905f5b81811061034557505050816102fa910382611001565b604051918291602083019060208452518091526040830191905f5b818110610323575050500390f35b82516001600160a01b0316845285945060209384019390920191600101610315565b82546001600160a01b03168452602090930192600192830192016102e4565b346101095761037236610d79565b9261037c33611a21565b156101705761038c84841461142a565b5f5b83811061039757005b806103ad6103a86001938887611468565b611478565b6103be6100fa6103a8848988611468565b90838060a01b03166bffffffffffffffffffffffff60a01b8254161790556103ea6103a8828786611468565b7f4fe8b553735635c9c7eaab3168d420311ff503100b03acc262e7118772ca7d95602061041b6103a8858b8a611468565b60405160a087901b8790039182168152931692a20161038e565b34610109576020366003190112610109576040610450610d0f565b61045981611af2565b8281015190805115159283610495575b508261047f575b50825191151582526020820152f35b6032919250602060ff9101511610159083610470565b9092506104ab6104a5834261196c565b91611cbd565b10159184610469565b34610109576104c236610d79565b906104cf93929333611a21565b15610170576104df82851461142a565b5f5b8481106104ea57005b8060ff6105056105006103a86001958a8a611468565b610dd6565b5416156105be575b610518818585611468565b3561056261052a6103a8848a8a611468565b6001600160a01b03165f9081527f4a095d6cb9b93a0f83522807df8d2f3070c21f3a0a4ca97d86925974cbbfd3d36020526040902090565b557f4e1e19819b9bfe79faf800def389d460793f6ae7f24841974445c76cc338f0ad6105926103a8838989611468565b61059d838787611468565b604080516001600160a01b039390931683529035602083015290a1016104e1565b6105d46105cf6103a8838989611468565b610f17565b6105e56105006103a8838989611468565b805460ff19168317905561050d565b346101095760403660031901126101095761060d610d0f565b6024356001600160a01b03811691908290036101095761062c33611a21565b156101705760208161065e7f4fe8b553735635c9c7eaab3168d420311ff503100b03acc262e7118772ca7d9593610eb6565b80546001600160a01b031916851790556040519384526001600160a01b031692a2005b346101095760203660031901126101095760806106ad61069f610d0f565b6106a7611948565b50611af2565b6060604051918051835260ff60208201511660208401526040810151604084015201516060820152f35b346101095760203660031901126101095760206106fa6106f5610d0f565b611856565b6040519015158152f35b346101095760403660031901126101095761071d610d0f565b6024359060ff821682036101095761075e9161073891611529565b60ff60409492945194859415158552166020840152606060408401526060830190610d25565b0390f35b34610109576020366003190112610109576040610785610780610d0f565b611af2565b60ff602082519201511682519182526020820152f35b34610109576107a936610d79565b916107b693919333611a21565b15610170576107c683821461142a565b5f5b8181106107d157005b60ff6107e96107e46103a8848688611468565b610e0e565b5416156107f9575b6001016107c8565b6108076103a8828486611468565b610812828688611468565b359060068210156101095761084561084a926040519261083184610f94565b6001600160a01b0316835260208301611022565b61102e565b61085b6107e46103a8838587611468565b805460ff191660011790556108746103a8828486611468565b90610880818688611468565b35916006831015610109576001927feeb11921cde855ce4746bb2dd2e4173194aee9216eeda0f8c810f06230aae06c916108bf604051928392836110d1565b0390a190506107f1565b34610109576020366003190112610109576108e2610d0f565b606060a06040516108f281610faf565b5f81528260208201528260408201525f838201525f6080820152015260ff61091982610e46565b541615610b1e5761092981610e7e565b6040519061093682610fca565b80546001600160a01b038116835291602081019160a084901c60ff166004811015610b5a5761012091600791855261097860ff604086019760a81c1687611022565b610984600182016110f3565b60608501526002810154608085015261099f60038201611144565b60a08501526109b060048201611144565b60c0850152600581015460e085015260ff6006820154161515610100850152015491015280516004811015610b5a57600114159081610b6e575b50610b1e57516006811015610b5a576001600160a01b0390610a0b9061120e565b5416908115610b1e576040516327d0b1f960e21b81526001600160a01b039091166004820152905f90829060249082905afa908115610b4f575f91610b2d575b50805115610b1e576040516020815260e081019180511515602083015260208101519260c060408401528351809152602061010084019401905f5b818110610aff57505050604081015192601f19838203016060840152602080855192838152019401905f5b818110610ae957848061075e8860a0886060810151608086015260808101511515828601520151601f198483030160c0850152610d25565b8251865260209586019590920191600101610ab1565b82516001600160a01b0316865260209586019590920191600101610a86565b637ea7fba160e11b5f5260045ffd5b610b4991503d805f833e610b418183611001565b810190611268565b81610a4b565b6040513d5f823e3d90fd5b634e487b7160e01b5f52602160045260245ffd5b9050516004811015610b5a5760021415836109ea565b3461010957604036600319011261010957610b9d610d0f565b602435600681101561010957610bb233611a21565b156101705760ff610bc283610e0e565b541615610bcb57005b7feeb11921cde855ce4746bb2dd2e4173194aee9216eeda0f8c810f06230aae06c91610c15604051610bfc81610f94565b6001600160a01b03831681526108458460208301611022565b610c1e81610e0e565b600160ff19825416179055610c38604051928392836110d1565b0390a1005b3461010957604036600319011261010957610c56610d0f565b602435610c6233611a21565b15610170578160ff610c947f4e1e19819b9bfe79faf800def389d460793f6ae7f24841974445c76cc338f0ad94610dd6565b541615610ced575b6001600160a01b03165f8181527f4a095d6cb9b93a0f83522807df8d2f3070c21f3a0a4ca97d86925974cbbfd3d3602090815260409182902084905581519283528201929092529081908101610c38565b610cf681610f17565b610cff81610dd6565b805460ff19166001179055610c9c565b600435906001600160a01b038216820361010957565b805180835260209291819084018484015e5f828201840152601f01601f1916010190565b9181601f84011215610109578235916001600160401b038311610109576020808501948460051b01011161010957565b6040600319820112610109576004356001600160401b0381116101095781610da391600401610d49565b92909291602435906001600160401b03821161010957610dc591600401610d49565b9091565b906006821015610b5a5752565b6001600160a01b03165f9081527f4a095d6cb9b93a0f83522807df8d2f3070c21f3a0a4ca97d86925974cbbfd3d26020526040902090565b6001600160a01b03165f9081527f4a095d6cb9b93a0f83522807df8d2f3070c21f3a0a4ca97d86925974cbbfd3d06020526040902090565b6001600160a01b03165f9081527f4a095d6cb9b93a0f83522807df8d2f3070c21f3a0a4ca97d86925974cbbfd3db6020526040902090565b6001600160a01b03165f9081527f4a095d6cb9b93a0f83522807df8d2f3070c21f3a0a4ca97d86925974cbbfd3da6020526040902090565b6001600160a01b03165f9081527f4a095d6cb9b93a0f83522807df8d2f3070c21f3a0a4ca97d86925974cbbfd3ce6020526040902090565b8054821015610f03575f5260205f2001905f90565b634e487b7160e01b5f52603260045260245ffd5b5f8051602061261483398151915254600160401b811015610f8057806001610f5c92015f80516020612614833981519152555f80516020612614833981519152610eee565b81546001600160a01b0393841660039290921b91821b9390911b1916919091179055565b634e487b7160e01b5f52604160045260245ffd5b604081019081106001600160401b03821117610f8057604052565b60c081019081106001600160401b03821117610f8057604052565b61014081019081106001600160401b03821117610f8057604052565b608081019081106001600160401b03821117610f8057604052565b90601f801991011681019081106001600160401b03821117610f8057604052565b6006821015610b5a5752565b5f805160206125f483398151915254600160401b811015610f805780600161107392015f805160206125f4833981519152555f805160206125f4833981519152610eee565b9190916110be57805182546001600160a01b0319166001600160a01b0391909116178255602001516006811015610b5a57815460ff60a01b191660a09190911b60ff60a01b16179055565b634e487b7160e01b5f525f60045260245ffd5b6001600160a01b0390911681526040810192916110f19160200190610dc9565b565b90604051918281549182825260208201905f5260205f20925f5b8181106111225750506110f192500383611001565b84546001600160a01b031683526001948501948794506020909301920161110d565b90604051915f8154908160011c9260018316928315611204575b6020851084146111f05784875286939081156111ce575060011461118a575b506110f192500383611001565b90505f9291925260205f20905f915b8183106111b25750509060206110f1928201015f61117d565b6020919350806001915483858901015201910190918492611199565b9050602092506110f194915060ff191682840152151560051b8201015f61117d565b634e487b7160e01b5f52602260045260245ffd5b93607f169361115e565b6006811015610b5a575f527f4a095d6cb9b93a0f83522807df8d2f3070c21f3a0a4ca97d86925974cbbfd3b660205260405f2090565b5190811515820361010957565b6001600160401b038111610f805760051b60200190565b602081830312610109578051906001600160401b03821161010957019060c082820312610109576040519161129c83610faf565b6112a581611244565b835260208101516001600160401b03811161010957810182601f820112156101095780516112d281611251565b916112e06040519384611001565b81835260208084019260051b8201019085821161010957602001915b81831061140a57505050602084015260408101516001600160401b03811161010957810182601f820112156101095780519061133782611251565b916113456040519384611001565b80835260208084019160051b8301019185831161010957602001905b8282106113fa5750505060408401526060810151606084015261138660808201611244565b608084015260a0810151906001600160401b038211610109570181601f82011215610109578051906001600160401b038211610f8057604051926113d4601f8401601f191660200185611001565b8284526020838301011161010957815f9260208093018386015e8301015260a082015290565b8151815260209182019101611361565b82516001600160a01b0381168103610109578152602092830192016112fc565b1561143157565b60405162461bcd60e51b815260206004820152600f60248201526e098cadccee8d040dad2e6dac2e8c6d608b1b6044820152606490fd5b9190811015610f035760051b0190565b356001600160a01b03811681036101095790565b519060ff8216820361010957565b9190826040910312610109576114b460208351930161148c565b90565b5f60443d106114b4576040513d600319016004823e8051913d60248401116001600160401b0384111761152357828201928351916001600160401b03831161151b573d8401600319018584016020011161151b57506114b492910160200190611001565b949350505050565b92915050565b9190606060ff61153885610e46565b541615611806575061154983610e7e565b60405161155581610fca565b81546001600160a01b038116825260a081901c60ff166004811015610b5a57602083015261158c9060a81c60ff1660408301611022565b611598600183016110f3565b6060820152600282015460808201526115b360038301611144565b60a08201526115c460048301611144565b60c0820152600582015460e0820152610120600760ff6006850154161593841561010085015201549101526117cd57604080516341976e0960e01b81526001600160a01b03909416600485015283602481305afa92835f915f9561179a575b506116f157505090505f60033d116116e1575b6308c379a014611680575b5f905f90604051611653604082611001565b601e81527f4f7261636c6520726576657274656420776974686f757420726561736f6e0000602082015290565b6116886114b7565b806116935750611641565b6116da603160209260405193849170027b930b1b632903932bb32b93a32b21d1607d1b828401528051918291018484015e81015f838201520301601f198101835282611001565b5f915f9190565b5060045f803e5f5160e01c611636565b156117585760ff1660ff83161061171a5760019190604051611714602082611001565b5f815290565b5f919060405161172b604082611001565b601a81527f436f6e666964656e63652062656c6f77207468726573686f6c64000000000000602082015290565b5090505f905f9060405161176d604082611001565b601a81527f4f7261636c652072657475726e6564207a65726f207072696365000000000000602082015290565b9094506117bf915060403d6040116117c6575b6117b78183611001565b81019061149a565b935f611623565b503d6117ad565b5090505f905f906040516117e2604082611001565b6014815273105cdcd95d081a5cc819195858dd1a5d985d195960621b602082015290565b919250505f915f9161181b6040519182611001565b602581527f4173736574206e6f74207265676973746572656420696e20436861696e526567602082015264697374727960d81b604082015290565b60ff61186182610e46565b541661186c57505f90565b61187590610e7e565b60405161188181610fca565b81546001600160a01b0381168252602082019260a082901c60ff16906004821015610b5a576118c160ff61012094600794885260a81c1660408601611022565b6118cd600182016110f3565b6060850152600281015460808501526118e860038201611144565b60a08501526118f960048201611144565b60c0850152600581015460e085015260ff6006820154161515610100850152015491015280516004811015610b5a57600114908115611936575090565b9050516004811015610b5a5760021490565b6040519061195582610fe6565b5f6060838281528260208201528260408201520152565b9190820391821161197957565b634e487b7160e01b5f52601160045260245ffd5b906110f160405161199d81610f94565b92546001600160a01b038116845260a01c60ff1660208401611022565b6040516313bbe96360e11b81526001600160a01b0390911660048201525f81602481305afa908115610b4f575f91611a07575b50805115611a02576119fe90611d66565b5090565b505f90565b611a1b91503d805f833e610b418183611001565b5f6119ed565b7f4a095d6cb9b93a0f83522807df8d2f3070c21f3a0a4ca97d86925974cbbfd39a546001600160a01b03828116911614611aec57305f9081527f4a095d6cb9b93a0f83522807df8d2f3070c21f3a0a4ca97d86925974cbbfd3936020908152604080832083805282528083206001600160a01b038516845290915290205460ff16611aec576001600160a01b03165f9081527f577ffbf32e3154d879602fc33c6c618fd87b4f6285b5b7d95589de65fd7269d2602052604090205460ff16611ae7575f90565b600190565b50600190565b611afa611948565b506001600160a01b03611b0c82610eb6565b5416611c5e575b611b1c81612102565b81611c25575050611b2c8161228f565b81611bef57505060018060a01b0381165f527f4a095d6cb9b93a0f83522807df8d2f3070c21f3a0a4ca97d86925974cbbfd3d460205260405f2060405191611b7383610fe6565b815480845260ff600184015416602085015260036002840154938460408701520154606085015215159182611bd6575b50506114b45750604051611bb681610fe6565b5f808252602082018190526040820152636e6f6e6560e01b606082015290565b611be69192506104a5904261196c565b10155f80611ba3565b604051925060ff91611c0084610fe6565b83521660208201524260408201526a6465785f726f7574696e6760a81b606082015290565b604051925060ff91611c3684610fe6565b83521660208201524260408201526d36382fb1b0b631bab630ba34b7b760911b606082015290565b611c6781611f4d565b81151580611caf575b611c7b575050611b13565b604051925060ff91611c8c84610fe6565b835216602082015242604082015268636861696e6c696e6b60b81b606082015290565b50605060ff82161015611c70565b6001600160a01b03165f9081527f4a095d6cb9b93a0f83522807df8d2f3070c21f3a0a4ca97d86925974cbbfd3d960205260409020548015611cfc5790565b50610e1090565b8051821015610f035760209160051b010190565b8181029291811591840414171561197957565b60ff16604d811161197957600a0a90565b8115611d45570490565b634e487b7160e01b5f52601260045260245ffd5b9190820180921161197957565b6060810191825115611f2e575f6064935f5b602085019283518051831015611ef1576001600160a01b0390611d9c908490611d03565b5116604051906341976e0960e01b82526004820152604081602481305afa948515610b4f575f915f96611ecf575b508115611ec1575160049291906020906001600160a01b0390611dee908790611d03565b51166040519485809263313ce56760e01b82525afa928315610b4f578885915f95611e6a575b50611e37611e4393611e31611e4997946040611e3d950151611d03565b51611d17565b91611d2a565b90611d3b565b90611d59565b9260ff871660ff821610611e61575b50600101611d78565b95506001611e58565b945050506020833d8211611eb9575b81611e8660209383611001565b810103126101095787611e3d611e37611e4393611e31886040611eab611e499a61148c565b995050949350509350611e14565b3d9150611e79565b505f97508796505050505050565b909550611eea915060403d81116117c6576117b78183611001565b945f611dca565b50925050919250670de0b6b3a7640000810291818304670de0b6b3a76400001482151715611979576114b492611f28915190611d3b565b93612451565b5f9250829150565b519069ffffffffffffffffffff8216820361010957565b6001600160a01b0390611f5f90610eb6565b54169081156120fb57604051633fabe5a360e21b81529160a083600481845afa905f915f92815f965f936120a1575b50611f9f575050505090505f905f90565b5f8413156120955769ffffffffffffffffffff80911691161061208b5762015180611fca854261196c565b1161208b5760206004916040519283809263313ce56760e01b82525afa8015610b4f575f90612051575b60ff91501660120360ff8111611979576120219161201461201a92611d2a565b90611d17565b924261196c565b61012c81116120305750605f90565b610384811161203f5750605a90565b610e101061204c57605090565b603290565b506020813d602011612083575b8161206b60209383611001565b810103126101095761207e60ff9161148c565b611ff4565b3d915061205e565b505090505f905f90565b5050505090505f905f90565b94509550505060a0823d60a0116120f3575b816120c060a09383611001565b81010312610109576120d182611f36565b9360208301516120e8608060608601519501611f36565b95909395915f611f8e565b3d91506120b3565b5f91508190565b60ff61210d82610e46565b5416156122505761211d81610e7e565b6040519061212a82610fca565b80546001600160a01b038116835291602081019160a084901c60ff166004811015610b5a5761012091600791855261216c60ff604086019760a81c1687611022565b612178600182016110f3565b60608501526002810154608085015261219360038201611144565b60a08501526121a460048201611144565b60c0850152600581015460e085015260ff6006820154161515610100850152015491015280516004811015610b5a57600114159081612279575b5061227157516006811015610b5a576001600160a01b03906121ff9061120e565b5416908115612271576040516327d0b1f960e21b81526001600160a01b039091166004820152905f90829060249082905afa908115610b4f575f91612257575b5080511561225057610dc590611d66565b505f905f90565b61226b91503d805f833e610b418183611001565b5f61223f565b50505f905f90565b9050516004811015610b5a57600214155f6121de565b5f805160206125f4833981519152545f80516020612614833981519152545f9392849291908183855b8581106122fd5750505050505082156122f557826122d591611d3b565b91600381106122e45750604b90565b6002116122f057603c90565b602890565b505f91508190565b81811015610f03575f805160206125f48339815191525f526123407f5d1bdf9a615b7d017c7b2ce54ea3d1f29cd5221336315a192b14e65eb53e2e05820161198d565b5f5b8681106123535750506001016122b8565b84811015610f03575f805160206126148339815191525f527f86c95662d08024459c3b6e690a8e7f10e7b09cc72424f7cdc714c64c001ba3188101546001600160a01b039081169087168114612448576123ae838289612530565b806123c0575b50506001905b01612342565b604051916341976e0960e01b83526004830152604082602481305afa918215610b4f575f92612427575b5081156123b45761240a612411939d9b92670de0b6b3a764000092611d17565b0490611d59565b975f1981146119795760018091019a905f6123b4565b61244091925060403d81116117c6576117b78183611001565b50905f6123ea565b506001906123ba565b8069d3c21bcecceda10000008310612496575060ff9150166069810290808204606914901517156119795760649004606481115f146124905750606490565b60ff1690565b6934f086f3b33b6840000083106124ad5750905090565b60ff92690a968163f0a57b40000081106124e157505016605a810290808204605a149015171561197957606460ff91041690565b69021e19e0c9bab24000001161251057501660468102908082046046149015171561197957606460ff91041690565b90501660288102908082046028149015171561197957606460ff91041690565b91909160208201516006811015610b5a576001600160a01b03906125539061120e565b54169182156125eb5751604051633752511b60e11b81526001600160a01b0391821660048201529181166024830152929092166044830152670de0b6b3a76400006064830152602090829060849082905afa5f91816125b7575b506114b457505f90565b9091506020813d6020116125e3575b816125d360209383611001565b810103126101095751905f6125ad565b3d91506125c6565b505050505f9056fe4a095d6cb9b93a0f83522807df8d2f3070c21f3a0a4ca97d86925974cbbfd3cf4a095d6cb9b93a0f83522807df8d2f3070c21f3a0a4ca97d86925974cbbfd3d1a264697066735822122082ac52bf3faefd9358a7be21277cf7fe741467c67df0a59d84bfad5defbc0c9064736f6c634300081a0033
Deployed Bytecode
0x60806040526004361015610011575f80fd5b5f3560e01c80630aa67e7f14610c3d5780631b23c9c414610b845780632777d2c6146108c95780632a6b69751461079b57806341976e09146107625780635dd64e5e146107045780636497587a146106d757806372279ba11461068157806381cab8c6146105f45780638a50a15f146104b45780638ba4e33d14610435578063981f88f414610364578063bed64c2f146102a1578063ca088bfa146101a9578063d62601691461017e578063ddac7f1b1461010d5763ea0129ca146100d4575f80fd5b346101095760203660031901126101095760206001600160a01b036100ff6100fa610d0f565b610eb6565b5416604051908152f35b5f80fd5b3461010957604036600319011261010957610126610d0f565b61012f33611a21565b15610170576001600160a01b03165f9081527f4a095d6cb9b93a0f83522807df8d2f3070c21f3a0a4ca97d86925974cbbfd3d9602052604090206024359055005b6282b42960e81b5f5260045ffd5b346101095760203660031901126101095760206101a161019c610d0f565b6119ba565b604051908152f35b34610109575f366003190112610109575f805160206125f4833981519152546101d181611251565b906101df6040519283611001565b8082525f805160206125f48339815191525f9081526020830191907f5d1bdf9a615b7d017c7b2ce54ea3d1f29cd5221336315a192b14e65eb53e2e05835b838310610284578486604051918291602083019060208452518091526040830191905f5b81811061024f575050500390f35b9193509160206040600192610276838851868060a01b038151168452015184830190610dc9565b019401910191849392610241565b6001602081926102938561198d565b81520192019201919061021d565b34610109575f366003190112610109576040515f8051602061261483398151915254908181526020810180925f805160206126148339815191525f5260205f20905f5b81811061034557505050816102fa910382611001565b604051918291602083019060208452518091526040830191905f5b818110610323575050500390f35b82516001600160a01b0316845285945060209384019390920191600101610315565b82546001600160a01b03168452602090930192600192830192016102e4565b346101095761037236610d79565b9261037c33611a21565b156101705761038c84841461142a565b5f5b83811061039757005b806103ad6103a86001938887611468565b611478565b6103be6100fa6103a8848988611468565b90838060a01b03166bffffffffffffffffffffffff60a01b8254161790556103ea6103a8828786611468565b7f4fe8b553735635c9c7eaab3168d420311ff503100b03acc262e7118772ca7d95602061041b6103a8858b8a611468565b60405160a087901b8790039182168152931692a20161038e565b34610109576020366003190112610109576040610450610d0f565b61045981611af2565b8281015190805115159283610495575b508261047f575b50825191151582526020820152f35b6032919250602060ff9101511610159083610470565b9092506104ab6104a5834261196c565b91611cbd565b10159184610469565b34610109576104c236610d79565b906104cf93929333611a21565b15610170576104df82851461142a565b5f5b8481106104ea57005b8060ff6105056105006103a86001958a8a611468565b610dd6565b5416156105be575b610518818585611468565b3561056261052a6103a8848a8a611468565b6001600160a01b03165f9081527f4a095d6cb9b93a0f83522807df8d2f3070c21f3a0a4ca97d86925974cbbfd3d36020526040902090565b557f4e1e19819b9bfe79faf800def389d460793f6ae7f24841974445c76cc338f0ad6105926103a8838989611468565b61059d838787611468565b604080516001600160a01b039390931683529035602083015290a1016104e1565b6105d46105cf6103a8838989611468565b610f17565b6105e56105006103a8838989611468565b805460ff19168317905561050d565b346101095760403660031901126101095761060d610d0f565b6024356001600160a01b03811691908290036101095761062c33611a21565b156101705760208161065e7f4fe8b553735635c9c7eaab3168d420311ff503100b03acc262e7118772ca7d9593610eb6565b80546001600160a01b031916851790556040519384526001600160a01b031692a2005b346101095760203660031901126101095760806106ad61069f610d0f565b6106a7611948565b50611af2565b6060604051918051835260ff60208201511660208401526040810151604084015201516060820152f35b346101095760203660031901126101095760206106fa6106f5610d0f565b611856565b6040519015158152f35b346101095760403660031901126101095761071d610d0f565b6024359060ff821682036101095761075e9161073891611529565b60ff60409492945194859415158552166020840152606060408401526060830190610d25565b0390f35b34610109576020366003190112610109576040610785610780610d0f565b611af2565b60ff602082519201511682519182526020820152f35b34610109576107a936610d79565b916107b693919333611a21565b15610170576107c683821461142a565b5f5b8181106107d157005b60ff6107e96107e46103a8848688611468565b610e0e565b5416156107f9575b6001016107c8565b6108076103a8828486611468565b610812828688611468565b359060068210156101095761084561084a926040519261083184610f94565b6001600160a01b0316835260208301611022565b61102e565b61085b6107e46103a8838587611468565b805460ff191660011790556108746103a8828486611468565b90610880818688611468565b35916006831015610109576001927feeb11921cde855ce4746bb2dd2e4173194aee9216eeda0f8c810f06230aae06c916108bf604051928392836110d1565b0390a190506107f1565b34610109576020366003190112610109576108e2610d0f565b606060a06040516108f281610faf565b5f81528260208201528260408201525f838201525f6080820152015260ff61091982610e46565b541615610b1e5761092981610e7e565b6040519061093682610fca565b80546001600160a01b038116835291602081019160a084901c60ff166004811015610b5a5761012091600791855261097860ff604086019760a81c1687611022565b610984600182016110f3565b60608501526002810154608085015261099f60038201611144565b60a08501526109b060048201611144565b60c0850152600581015460e085015260ff6006820154161515610100850152015491015280516004811015610b5a57600114159081610b6e575b50610b1e57516006811015610b5a576001600160a01b0390610a0b9061120e565b5416908115610b1e576040516327d0b1f960e21b81526001600160a01b039091166004820152905f90829060249082905afa908115610b4f575f91610b2d575b50805115610b1e576040516020815260e081019180511515602083015260208101519260c060408401528351809152602061010084019401905f5b818110610aff57505050604081015192601f19838203016060840152602080855192838152019401905f5b818110610ae957848061075e8860a0886060810151608086015260808101511515828601520151601f198483030160c0850152610d25565b8251865260209586019590920191600101610ab1565b82516001600160a01b0316865260209586019590920191600101610a86565b637ea7fba160e11b5f5260045ffd5b610b4991503d805f833e610b418183611001565b810190611268565b81610a4b565b6040513d5f823e3d90fd5b634e487b7160e01b5f52602160045260245ffd5b9050516004811015610b5a5760021415836109ea565b3461010957604036600319011261010957610b9d610d0f565b602435600681101561010957610bb233611a21565b156101705760ff610bc283610e0e565b541615610bcb57005b7feeb11921cde855ce4746bb2dd2e4173194aee9216eeda0f8c810f06230aae06c91610c15604051610bfc81610f94565b6001600160a01b03831681526108458460208301611022565b610c1e81610e0e565b600160ff19825416179055610c38604051928392836110d1565b0390a1005b3461010957604036600319011261010957610c56610d0f565b602435610c6233611a21565b15610170578160ff610c947f4e1e19819b9bfe79faf800def389d460793f6ae7f24841974445c76cc338f0ad94610dd6565b541615610ced575b6001600160a01b03165f8181527f4a095d6cb9b93a0f83522807df8d2f3070c21f3a0a4ca97d86925974cbbfd3d3602090815260409182902084905581519283528201929092529081908101610c38565b610cf681610f17565b610cff81610dd6565b805460ff19166001179055610c9c565b600435906001600160a01b038216820361010957565b805180835260209291819084018484015e5f828201840152601f01601f1916010190565b9181601f84011215610109578235916001600160401b038311610109576020808501948460051b01011161010957565b6040600319820112610109576004356001600160401b0381116101095781610da391600401610d49565b92909291602435906001600160401b03821161010957610dc591600401610d49565b9091565b906006821015610b5a5752565b6001600160a01b03165f9081527f4a095d6cb9b93a0f83522807df8d2f3070c21f3a0a4ca97d86925974cbbfd3d26020526040902090565b6001600160a01b03165f9081527f4a095d6cb9b93a0f83522807df8d2f3070c21f3a0a4ca97d86925974cbbfd3d06020526040902090565b6001600160a01b03165f9081527f4a095d6cb9b93a0f83522807df8d2f3070c21f3a0a4ca97d86925974cbbfd3db6020526040902090565b6001600160a01b03165f9081527f4a095d6cb9b93a0f83522807df8d2f3070c21f3a0a4ca97d86925974cbbfd3da6020526040902090565b6001600160a01b03165f9081527f4a095d6cb9b93a0f83522807df8d2f3070c21f3a0a4ca97d86925974cbbfd3ce6020526040902090565b8054821015610f03575f5260205f2001905f90565b634e487b7160e01b5f52603260045260245ffd5b5f8051602061261483398151915254600160401b811015610f8057806001610f5c92015f80516020612614833981519152555f80516020612614833981519152610eee565b81546001600160a01b0393841660039290921b91821b9390911b1916919091179055565b634e487b7160e01b5f52604160045260245ffd5b604081019081106001600160401b03821117610f8057604052565b60c081019081106001600160401b03821117610f8057604052565b61014081019081106001600160401b03821117610f8057604052565b608081019081106001600160401b03821117610f8057604052565b90601f801991011681019081106001600160401b03821117610f8057604052565b6006821015610b5a5752565b5f805160206125f483398151915254600160401b811015610f805780600161107392015f805160206125f4833981519152555f805160206125f4833981519152610eee565b9190916110be57805182546001600160a01b0319166001600160a01b0391909116178255602001516006811015610b5a57815460ff60a01b191660a09190911b60ff60a01b16179055565b634e487b7160e01b5f525f60045260245ffd5b6001600160a01b0390911681526040810192916110f19160200190610dc9565b565b90604051918281549182825260208201905f5260205f20925f5b8181106111225750506110f192500383611001565b84546001600160a01b031683526001948501948794506020909301920161110d565b90604051915f8154908160011c9260018316928315611204575b6020851084146111f05784875286939081156111ce575060011461118a575b506110f192500383611001565b90505f9291925260205f20905f915b8183106111b25750509060206110f1928201015f61117d565b6020919350806001915483858901015201910190918492611199565b9050602092506110f194915060ff191682840152151560051b8201015f61117d565b634e487b7160e01b5f52602260045260245ffd5b93607f169361115e565b6006811015610b5a575f527f4a095d6cb9b93a0f83522807df8d2f3070c21f3a0a4ca97d86925974cbbfd3b660205260405f2090565b5190811515820361010957565b6001600160401b038111610f805760051b60200190565b602081830312610109578051906001600160401b03821161010957019060c082820312610109576040519161129c83610faf565b6112a581611244565b835260208101516001600160401b03811161010957810182601f820112156101095780516112d281611251565b916112e06040519384611001565b81835260208084019260051b8201019085821161010957602001915b81831061140a57505050602084015260408101516001600160401b03811161010957810182601f820112156101095780519061133782611251565b916113456040519384611001565b80835260208084019160051b8301019185831161010957602001905b8282106113fa5750505060408401526060810151606084015261138660808201611244565b608084015260a0810151906001600160401b038211610109570181601f82011215610109578051906001600160401b038211610f8057604051926113d4601f8401601f191660200185611001565b8284526020838301011161010957815f9260208093018386015e8301015260a082015290565b8151815260209182019101611361565b82516001600160a01b0381168103610109578152602092830192016112fc565b1561143157565b60405162461bcd60e51b815260206004820152600f60248201526e098cadccee8d040dad2e6dac2e8c6d608b1b6044820152606490fd5b9190811015610f035760051b0190565b356001600160a01b03811681036101095790565b519060ff8216820361010957565b9190826040910312610109576114b460208351930161148c565b90565b5f60443d106114b4576040513d600319016004823e8051913d60248401116001600160401b0384111761152357828201928351916001600160401b03831161151b573d8401600319018584016020011161151b57506114b492910160200190611001565b949350505050565b92915050565b9190606060ff61153885610e46565b541615611806575061154983610e7e565b60405161155581610fca565b81546001600160a01b038116825260a081901c60ff166004811015610b5a57602083015261158c9060a81c60ff1660408301611022565b611598600183016110f3565b6060820152600282015460808201526115b360038301611144565b60a08201526115c460048301611144565b60c0820152600582015460e0820152610120600760ff6006850154161593841561010085015201549101526117cd57604080516341976e0960e01b81526001600160a01b03909416600485015283602481305afa92835f915f9561179a575b506116f157505090505f60033d116116e1575b6308c379a014611680575b5f905f90604051611653604082611001565b601e81527f4f7261636c6520726576657274656420776974686f757420726561736f6e0000602082015290565b6116886114b7565b806116935750611641565b6116da603160209260405193849170027b930b1b632903932bb32b93a32b21d1607d1b828401528051918291018484015e81015f838201520301601f198101835282611001565b5f915f9190565b5060045f803e5f5160e01c611636565b156117585760ff1660ff83161061171a5760019190604051611714602082611001565b5f815290565b5f919060405161172b604082611001565b601a81527f436f6e666964656e63652062656c6f77207468726573686f6c64000000000000602082015290565b5090505f905f9060405161176d604082611001565b601a81527f4f7261636c652072657475726e6564207a65726f207072696365000000000000602082015290565b9094506117bf915060403d6040116117c6575b6117b78183611001565b81019061149a565b935f611623565b503d6117ad565b5090505f905f906040516117e2604082611001565b6014815273105cdcd95d081a5cc819195858dd1a5d985d195960621b602082015290565b919250505f915f9161181b6040519182611001565b602581527f4173736574206e6f74207265676973746572656420696e20436861696e526567602082015264697374727960d81b604082015290565b60ff61186182610e46565b541661186c57505f90565b61187590610e7e565b60405161188181610fca565b81546001600160a01b0381168252602082019260a082901c60ff16906004821015610b5a576118c160ff61012094600794885260a81c1660408601611022565b6118cd600182016110f3565b6060850152600281015460808501526118e860038201611144565b60a08501526118f960048201611144565b60c0850152600581015460e085015260ff6006820154161515610100850152015491015280516004811015610b5a57600114908115611936575090565b9050516004811015610b5a5760021490565b6040519061195582610fe6565b5f6060838281528260208201528260408201520152565b9190820391821161197957565b634e487b7160e01b5f52601160045260245ffd5b906110f160405161199d81610f94565b92546001600160a01b038116845260a01c60ff1660208401611022565b6040516313bbe96360e11b81526001600160a01b0390911660048201525f81602481305afa908115610b4f575f91611a07575b50805115611a02576119fe90611d66565b5090565b505f90565b611a1b91503d805f833e610b418183611001565b5f6119ed565b7f4a095d6cb9b93a0f83522807df8d2f3070c21f3a0a4ca97d86925974cbbfd39a546001600160a01b03828116911614611aec57305f9081527f4a095d6cb9b93a0f83522807df8d2f3070c21f3a0a4ca97d86925974cbbfd3936020908152604080832083805282528083206001600160a01b038516845290915290205460ff16611aec576001600160a01b03165f9081527f577ffbf32e3154d879602fc33c6c618fd87b4f6285b5b7d95589de65fd7269d2602052604090205460ff16611ae7575f90565b600190565b50600190565b611afa611948565b506001600160a01b03611b0c82610eb6565b5416611c5e575b611b1c81612102565b81611c25575050611b2c8161228f565b81611bef57505060018060a01b0381165f527f4a095d6cb9b93a0f83522807df8d2f3070c21f3a0a4ca97d86925974cbbfd3d460205260405f2060405191611b7383610fe6565b815480845260ff600184015416602085015260036002840154938460408701520154606085015215159182611bd6575b50506114b45750604051611bb681610fe6565b5f808252602082018190526040820152636e6f6e6560e01b606082015290565b611be69192506104a5904261196c565b10155f80611ba3565b604051925060ff91611c0084610fe6565b83521660208201524260408201526a6465785f726f7574696e6760a81b606082015290565b604051925060ff91611c3684610fe6565b83521660208201524260408201526d36382fb1b0b631bab630ba34b7b760911b606082015290565b611c6781611f4d565b81151580611caf575b611c7b575050611b13565b604051925060ff91611c8c84610fe6565b835216602082015242604082015268636861696e6c696e6b60b81b606082015290565b50605060ff82161015611c70565b6001600160a01b03165f9081527f4a095d6cb9b93a0f83522807df8d2f3070c21f3a0a4ca97d86925974cbbfd3d960205260409020548015611cfc5790565b50610e1090565b8051821015610f035760209160051b010190565b8181029291811591840414171561197957565b60ff16604d811161197957600a0a90565b8115611d45570490565b634e487b7160e01b5f52601260045260245ffd5b9190820180921161197957565b6060810191825115611f2e575f6064935f5b602085019283518051831015611ef1576001600160a01b0390611d9c908490611d03565b5116604051906341976e0960e01b82526004820152604081602481305afa948515610b4f575f915f96611ecf575b508115611ec1575160049291906020906001600160a01b0390611dee908790611d03565b51166040519485809263313ce56760e01b82525afa928315610b4f578885915f95611e6a575b50611e37611e4393611e31611e4997946040611e3d950151611d03565b51611d17565b91611d2a565b90611d3b565b90611d59565b9260ff871660ff821610611e61575b50600101611d78565b95506001611e58565b945050506020833d8211611eb9575b81611e8660209383611001565b810103126101095787611e3d611e37611e4393611e31886040611eab611e499a61148c565b995050949350509350611e14565b3d9150611e79565b505f97508796505050505050565b909550611eea915060403d81116117c6576117b78183611001565b945f611dca565b50925050919250670de0b6b3a7640000810291818304670de0b6b3a76400001482151715611979576114b492611f28915190611d3b565b93612451565b5f9250829150565b519069ffffffffffffffffffff8216820361010957565b6001600160a01b0390611f5f90610eb6565b54169081156120fb57604051633fabe5a360e21b81529160a083600481845afa905f915f92815f965f936120a1575b50611f9f575050505090505f905f90565b5f8413156120955769ffffffffffffffffffff80911691161061208b5762015180611fca854261196c565b1161208b5760206004916040519283809263313ce56760e01b82525afa8015610b4f575f90612051575b60ff91501660120360ff8111611979576120219161201461201a92611d2a565b90611d17565b924261196c565b61012c81116120305750605f90565b610384811161203f5750605a90565b610e101061204c57605090565b603290565b506020813d602011612083575b8161206b60209383611001565b810103126101095761207e60ff9161148c565b611ff4565b3d915061205e565b505090505f905f90565b5050505090505f905f90565b94509550505060a0823d60a0116120f3575b816120c060a09383611001565b81010312610109576120d182611f36565b9360208301516120e8608060608601519501611f36565b95909395915f611f8e565b3d91506120b3565b5f91508190565b60ff61210d82610e46565b5416156122505761211d81610e7e565b6040519061212a82610fca565b80546001600160a01b038116835291602081019160a084901c60ff166004811015610b5a5761012091600791855261216c60ff604086019760a81c1687611022565b612178600182016110f3565b60608501526002810154608085015261219360038201611144565b60a08501526121a460048201611144565b60c0850152600581015460e085015260ff6006820154161515610100850152015491015280516004811015610b5a57600114159081612279575b5061227157516006811015610b5a576001600160a01b03906121ff9061120e565b5416908115612271576040516327d0b1f960e21b81526001600160a01b039091166004820152905f90829060249082905afa908115610b4f575f91612257575b5080511561225057610dc590611d66565b505f905f90565b61226b91503d805f833e610b418183611001565b5f61223f565b50505f905f90565b9050516004811015610b5a57600214155f6121de565b5f805160206125f4833981519152545f80516020612614833981519152545f9392849291908183855b8581106122fd5750505050505082156122f557826122d591611d3b565b91600381106122e45750604b90565b6002116122f057603c90565b602890565b505f91508190565b81811015610f03575f805160206125f48339815191525f526123407f5d1bdf9a615b7d017c7b2ce54ea3d1f29cd5221336315a192b14e65eb53e2e05820161198d565b5f5b8681106123535750506001016122b8565b84811015610f03575f805160206126148339815191525f527f86c95662d08024459c3b6e690a8e7f10e7b09cc72424f7cdc714c64c001ba3188101546001600160a01b039081169087168114612448576123ae838289612530565b806123c0575b50506001905b01612342565b604051916341976e0960e01b83526004830152604082602481305afa918215610b4f575f92612427575b5081156123b45761240a612411939d9b92670de0b6b3a764000092611d17565b0490611d59565b975f1981146119795760018091019a905f6123b4565b61244091925060403d81116117c6576117b78183611001565b50905f6123ea565b506001906123ba565b8069d3c21bcecceda10000008310612496575060ff9150166069810290808204606914901517156119795760649004606481115f146124905750606490565b60ff1690565b6934f086f3b33b6840000083106124ad5750905090565b60ff92690a968163f0a57b40000081106124e157505016605a810290808204605a149015171561197957606460ff91041690565b69021e19e0c9bab24000001161251057501660468102908082046046149015171561197957606460ff91041690565b90501660288102908082046028149015171561197957606460ff91041690565b91909160208201516006811015610b5a576001600160a01b03906125539061120e565b54169182156125eb5751604051633752511b60e11b81526001600160a01b0391821660048201529181166024830152929092166044830152670de0b6b3a76400006064830152602090829060849082905afa5f91816125b7575b506114b457505f90565b9091506020813d6020116125e3575b816125d360209383611001565b810103126101095751905f6125ad565b3d91506125c6565b505050505f9056fe4a095d6cb9b93a0f83522807df8d2f3070c21f3a0a4ca97d86925974cbbfd3cf4a095d6cb9b93a0f83522807df8d2f3070c21f3a0a4ca97d86925974cbbfd3d1a264697066735822122082ac52bf3faefd9358a7be21277cf7fe741467c67df0a59d84bfad5defbc0c9064736f6c634300081a0033
Deployed Bytecode Sourcemap
1758:24587:12:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;1758:24587:12;;;;;-1:-1:-1;;;;;26313:23:12;1758:24587;;:::i;:::-;26313:23;:::i;:::-;1758:24587;;;;;;;;;;;;;;;;;;-1:-1:-1;;1758:24587:12;;;;;;:::i;:::-;2159:37;2185:10;2159:37;:::i;:::-;2158:38;2154:90;;-1:-1:-1;;;;;1758:24587:12;;;;;25272:13;1758:24587;;;;;;;;;;2154:90;2219:14;;;1758:24587;2219:14;1758:24587;;2219:14;1758:24587;;;;;;-1:-1:-1;;1758:24587:12;;;;;;;;:::i;:::-;;:::i;:::-;;;;;;;;;;;;;-1:-1:-1;;1758:24587:12;;;;-1:-1:-1;;;;;;;;;;;1758:24587:12;;;;:::i;:::-;;;;;;;;:::i;:::-;;;;-1:-1:-1;;;;;;;;;;;1758:24587:12;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;-1:-1:-1;;1758:24587:12;;;;;;-1:-1:-1;;;;;;;;;;;1758:24587:12;;;;;;;;;;-1:-1:-1;;;;;;;;;;;1758:24587:12;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;1758:24587:12;;;;;-1:-1:-1;1758:24587:12;;;;;;;;;;;;;;;;-1:-1:-1;;;;;1758:24587:12;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;2185:10;2159:37;2185:10;2159:37;:::i;:::-;2158:38;2154:90;;21496:57;21504:29;;;21496:57;:::i;:::-;1758:24587;21677:17;;;;;;1758:24587;21696:3;21745:8;;;1758:24587;21745:8;;;;:::i;:::-;;:::i;:::-;21715:27;21732:9;;;;;;:::i;21715:27::-;1758:24587;;;;;;;;;;;;;;;;21791:9;;;;;;:::i;:::-;21772:39;1758:24587;21802:8;;;;;;:::i;:::-;1758:24587;;;;;;;;;;;;;;;;;21772:39;1758:24587;21662:13;;1758:24587;;;;;;-1:-1:-1;;1758:24587:12;;;;;;;:::i;:::-;4207:20;;;:::i;:::-;4250:15;;;1758:24587;;;;4285:14;;:72;;;;1758:24587;4285:97;;;;1758:24587;;;;;;;;;;;;;;4285:97;4380:2;4361:15;;;1758:24587;;4361:15;;1758:24587;;4361:21;;4285:97;;;;:72;4303:15;;;4335:22;4303:28;:15;;:28;:::i;:::-;4335:22;;:::i;:::-;-1:-1:-1;4303:54:12;4285:72;;;;1758:24587;;;;;;;:::i;:::-;2185:10;2159:37;2185:10;;;;2159:37;:::i;:::-;2158:38;2154:90;;24332:62;24340:34;;;24332:62;:::i;:::-;1758:24587;24518:17;;;;;;1758:24587;24537:3;24575:9;1758:24587;24561:24;24575:9;;1758:24587;24575:9;;;;:::i;:::-;24561:24;:::i;:::-;1758:24587;;24560:25;24556:141;;24537:3;24743:13;;;;;:::i;:::-;1758:24587;24710:30;24730:9;;;;;;:::i;:::-;-1:-1:-1;;;;;1758:24587:12;;;;;23905:19;1758:24587;;;;;;;24710:30;1758:24587;24775:40;24790:9;;;;;;:::i;:::-;24801:13;;;;;:::i;:::-;1758:24587;;;-1:-1:-1;;;;;1758:24587:12;;;;;;;;;;;;;24775:40;1758:24587;24503:13;;24556:141;24605:28;24623:9;;;;;;:::i;:::-;24605:28;:::i;:::-;24651:24;24665:9;;;;;;:::i;24651:24::-;1758:24587;;-1:-1:-1;;1758:24587:12;;;;;24556:141;;1758:24587;;;;;;-1:-1:-1;;1758:24587:12;;;;;;:::i;:::-;;;-1:-1:-1;;;;;1758:24587:12;;;;;;;;;2159:37;2185:10;2159:37;:::i;:::-;2158:38;2154:90;;1758:24587;21084:23;;21129:31;21084:23;;:::i;:::-;1758:24587;;-1:-1:-1;;;;;;1758:24587:12;;;;;;;;;;-1:-1:-1;;;;;1758:24587:12;;21129:31;1758:24587;;;;;;;-1:-1:-1;;1758:24587:12;;;;;3853:20;1758:24587;;:::i;:::-;;;:::i;:::-;;3853:20;:::i;:::-;1758:24587;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;1758:24587:12;;;;;;;;:::i;:::-;;:::i;:::-;;;;;;;;;;;;;;;-1:-1:-1;;1758:24587:12;;;;;;:::i;:::-;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;-1:-1:-1;;1758:24587:12;;;;;3524:20;1758:24587;;:::i;:::-;3524:20;:::i;:::-;1758:24587;;;;3574:15;;1758:24587;;;;;;;;;;;;;;;;;;;:::i;:::-;2185:10;2159:37;2185:10;;;;2159:37;:::i;:::-;2158:38;2154:90;;22846:64;22854:36;;;22846:64;:::i;:::-;1758:24587;23034:18;;;;;;1758:24587;23054:3;1758:24587;23078:29;23096:10;;;;;;:::i;:::-;23078:29;:::i;:::-;1758:24587;;23077:30;23073:338;;23054:3;1758:24587;;23019:13;;23073:338;23199:10;;;;;;:::i;:::-;23243:14;;;;;:::i;:::-;1758:24587;;;;;;;;23145:131;23127:150;1758:24587;;;;;;;:::i;:::-;-1:-1:-1;;;;;1758:24587:12;;;23145:131;;;;:::i;:::-;23127:150;:::i;:::-;23295:29;23313:10;;;;;;:::i;23295:29::-;1758:24587;;-1:-1:-1;;1758:24587:12;;;;;23369:10;;;;;;:::i;:::-;23381:14;;;;;;:::i;:::-;1758:24587;;;;;;;;;;23354:42;1758:24587;23354:42;1758:24587;;23354:42;;;;;:::i;:::-;;;;23073:338;;;;1758:24587;;;;;;-1:-1:-1;;1758:24587:12;;;;;;:::i;:::-;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;5695:28;;;:::i;:::-;1758:24587;;5694:29;5690:81;;5822:24;;;:::i;:::-;1758:24587;;;;;;:::i;:::-;;;-1:-1:-1;;;;;1758:24587:12;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;5905:43;;:94;;;;1758:24587;5901:146;;;1758:24587;;;;;;;-1:-1:-1;;;;;1758:24587:12;6115:39;;;:::i;:::-;1758:24587;;6168:23;;;6164:149;;1758:24587;;-1:-1:-1;;;6416:44:12;;-1:-1:-1;;;;;1758:24587:12;;;;6416:44;;1758:24587;;;;;;6115:17;;1758:24587;;6416:44;;;;;;;1758:24587;6416:44;;;1758:24587;;;;;6479:128;;1758:24587;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;1758:24587:12;;;;;;;;;;;;;;;;6479:128;5746:14;;;1758:24587;6518:14;1758:24587;;6518:14;6416:44;;;;;;1758:24587;6416:44;;;;;;:::i;:::-;;;;;:::i;:::-;;;;;1758:24587;;;;;;;;;;;;;;;;;;6115:17;1758:24587;;5905:94;1758:24587;;;;;;;;;;5952:47;;5905:94;;;1758:24587;;;;;;-1:-1:-1;;1758:24587:12;;;;;;:::i;:::-;;;;;;;;;2159:37;2185:10;2159:37;:::i;:::-;2158:38;2154:90;;1758:24587;22212:25;;;:::i;:::-;1758:24587;;22211:26;22207:286;;1758:24587;22207:286;22448:34;1758:24587;22253:130;1758:24587;;;;;:::i;:::-;-1:-1:-1;;;;;1758:24587:12;;;;22271:111;;1758:24587;22271:111;;;:::i;22253:130::-;22397:25;;;:::i;:::-;22425:4;1758:24587;;;;;;;;22448:34;1758:24587;;22448:34;;;;;:::i;:::-;;;;1758:24587;;;;;;;-1:-1:-1;;1758:24587:12;;;;;;:::i;:::-;;;2159:37;2185:10;2159:37;:::i;:::-;2158:38;2154:90;;23784:20;1758:24587;23784:20;23957:31;23784:20;;:::i;:::-;1758:24587;;23783:21;23779:117;;1758:24587;-1:-1:-1;;;;;1758:24587:12;;;;;23905:19;1758:24587;;;;;;;;;;;;;;;;;;;;;;;;;;;;23957:31;1758:24587;23779:117;23820:24;;;:::i;:::-;23858:20;;;:::i;:::-;1758:24587;;-1:-1:-1;;1758:24587:12;;;;;23779:117;;1758:24587;;;;-1:-1:-1;;;;;1758:24587:12;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;-1:-1:-1;1758:24587:12;;;;;;;;-1:-1:-1;;1758:24587:12;;;;:::o;:::-;;;;;;;;;;;;;-1:-1:-1;;;;;1758:24587:12;;;;;;;;;;;;;;;;;:::o;:::-;;-1:-1:-1;;1758:24587:12;;;;;;;-1:-1:-1;;;;;1758:24587:12;;;;;;;;;;:::i;:::-;;;;;;;;-1:-1:-1;;;;;1758:24587:12;;;;;;;;;:::i;:::-;;;:::o;:::-;;;;;;;;;:::o;:::-;-1:-1:-1;;;;;1758:24587:12;;;;;23784:13;1758:24587;;;;;;:::o;:::-;-1:-1:-1;;;;;1758:24587:12;;;;;22212:17;1758:24587;;;;;;:::o;:::-;-1:-1:-1;;;;;1758:24587:12;;;;;5695:19;1758:24587;;;;;;:::o;:::-;-1:-1:-1;;;;;1758:24587:12;;;;;5822:15;1758:24587;;;;;;:::o;:::-;-1:-1:-1;;;;;1758:24587:12;;;;;21084:16;1758:24587;;;;;;:::o;:::-;;;;;;;;-1:-1:-1;1758:24587:12;;-1:-1:-1;1758:24587:12;;;-1:-1:-1;1758:24587:12;:::o;:::-;;;;;;;;;;;;;-1:-1:-1;;;;;;;;;;;1758:24587:12;-1:-1:-1;;;1758:24587:12;;;;;;;;;;-1:-1:-1;;;;;;;;;;;1758:24587:12;-1:-1:-1;;;;;;;;;;;1758:24587:12;:::i;:::-;;;-1:-1:-1;;;;;1758:24587:12;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;1758:24587:12;;;;;;;:::o;:::-;;;;;;;-1:-1:-1;;;;;1758:24587:12;;;;;;;:::o;:::-;;;;;;;-1:-1:-1;;;;;1758:24587:12;;;;;;;:::o;:::-;;;;;;;-1:-1:-1;;;;;1758:24587:12;;;;;;;:::o;:::-;;;;;;;;;;;;;-1:-1:-1;;;;;1758:24587:12;;;;;;;:::o;:::-;;;;;;;;:::o;:::-;-1:-1:-1;;;;;;;;;;;1758:24587:12;-1:-1:-1;;;1758:24587:12;;;;;;;;;;-1:-1:-1;;;;;;;;;;;1758:24587:12;-1:-1:-1;;;;;;;;;;;1758:24587:12;:::i;:::-;;;;;;;;;;-1:-1:-1;;;;;;1758:24587:12;-1:-1:-1;;;;;1758:24587:12;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;1758:24587:12;;;;;;-1:-1:-1;;;1758:24587:12;;;;:::o;:::-;;;;;;;;;;;;;-1:-1:-1;;;;;1758:24587:12;;;;;;;;;;;;;;;;:::i;:::-;:::o;:::-;;;;;;;;;;;;;;;;-1:-1:-1;1758:24587:12;;-1:-1:-1;1758:24587:12;;-1:-1:-1;1758:24587:12;;;;;;;;;;;;;;:::i;:::-;;;-1:-1:-1;;;;;1758:24587:12;;;;;;;;;;-1:-1:-1;1758:24587:12;;;;;;;;;;;;;-1:-1:-1;1758:24587:12;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;-1:-1:-1;1758:24587:12;;;;;-1:-1:-1;1758:24587:12;;-1:-1:-1;1758:24587:12;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;1758:24587:12;;;;;-1:-1:-1;1758:24587:12;;;;;;;;;;;;;;;-1:-1:-1;1758:24587:12;6115:17;1758:24587;;;-1:-1:-1;1758:24587:12;;:::o;:::-;;;;;;;;;;:::o;:::-;-1:-1:-1;;;;;1758:24587:12;;;;;;;;;:::o;:::-;;;;;;;;;;;-1:-1:-1;;;;;1758:24587:12;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;:::i;:::-;;;;;;;-1:-1:-1;;;;;1758:24587:12;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;1758:24587:12;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;-1:-1:-1;;;;;1758:24587:12;;;;;;;;;;;;;;;;-1:-1:-1;;;;;1758:24587:12;;;;;;;;;;;-1:-1:-1;;1758:24587:12;;;;;:::i;:::-;;;;;;;;;;;;;-1:-1:-1;1758:24587:12;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;-1:-1:-1;;;;;1758:24587:12;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;-1:-1:-1;;;1758:24587:12;;;;;;;;;;;;-1:-1:-1;;;1758:24587:12;;;;;;;;;;;;;;;;;;;:::o;:::-;;-1:-1:-1;;;;;1758:24587:12;;;;;;;:::o;:::-;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;:::i;:::-;;:::o;:::-;;;;;;;;;;-1:-1:-1;;1758:24587:12;;;;;;;;;;;;-1:-1:-1;;;;;1758:24587:12;;;;;;;;;;;;-1:-1:-1;;;;;1758:24587:12;;;;;;;-1:-1:-1;;1758:24587:12;;;;;;;;;-1:-1:-1;1758:24587:12;;;;;;;;:::i;:::-;;;;;;;:::o;:::-;;;;;:::o;19287:1306::-;;;1758:24587;;19602:26;;;:::i;:::-;1758:24587;;19601:27;19597:116;;19764:22;;;;:::i;:::-;1758:24587;;;;;:::i;:::-;;;-1:-1:-1;;;;;1758:24587:12;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;19842:91;;1758:24587;;;-1:-1:-1;;;19992:20:12;;-1:-1:-1;;;;;1758:24587:12;;;;19992:20;;1758:24587;;19992:20;1758:24587;19992:4;:20;;;;1758:24587;;;19992:20;;;19287:1306;-1:-1:-1;19988:599:12;;;;;;1758:24587;;;;;;19988:599;;;;;;1758:24587;20525:51;1758:24587;;;;;;;;:::i;:::-;;;;;;;;;20525:51;:::o;19988:599::-;20379:125;;:::i;:::-;;;;19988:599;;;20379:125;20449:42;1758:24587;;;;;20449:42;;;-1:-1:-1;;;20449:42:12;;;1758:24587;;;;;;;;;;;;;;;;;;20449:42;;1758:24587;;20449:42;;;;;;:::i;:::-;1758:24587;20424:69;1758:24587;20424:69;;:::o;1758:24587::-;;;;;;;;;;;;19988:599;20073:10;20069:96;;1758:24587;;;;;20195:26;20191:121;;1758:24587;20338:29;1758:24587;;;;;;;:::i;:::-;;;;20338:29;:::o;20191:121::-;1758:24587;20241:56;1758:24587;;;;;;;:::i;:::-;;;;;;;;;20241:56;:::o;20069:96::-;20103:47;;;1758:24587;20103:47;1758:24587;;;;;;;;:::i;:::-;;;;;;;;;20103:47;:::o;19992:20::-;;;;;;;1758:24587;19992:20;1758:24587;19992:20;;;;;;;;:::i;:::-;;;;;:::i;:::-;;;;;;;;;;19842:91;19881:41;;;1758:24587;19881:41;1758:24587;;;;;;;;:::i;:::-;;;;-1:-1:-1;;;1758:24587:12;;;;19881:41;:::o;19597:116::-;19644:58;;;;1758:24587;19644:58;1758:24587;;;;;;;;:::i;:::-;;;;;;;;;-1:-1:-1;;;1758:24587:12;;;;19644:58;:::o;4659:539::-;1758:24587;4892:26;;;:::i;:::-;1758:24587;;4888:229;;5179:12;1758:24587;4659:539;:::o;4888:229::-;4967:22;;;:::i;:::-;1758:24587;;;;;:::i;:::-;;;-1:-1:-1;;;;;1758:24587:12;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;5011:43;:94;;;;;5003:103;;:::o;5011:94::-;1758:24587;;;;;;;;;;5058:47;5003:103;:::o;1758:24587::-;;;;;;;:::i;:::-;-1:-1:-1;1758:24587:12;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;-1:-1:-1;;;;;1758:24587:12;;;;;;;;;;;;:::i;6789:276::-;1758:24587;;-1:-1:-1;;;6911:28:12;;-1:-1:-1;;;;;1758:24587:12;;;6911:28;;;1758:24587;-1:-1:-1;1758:24587:12;6911:28;1758:24587;6911:4;:28;;;;;;;1758:24587;6911:28;;;6789:276;1758:24587;;;;6949:24;;7004:30;;;:::i;:::-;7044:14;6789:276;:::o;6949:24::-;6965:8;1758:24587;6965:8;:::o;6911:28::-;;;;;;1758:24587;6911:28;;;;;;:::i;:::-;;;;1429:590:10;1696:15;1758:24587:12;-1:-1:-1;;;;;1758:24587:12;;;;;1685:26:10;1658:66;;1811:4;-1:-1:-1;1758:24587:12;;;1787:15:10;1758:24587:12;;;;;;;;;;;;;;;;-1:-1:-1;;;;;1758:24587:12;;;;;;;;;;;;1783:62:10;;-1:-1:-1;;;;;1758:24587:12;-1:-1:-1;1758:24587:12;;;;;;;;;;;;1922:59:10;;1758:24587:12;1429:590:10;:::o;1922:59::-;1662:19;1970:11;:::o;1783:62::-;1834:11;1662:19;1834:11;:::o;7277:2037:12:-;1758:24587;;:::i;:::-;-1:-1:-1;;;;;;7533:23:12;;;:::i;:::-;1758:24587;;7529:495;;7277:2037;8231:23;;;:::i;:::-;8268:11;8264:244;;8630:23;;;;;:::i;:::-;8667:12;8663:244;;9005:21;;1758:24587;;;;;;;-1:-1:-1;1758:24587:12;9005:14;1758:24587;;;-1:-1:-1;1758:24587:12;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;9040:16;;:81;;;;7277:2037;9036:125;;;;1758:24587;;;;;;:::i;:::-;-1:-1:-1;1758:24587:12;;;;9229:78;;1758:24587;;;;9229:78;;1758:24587;-1:-1:-1;;;1758:24587:12;9229:78;;1758:24587;;7277:2037::o;9040:81::-;9099:22;9060:15;;;:35;:15;;:35;:::i;9099:22::-;-1:-1:-1;9060:61:12;9040:81;;;;8663:244;1758:24587;;;-1:-1:-1;1758:24587:12;;;;;:::i;:::-;;;;8702:194;;;1758:24587;8818:15;1758:24587;8702:194;;1758:24587;-1:-1:-1;;;8702:194:12;;;1758:24587;;8695:201::o;8264:244::-;1758:24587;;;-1:-1:-1;1758:24587:12;;;;;:::i;:::-;;;;8302:195;;;1758:24587;8416:15;1758:24587;8302:195;;1758:24587;-1:-1:-1;;;8302:195:12;;;1758:24587;;8295:202::o;7529:495::-;7622:25;;;:::i;:::-;7665:9;;;:29;;;7529:495;7661:281;;7529:495;;;;7661:281;1758:24587;;;-1:-1:-1;1758:24587:12;;;;;:::i;:::-;;;;7721:206;;;1758:24587;7843:15;1758:24587;7721:206;;1758:24587;-1:-1:-1;;;7721:206:12;;;1758:24587;;7714:213::o;7665:29::-;1758:24587;7692:2;1758:24587;;;7678:16;;7665:29;;17336:272;-1:-1:-1;;;;;1758:24587:12;;;;;25272:13;1758:24587;;;;;;17560:13;;;;17336:272;:::o;17560:41::-;;1971:7;17336:272;:::o;1758:24587::-;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;:::o;:::-;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;;;;;;:::o;12833:1389::-;12962:16;;;1758:24587;;;12962:21;12958:40;;-1:-1:-1;13071:3:12;13161:13;-1:-1:-1;13200:3:12;13180:11;;;;;;1758:24587;;13176:22;;;;;-1:-1:-1;;;;;1758:24587:12;13334:14;;1758:24587;;13334:14;:::i;:::-;1758:24587;;;;;;;;13320:29;;;;;1758:24587;;13320:4;:29;:4;;:29;;;;;;;-1:-1:-1;;;13320:29:12;;;13200:3;13367:15;;;13363:34;;13551:11;13320:29;;1758:24587;;13180:11;;-1:-1:-1;;;;;1758:24587:12;13551:14;;1758:24587;;13551:14;:::i;:::-;1758:24587;;;;;;;;;;;13536:41;;;;;;;;;;;;-1:-1:-1;13536:41:12;;;13200:3;13613:13;:29;13612:50;13613:13;:16;13689:24;13613:13;;1758:24587;13647:14;13613:13;;;:16;:::i;:::-;1758:24587;13613:29;:::i;:::-;13647:14;;:::i;:::-;13612:50;;:::i;:::-;13689:24;;:::i;:::-;1758:24587;;;;;;;13784:25;13780:89;;13200:3;;1758:24587;;13161:13;;13780:89;;-1:-1:-1;1758:24587:12;13780:89;;13536:41;;;;;13180:11;13536:41;;;;;;;;;13180:11;13536:41;;;:::i;:::-;;;1758:24587;;;;;13647:14;13613:29;13612:50;1758:24587;13613:16;1758:24587;;;13689:24;1758:24587;;:::i;:::-;13536:41;;;;;;;;;;;;;;-1:-1:-1;13536:41:12;;13363:34;-1:-1:-1;;;;;;;;;;;;13384:13:12:o;13320:29::-;;;;;;;1758:24587;13320:29;;;;;;;;;:::i;:::-;;;;;13176:22;;;;;;;;1920:4;1758:24587;;;;;;1920:4;1758:24587;;;;;;;14162:53;1758:24587;13935:49;1758:24587;;13935:49;;:::i;:::-;14162:53;;:::i;12958:40::-;-1:-1:-1;;;;;;12985:13:12:o;1758:24587::-;;;;;;;;;;:::o;9506:1429::-;-1:-1:-1;;;;;1758:24587:12;9715:23;;;:::i;:::-;1758:24587;;9752:18;;;9748:37;;1758:24587;;-1:-1:-1;;;9800:46:12;;1758:24587;9800:46;1758:24587;9800:46;1758:24587;;9800:46;;;-1:-1:-1;;;;;;;;9800:46:12;;;9506:1429;-1:-1:-1;9796:1133:12;;10905:13;;;;;;-1:-1:-1;10905:13:12;-1:-1:-1;10905:13:12;:::o;9796:1133::-;-1:-1:-1;9995:11:12;;;9991:30;;1758:24587;;;;;;10114:25;10110:44;;10279:8;10249:27;:15;;:27;:::i;:::-;:38;10245:57;;10372:39;9800:46;1758:24587;;;;;;;;;;10372:39;;;;;;;;-1:-1:-1;10372:39:12;;;9796:1133;1758:24587;;;;10459:2;1758:24587;;;;;;10558:27;10452:21;;10433:41;10452:21;;:::i;:::-;10433:41;;:::i;:::-;10249:15;;10558:27;:::i;:::-;10610:9;10603:16;;10610:9;;10639:15;10652:2;10599:275;9506:1429::o;10599:275::-;10686:10;10679:17;;10686:10;;10716:15;10729:2;10675:199;9506:1429::o;10675:199::-;10763:7;-1:-1:-1;10763:7:12;;10803:2;10752:122;1758:24587::o;10752:122::-;10857:2;10752:122;1758:24587::o;10372:39::-;;;;;;;;;;;;;;;;:::i;:::-;;;1758:24587;;;;;;;;:::i;:::-;10372:39;;;;;-1:-1:-1;10372:39:12;;10245:57;10289:13;;;;-1:-1:-1;10289:13:12;-1:-1:-1;10289:13:12;:::o;9991:30::-;10008:13;;;;;;-1:-1:-1;10008:13:12;-1:-1:-1;10008:13:12;:::o;9800:46::-;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;1758:24587;;;;;;;:::i;:::-;;;;;;;;;;;;;;;:::i;:::-;9800:46;;;;;;;;;;;-1:-1:-1;9800:46:12;;9748:37;-1:-1:-1;;;;;9772:13:12:o;11227:1279::-;1758:24587;11479:28;;;:::i;:::-;1758:24587;;11478:29;11474:104;;11629:24;;;:::i;:::-;1758:24587;;;;;;:::i;:::-;;;-1:-1:-1;;;;;1758:24587:12;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;11710:43;;:94;;;;11227:1279;11706:157;;;1758:24587;;;;;;;-1:-1:-1;;;;;1758:24587:12;11931:39;;;:::i;:::-;1758:24587;;11984:23;;;11980:136;;1758:24587;;-1:-1:-1;;;12246:44:12;;-1:-1:-1;;;;;1758:24587:12;;;;12246:44;;1758:24587;;-1:-1:-1;;1758:24587:12;;11931:17;;1758:24587;;12246:44;;;;;;;-1:-1:-1;12246:44:12;;;11227:1279;1758:24587;;;;12309:85;;12469:30;;;:::i;12309:85::-;12339:13;-1:-1:-1;12339:13:12;-1:-1:-1;12339:13:12;:::o;12246:44::-;;;;;;-1:-1:-1;12246:44:12;;;;;;:::i;:::-;;;;11980:136;12023:13;;-1:-1:-1;12023:13:12;-1:-1:-1;12023:13:12;:::o;11710:94::-;1758:24587;;;;;;;;;;11757:47;;11710:94;;;14481:1524;-1:-1:-1;;;;;;;;;;;1758:24587:12;-1:-1:-1;;;;;;;;;;;1758:24587:12;14708:1;;14481:1524;14708:1;;1758:24587;;;;14708:1;14814:23;;;;;;15680:16;;;;;;;;15676:35;;15734:24;;;;:::i;:::-;15827:16;1758:24587;15827:16;;1758:24587;;15859:15;15872:2;15823:176;14481:1524::o;15823:176::-;15910:1;-1:-1:-1;15910:1:12;;15940:2;15891:108;14481:1524::o;15891:108::-;15986:2;15891:108;1758:24587::o;15676:35::-;-1:-1:-1;14708:1:12;;-1:-1:-1;14708:1:12;;15698:13::o;14839:3::-;1758:24587;;;;;;-1:-1:-1;;;;;;;;;;;14708:1:12;1758:24587;;;;;;:::i;:::-;14708:1;15008:23;;;;;;14839:3;;1758:24587;;14799:13;;15033:3;1758:24587;;;;;;-1:-1:-1;;;;;;;;;;;14708:1:12;1758:24587;;;;;-1:-1:-1;;;;;1758:24587:12;;;;;;15113:18;;15109:32;;15176:52;;;;;:::i;:::-;15250:9;15246:396;;15033:3;;;1758:24587;15033:3;14993:13;1758:24587;14993:13;;15246:396;1758:24587;;;;;;15367:24;;;;;1758:24587;;15367:4;:24;:4;;:24;;;;;;;14708:1;15367:24;;;15246:396;15417:13;;15413:211;15246:396;15413:211;15478:17;15540:22;15478:17;;;;1920:4;15478:17;;:::i;:::-;1758:24587;15540:22;;:::i;:::-;1758:24587;-1:-1:-1;;1758:24587:12;;;;;;;;15413:211;;;15246:396;;15367:24;;;;;1758:24587;15367:24;;;;;;;;;:::i;:::-;;;;;;15109:32;15133:8;1758:24587;15133:8;;;17854:899;18018:35;2377:12;18076:40;;2377:12;;1758:24587;;;;;18180:3;1758:24587;;;;;;18180:3;1758:24587;;;;;;;18186:3;1758:24587;;18186:3;18224:13;;:36;18186:3;;;18224:36;18186:3;18072:675;17854:899::o;18224:36::-;1758:24587;;;:::o;18072:675::-;2466:10;18281:35;;2466:10;;18332:25;;;;:::o;18277:470::-;1758:24587;;2568:9;18378:41;;2568:9;;1758:24587;;;18492:2;1758:24587;;;;;;18492:2;1758:24587;;;;;;;18497:3;1758:24587;;;;18374:373;17854:899::o;18374:373::-;2657:9;-1:-1:-1;2657:9:12;;1758:24587;;18630:2;1758:24587;;;;;;18630:2;1758:24587;;;;;;;18635:3;1758:24587;;;;18518:229;1758:24587::o;18518:229::-;1758:24587;;;18727:2;1758:24587;;;;;;18727:2;1758:24587;;;;;;;18732:3;1758:24587;;;;18518:229;1758:24587::o;16375:767::-;;;;16730:21;;;1758:24587;;;;;;;-1:-1:-1;;;;;1758:24587:12;16712:40;;;:::i;:::-;1758:24587;;16766:23;;;16762:37;;1758:24587;;;-1:-1:-1;;;16873:153:12;;-1:-1:-1;;;;;1758:24587:12;;;16873:153;;;1758:24587;;;;16712:17;1758:24587;;;;;;;;;;;1920:4;1758:24587;;;;16730:21;;1758:24587;;16873:153;;1758:24587;;16873:153;;1758:24587;;16873:153;;;16375:767;-1:-1:-1;16869:267:12;;17117:8;1758:24587;17117:8;:::o;16873:153::-;;;;16730:21;16873:153;;16730:21;16873:153;;;;;;16730:21;16873:153;;;:::i;:::-;;;1758:24587;;;;;16873:153;;;;;;;-1:-1:-1;16873:153:12;;16762:37;16791:8;;;;1758:24587;16791:8;:::o
Swarm Source
ipfs://82ac52bf3faefd9358a7be21277cf7fe741467c67df0a59d84bfad5defbc0c90
Loading...
Loading
Loading...
Loading
Loading...
Loading
Net Worth in USD
$0.00
Net Worth in S
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.