Feature Tip: Add private address tag to any address under My Name Tag !
Overview
ETH Balance
0 ETH
Eth Value
$0.00Latest 25 internal transactions (View All)
Advanced mode:
| Parent Transaction Hash | Method | Block |
From
|
|
To
|
||
|---|---|---|---|---|---|---|---|
| Parse Price Feed... | 24279954 | 7 hrs ago | 1 wei | ||||
| Parse And Valida... | 24279954 | 7 hrs ago | 1 wei | ||||
| Parse Price Feed... | 24279009 | 10 hrs ago | 1 wei | ||||
| Parse And Valida... | 24279009 | 10 hrs ago | 1 wei | ||||
| Parse Price Feed... | 24276023 | 20 hrs ago | 1 wei | ||||
| Parse And Valida... | 24276023 | 20 hrs ago | 1 wei | ||||
| Parse Price Feed... | 24275334 | 23 hrs ago | 1 wei | ||||
| Parse And Valida... | 24275334 | 23 hrs ago | 1 wei | ||||
| Parse Price Feed... | 24269118 | 43 hrs ago | 1 wei | ||||
| Parse And Valida... | 24269118 | 43 hrs ago | 1 wei | ||||
| Parse Price Feed... | 24269086 | 44 hrs ago | 1 wei | ||||
| Parse And Valida... | 24269086 | 44 hrs ago | 1 wei | ||||
| Parse Price Feed... | 24267898 | 2 days ago | 1 wei | ||||
| Parse And Valida... | 24267898 | 2 days ago | 1 wei | ||||
| Parse Price Feed... | 24267102 | 2 days ago | 1 wei | ||||
| Parse And Valida... | 24267102 | 2 days ago | 1 wei | ||||
| Parse Price Feed... | 24267084 | 2 days ago | 1 wei | ||||
| Parse And Valida... | 24267084 | 2 days ago | 1 wei | ||||
| Parse Price Feed... | 24264684 | 2 days ago | 1 wei | ||||
| Parse And Valida... | 24264684 | 2 days ago | 1 wei | ||||
| Parse Price Feed... | 24264665 | 2 days ago | 1 wei | ||||
| Parse And Valida... | 24264665 | 2 days ago | 1 wei | ||||
| Parse Price Feed... | 24264644 | 2 days ago | 1 wei | ||||
| Parse And Valida... | 24264644 | 2 days ago | 1 wei | ||||
| Parse Price Feed... | 24248925 | 4 days ago | 1 wei |
Loading...
Loading
Cross-Chain Transactions
Loading...
Loading
Contract Name:
WstEthOracleMiddleware
Compiler Version
v0.8.26+commit.8a97fa7a
Optimization Enabled:
Yes with 20000 runs
Other Settings:
cancun EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.26;
import { IWstETH } from "../interfaces/IWstETH.sol";
import { PriceInfo } from "../interfaces/OracleMiddleware/IOracleMiddlewareTypes.sol";
import { IUsdnProtocolTypes as Types } from "../interfaces/UsdnProtocol/IUsdnProtocolTypes.sol";
import { OracleMiddleware } from "./OracleMiddleware.sol";
/**
* @title Middleware Implementation For WstETH Price
* @notice This contract is used to get the price of wstETH from the eth price oracle.
*/
contract WstEthOracleMiddleware is OracleMiddleware {
/// @notice The wstETH contract.
IWstETH internal immutable _wstEth;
/**
* @param pythContract The address of the Pyth contract.
* @param pythPriceID The ID of the ETH Pyth price feed.
* @param chainlinkPriceFeed The address of the ETH Chainlink price feed.
* @param wstETH The address of the wstETH contract.
* @param chainlinkTimeElapsedLimit The duration after which a Chainlink price is considered stale.
*/
constructor(
address pythContract,
bytes32 pythPriceID,
address chainlinkPriceFeed,
address wstETH,
uint256 chainlinkTimeElapsedLimit
) OracleMiddleware(pythContract, pythPriceID, chainlinkPriceFeed, chainlinkTimeElapsedLimit) {
_wstEth = IWstETH(wstETH);
}
/**
* @inheritdoc OracleMiddleware
* @notice Parses and validates `data`, returns the corresponding price data by applying eth/wstETH ratio.
* @dev The data format is specific to the middleware and is simply forwarded from the user transaction's calldata.
* Wsteth price is calculated as follows: `ethPrice x stEthPerToken / 1 ether`.
* A fee amounting to exactly {validationCost} (with the same `data` and `action`) must be sent or the transaction
* will revert.
* @param actionId A unique identifier for the current action. This identifier can be used to link an `Initiate`
* call with the corresponding `Validate` call.
* @param targetTimestamp The target timestamp for validating the price data. For validation actions, this is the
* timestamp of the initiation.
* @param action Type of action for which the price is requested. The middleware may use this to alter the
* validation of the price or the returned price.
* @param data The data to be used to communicate with oracles, the format varies from middleware to middleware and
* can be different depending on the action.
* @return result_ The price and timestamp as {IOracleMiddlewareTypes.PriceInfo}.
*/
function parseAndValidatePrice(
bytes32 actionId,
uint128 targetTimestamp,
Types.ProtocolAction action,
bytes calldata data
) public payable virtual override returns (PriceInfo memory) {
PriceInfo memory ethPrice = super.parseAndValidatePrice(actionId, targetTimestamp, action, data);
uint256 stEthPerToken = _wstEth.stEthPerToken();
return PriceInfo({
price: ethPrice.price * stEthPerToken / 1 ether,
neutralPrice: ethPrice.neutralPrice * stEthPerToken / 1 ether,
timestamp: ethPrice.timestamp
});
}
}// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
import { IERC20Metadata } from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import { IERC20Permit } from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Permit.sol";
interface IWstETH is IERC20Metadata, IERC20Permit {
/**
* @notice Exchanges stETH to wstETH
* @param _stETHAmount The amount of stETH to wrap in exchange for wstETH
* @dev Requirements:
* - `_stETHAmount` must be non-zero
* - msg.sender must approve at least `_stETHAmount` stETH to this contract
* - msg.sender must have at least `_stETHAmount` of stETH
* User should first approve `_stETHAmount` to the WstETH contract
* @return Amount of wstETH user receives after wrap
*/
function wrap(uint256 _stETHAmount) external returns (uint256);
/**
* @notice Exchanges wstETH to stETH
* @param _wstETHAmount The amount of wstETH to unwrap in exchange for stETH
* @dev Requirements:
* - `_wstETHAmount` must be non-zero
* - msg.sender must have at least `_wstETHAmount` wstETH
* @return The amount of stETH user receives after unwrap
*/
function unwrap(uint256 _wstETHAmount) external returns (uint256);
/**
* @notice Get the amount of wstETH for a given amount of stETH
* @param _stETHAmount The amount of stETH
* @return The amount of wstETH for a given stETH amount
*/
function getWstETHByStETH(uint256 _stETHAmount) external view returns (uint256);
/**
* @notice Get the amount of stETH for a given amount of wstETH
* @param _wstETHAmount The amount of wstETH
* @return The amount of stETH for a given wstETH amount
*/
function getStETHByWstETH(uint256 _wstETHAmount) external view returns (uint256);
/**
* @notice Get the amount of stETH for a one wstETH
* @return The amount of stETH for 1 wstETH
*/
function stEthPerToken() external view returns (uint256);
/**
* @notice Get the amount of wstETH for a one stETH
* @return The amount of wstETH for a 1 stETH
*/
function tokensPerStEth() external view returns (uint256);
/**
* @notice Get the address of stETH
* @return The address of stETH
*/
function stETH() external view returns (address);
}// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
/**
* @notice The price and timestamp returned by the oracle middleware.
* @param price The validated asset price, potentially adjusted by the middleware.
* @param neutralPrice The neutral/average price of the asset.
* @param timestamp The timestamp of the price data.
*/
struct PriceInfo {
uint256 price;
uint256 neutralPrice;
uint256 timestamp;
}
/**
* @notice The price and timestamp returned by the Chainlink oracle.
* @param price The asset price formatted by the middleware.
* @param timestamp When the price was published on chain.
*/
struct ChainlinkPriceInfo {
int256 price;
uint256 timestamp;
}
/**
* @notice Representation of a Pyth price with a uint256 price.
* @param price The price of the asset.
* @param conf The confidence interval around the price (in dollars, absolute value).
* @param publishTime Unix timestamp describing when the price was published.
*/
struct FormattedPythPrice {
uint256 price;
uint256 conf;
uint256 publishTime;
}
/**
* @notice The price and timestamp returned by the Redstone oracle.
* @param price The asset price formatted by the middleware.
* @param timestamp The timestamp of the price data.
*/
struct RedstonePriceInfo {
uint256 price;
uint256 timestamp;
}
/**
* @notice The different confidence interval of a Pyth price.
* @dev Applied to the neutral price and available as `price`.
* @param Up Adjusted price at the upper bound of the confidence interval.
* @param Down Adjusted price at the lower bound of the confidence interval.
* @param None Neutral price without adjustment.
*/
enum ConfidenceInterval {
Up,
Down,
None
}// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
import { IERC20Metadata } from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import { HugeUint } from "@smardex-solidity-libraries-1/HugeUint.sol";
import { LibBitmap } from "solady/src/utils/LibBitmap.sol";
import { DoubleEndedQueue } from "../../libraries/DoubleEndedQueue.sol";
import { IBaseLiquidationRewardsManager } from "../LiquidationRewardsManager/IBaseLiquidationRewardsManager.sol";
import { IBaseOracleMiddleware } from "../OracleMiddleware/IBaseOracleMiddleware.sol";
import { IBaseRebalancer } from "../Rebalancer/IBaseRebalancer.sol";
import { IUsdn } from "../Usdn/IUsdn.sol";
interface IUsdnProtocolTypes {
/**
* @notice All possible action types for the protocol.
* @dev This is used for pending actions and to interact with the oracle middleware.
* @param None No particular action.
* @param Initialize The contract is being initialized.
* @param InitiateDeposit Initiating a `deposit` action.
* @param ValidateDeposit Validating a `deposit` action.
* @param InitiateWithdrawal Initiating a `withdraw` action.
* @param ValidateWithdrawal Validating a `withdraw` action.
* @param InitiateOpenPosition Initiating an `open` position action.
* @param ValidateOpenPosition Validating an `open` position action.
* @param InitiateClosePosition Initiating a `close` position action.
* @param ValidateClosePosition Validating a `close` position action.
* @param Liquidation The price is requested for a liquidation action.
*/
enum ProtocolAction {
None,
Initialize,
InitiateDeposit,
ValidateDeposit,
InitiateWithdrawal,
ValidateWithdrawal,
InitiateOpenPosition,
ValidateOpenPosition,
InitiateClosePosition,
ValidateClosePosition,
Liquidation
}
/**
* @notice The outcome of the call targeting a long position.
* @param Processed The call did what it was supposed to do.
* An initiate close has been completed / a pending action was validated.
* @param Liquidated The position has been liquidated by this call.
* @param PendingLiquidations The call cannot be completed because of pending liquidations.
* Try calling the {IUsdnProtocolActions.liquidate} function with a fresh price to unblock the situation.
*/
enum LongActionOutcome {
Processed,
Liquidated,
PendingLiquidations
}
/**
* @notice Classifies how far in its logic the {UsdnProtocolLongLibrary._triggerRebalancer} function made it to.
* @dev Used to estimate the gas spent by the function call to more accurately calculate liquidation rewards.
* @param None The rebalancer is not set.
* @param NoImbalance The protocol imbalance is not reached.
* @param PendingLiquidation The rebalancer position should be liquidated.
* @param NoCloseNoOpen The action neither closes nor opens a position.
* @param Closed The action only closes a position.
* @param Opened The action only opens a position.
* @param ClosedOpened The action closes and opens a position.
*/
enum RebalancerAction {
None,
NoImbalance,
PendingLiquidation,
NoCloseNoOpen,
Closed,
Opened,
ClosedOpened
}
/**
* @notice Information about a long user position.
* @param validated Whether the position was validated.
* @param timestamp The timestamp of the position start.
* @param user The user's address.
* @param totalExpo The total exposure of the position (0 for vault deposits). The product of the initial
* collateral and the initial leverage.
* @param amount The amount of initial collateral in the position.
*/
struct Position {
bool validated; // 1 byte
uint40 timestamp; // 5 bytes. Max 1_099_511_627_775 (36812-02-20 01:36:15)
address user; // 20 bytes
uint128 totalExpo; // 16 bytes. Max 340_282_366_920_938_463_463.374_607_431_768_211_455 ether
uint128 amount; // 16 bytes
}
/**
* @notice A pending action in the queue.
* @param action The action type.
* @param timestamp The timestamp of the initiate action.
* @param var0 See {DepositPendingAction}, {WithdrawalPendingAction} and {LongPendingAction}.
* @param to The target of the action.
* @param validator The address that is supposed to validate the action.
* @param securityDepositValue The security deposit of the pending action.
* @param var1 See {DepositPendingAction}, {WithdrawalPendingAction} and {LongPendingAction}.
* @param var2 See {DepositPendingAction}, {WithdrawalPendingAction} and {LongPendingAction}.
* @param var3 See {DepositPendingAction}, {WithdrawalPendingAction} and {LongPendingAction}.
* @param var4 See {DepositPendingAction}, {WithdrawalPendingAction} and {LongPendingAction}.
* @param var5 See {DepositPendingAction}, {WithdrawalPendingAction} and {LongPendingAction}.
* @param var6 See {DepositPendingAction}, {WithdrawalPendingAction} and {LongPendingAction}.
* @param var7 See {DepositPendingAction}, {WithdrawalPendingAction} and {LongPendingAction}.
*/
struct PendingAction {
ProtocolAction action; // 1 byte
uint40 timestamp; // 5 bytes
uint24 var0; // 3 bytes
address to; // 20 bytes
address validator; // 20 bytes
uint64 securityDepositValue; // 8 bytes
int24 var1; // 3 bytes
uint128 var2; // 16 bytes
uint128 var3; // 16 bytes
uint256 var4; // 32 bytes
uint256 var5; // 32 bytes
uint256 var6; // 32 bytes
uint256 var7; // 32 bytes
}
/**
* @notice A pending action in the queue for a vault deposit.
* @param action The action type.
* @param timestamp The timestamp of the initiate action.
* @param feeBps Fee for the deposit, in BPS.
* @param to The recipient of the funds.
* @param validator The address that is supposed to validate the action.
* @param securityDepositValue The security deposit of the pending action.
* @param _unused Unused field to align the struct to `PendingAction`.
* @param amount The amount of assets of the pending deposit.
* @param assetPrice The price of the asset at the time of the last update.
* @param totalExpo The total exposure at the time of the last update.
* @param balanceVault The balance of the vault at the time of the last update.
* @param balanceLong The balance of the long position at the time of the last update.
* @param usdnTotalShares The total supply of USDN shares at the time of the action.
*/
struct DepositPendingAction {
ProtocolAction action; // 1 byte
uint40 timestamp; // 5 bytes
uint24 feeBps; // 3 bytes
address to; // 20 bytes
address validator; // 20 bytes
uint64 securityDepositValue; // 8 bytes
uint24 _unused; // 3 bytes
uint128 amount; // 16 bytes
uint128 assetPrice; // 16 bytes
uint256 totalExpo; // 32 bytes
uint256 balanceVault; // 32 bytes
uint256 balanceLong; // 32 bytes
uint256 usdnTotalShares; // 32 bytes
}
/**
* @notice A pending action in the queue for a vault withdrawal.
* @param action The action type.
* @param timestamp The timestamp of the initiate action.
* @param feeBps Fee for the withdrawal, in BPS.
* @param to The recipient of the funds.
* @param validator The address that is supposed to validate the action.
* @param securityDepositValue The security deposit of the pending action.
* @param sharesLSB 3 least significant bytes of the withdrawal shares amount (uint152).
* @param sharesMSB 16 most significant bytes of the withdrawal shares amount (uint152).
* @param assetPrice The price of the asset at the time of the last update.
* @param totalExpo The total exposure at the time of the last update.
* @param balanceVault The balance of the vault at the time of the last update.
* @param balanceLong The balance of the long position at the time of the last update.
* @param usdnTotalShares The total shares supply of USDN at the time of the action.
*/
struct WithdrawalPendingAction {
ProtocolAction action; // 1 byte
uint40 timestamp; // 5 bytes
uint24 feeBps; // 3 bytes
address to; // 20 bytes
address validator; // 20 bytes
uint64 securityDepositValue; // 8 bytes
uint24 sharesLSB; // 3 bytes
uint128 sharesMSB; // 16 bytes
uint128 assetPrice; // 16 bytes
uint256 totalExpo; // 32 bytes
uint256 balanceVault; // 32 bytes
uint256 balanceLong; // 32 bytes
uint256 usdnTotalShares; // 32 bytes
}
/**
* @notice A pending action in the queue for a long position.
* @param action The action type.
* @param timestamp The timestamp of the initiate action.
* @param closeLiqPenalty The liquidation penalty of the tick (only used when closing a position).
* @param to The recipient of the position.
* @param validator The address that is supposed to validate the action.
* @param securityDepositValue The security deposit of the pending action.
* @param tick The tick of the position.
* @param closeAmount The portion of the initial position amount to close (only used when closing a position).
* @param closePosTotalExpo The total expo of the position (only used when closing a position).
* @param tickVersion The version of the tick.
* @param index The index of the position in the tick list.
* @param liqMultiplier A fixed precision representation of the liquidation multiplier (with
* `LIQUIDATION_MULTIPLIER_DECIMALS` decimals) used to calculate the effective price for a given tick number.
* @param closeBoundedPositionValue The amount that was removed from the long balance on
* {IUsdnProtocolActions.initiateClosePosition} (only used when closing a position).
*/
struct LongPendingAction {
ProtocolAction action; // 1 byte
uint40 timestamp; // 5 bytes
uint24 closeLiqPenalty; // 3 bytes
address to; // 20 bytes
address validator; // 20 bytes
uint64 securityDepositValue; // 8 bytes
int24 tick; // 3 bytes
uint128 closeAmount; // 16 bytes
uint128 closePosTotalExpo; // 16 bytes
uint256 tickVersion; // 32 bytes
uint256 index; // 32 bytes
uint256 liqMultiplier; // 32 bytes
uint256 closeBoundedPositionValue; // 32 bytes
}
/**
* @notice The data allowing to validate an actionable pending action.
* @param priceData An array of bytes, each representing the data to be forwarded to the oracle middleware to
* validate a pending action in the queue.
* @param rawIndices An array of raw indices in the pending actions queue, in the same order as the corresponding
* priceData.
*/
struct PreviousActionsData {
bytes[] priceData;
uint128[] rawIndices;
}
/**
* @notice Information of a liquidated tick.
* @param totalPositions The total number of positions in the tick.
* @param totalExpo The total expo of the tick.
* @param remainingCollateral The remaining collateral after liquidation.
* @param tickPrice The corresponding price.
* @param priceWithoutPenalty The price without the liquidation penalty.
*/
struct LiqTickInfo {
uint256 totalPositions;
uint256 totalExpo;
int256 remainingCollateral;
uint128 tickPrice;
uint128 priceWithoutPenalty;
}
/**
* @notice The effects of executed liquidations on the protocol.
* @param liquidatedPositions The total number of liquidated positions.
* @param remainingCollateral The remaining collateral after liquidation.
* @param newLongBalance The new balance of the long side.
* @param newVaultBalance The new balance of the vault side.
* @param isLiquidationPending Whether some ticks are still populated above the current price (left to liquidate).
* @param liquidatedTicks Information about the liquidated ticks.
*/
struct LiquidationsEffects {
uint256 liquidatedPositions;
int256 remainingCollateral;
uint256 newLongBalance;
uint256 newVaultBalance;
bool isLiquidationPending;
LiqTickInfo[] liquidatedTicks;
}
/**
* @notice Accumulator for tick data.
* @param totalExpo The sum of the total expo of each position in the tick.
* @param totalPos The number of positions in the tick.
* @param liquidationPenalty The liquidation penalty for the positions in the tick.
* @dev Since the liquidation penalty is a parameter that can be updated, we need to ensure that positions that get
* created with a given penalty, use this penalty throughout their lifecycle. As such, once a tick gets populated by
* a first position, it gets assigned the current liquidation penalty parameter value and can't use another value
* until it gets liquidated or all positions exit the tick.
*/
struct TickData {
uint256 totalExpo;
uint248 totalPos;
uint24 liquidationPenalty;
}
/**
* @notice The unique identifier for a long position.
* @param tick The tick of the position.
* @param tickVersion The version of the tick.
* @param index The index of the position in the tick list.
*/
struct PositionId {
int24 tick;
uint256 tickVersion;
uint256 index;
}
/**
* @notice Parameters for the internal {UsdnProtocolActionsLongLibrary._initiateOpenPosition} function.
* @param user The address of the user initiating the open position.
* @param to The address that will be the owner of the position.
* @param validator The address that is supposed to validate the action.
* @param amount The amount of assets to deposit.
* @param desiredLiqPrice The desired liquidation price, including the liquidation penalty.
* @param userMaxPrice The maximum price at which the position can be opened. The userMaxPrice is compared with the
* price after confidence interval, penalty, etc...
* @param userMaxLeverage The maximum leverage for the newly created position.
* @param deadline The deadline of the open position to be initiated.
* @param securityDepositValue The value of the security deposit for the newly created pending action.
* @param currentPriceData The current price data (used to calculate the temporary leverage and entry price,
* pending validation).
*/
struct InitiateOpenPositionParams {
address user;
address to;
address validator;
uint128 amount;
uint128 desiredLiqPrice;
uint128 userMaxPrice;
uint256 userMaxLeverage;
uint256 deadline;
uint64 securityDepositValue;
}
/**
* @notice Parameters for the internal {UsdnProtocolLongLibrary._prepareInitiateOpenPosition} function.
* @param validator The address that is supposed to validate the action.
* @param amount The amount of assets to deposit.
* @param desiredLiqPrice The desired liquidation price, including the liquidation penalty.
* @param userMaxPrice The maximum price at which the position can be opened. The userMaxPrice is compared with the
* price after confidence interval, penalty, etc...
* @param userMaxLeverage The maximum leverage for the newly created position.
* @param currentPriceData The current price data.
*/
struct PrepareInitiateOpenPositionParams {
address validator;
uint128 amount;
uint128 desiredLiqPrice;
uint256 userMaxPrice;
uint256 userMaxLeverage;
bytes currentPriceData;
}
/**
* @notice Parameters for the internal {UsdnProtocolActionsUtilsLibrary._prepareClosePositionData} function.
* @param to The recipient of the funds.
* @param validator The address that is supposed to validate the action.
* @param posId The unique identifier of the position.
* @param amountToClose The amount of collateral to remove from the position's amount.
* @param userMinPrice The minimum price at which the position can be closed.
* @param deadline The deadline until the position can be closed.
* @param currentPriceData The current price data.
* @param delegationSignature An EIP712 signature that proves the caller is authorized by the owner of the position
* to close it on their behalf.
* @param domainSeparatorV4 The domain separator v4.
*/
struct PrepareInitiateClosePositionParams {
address to;
address validator;
PositionId posId;
uint128 amountToClose;
uint256 userMinPrice;
uint256 deadline;
bytes currentPriceData;
bytes delegationSignature;
bytes32 domainSeparatorV4;
}
/**
* @notice Parameters for the internal {UsdnProtocolActionsLongLibrary._initiateClosePosition} function.
* @param to The recipient of the funds.
* @param validator The address that is supposed to validate the action.
* @param posId The unique identifier of the position.
* @param amountToClose The amount to close.
* @param userMinPrice The minimum price at which the position can be closed.
* @param deadline The deadline of the close position to be initiated.
* @param securityDepositValue The value of the security deposit for the newly created pending action.
* @param domainSeparatorV4 The domain separator v4 for EIP712 signature.
*/
struct InitiateClosePositionParams {
address to;
address payable validator;
uint256 deadline;
PositionId posId;
uint128 amountToClose;
uint256 userMinPrice;
uint64 securityDepositValue;
bytes32 domainSeparatorV4;
}
/**
* @dev Structure to hold the transient data during {UsdnProtocolActionsLongLibrary._initiateClosePosition}
* @param pos The position to close.
* @param liquidationPenalty The liquidation penalty.
* @param totalExpoToClose The total expo to close.
* @param lastPrice The price after the last balances update.
* @param tempPositionValue The bounded value of the position that was removed from the long balance.
* @param longTradingExpo The long trading expo.
* @param liqMulAcc The liquidation multiplier accumulator.
* @param isLiquidationPending Whether some ticks are still populated above the current price (left to liquidate).
*/
struct ClosePositionData {
Position pos;
uint24 liquidationPenalty;
uint128 totalExpoToClose;
uint128 lastPrice;
uint256 tempPositionValue;
uint256 longTradingExpo;
HugeUint.Uint512 liqMulAcc;
bool isLiquidationPending;
}
/**
* @dev Structure to hold the transient data during {UsdnProtocolActionsLongLibrary._validateOpenPosition}.
* @param action The long pending action.
* @param startPrice The new entry price of the position.
* @param lastPrice The price of the last balances update.
* @param tickHash The tick hash.
* @param pos The position object.
* @param liqPriceWithoutPenaltyNorFunding The liquidation price without penalty nor funding used to calculate the
* user leverage and the new total expo.
* @param liqPriceWithoutPenalty The new liquidation price without penalty.
* @param leverage The new leverage.
* @param oldPosValue The value of the position according to the old entry price and the _lastPrice.
* @param liquidationPenalty The liquidation penalty for the position's tick.
* @param isLiquidationPending Whether some ticks are still populated above the current price (left to liquidate).
*/
struct ValidateOpenPositionData {
LongPendingAction action;
uint128 startPrice;
uint128 lastPrice;
bytes32 tickHash;
Position pos;
uint128 liqPriceWithoutPenaltyNorFunding;
uint128 liqPriceWithoutPenalty;
uint256 leverage;
uint256 oldPosValue;
uint24 liquidationPenalty;
bool isLiquidationPending;
}
/**
* @dev Structure to hold the transient data during {UsdnProtocolActionsLongLibrary._initiateOpenPosition}.
* @param adjustedPrice The adjusted price with position fees applied.
* @param posId The unique identifier of the position.
* @param liquidationPenalty The liquidation penalty.
* @param positionTotalExpo The total expo of the position. The product of the initial collateral and the initial
* leverage.
* @param positionValue The value of the position, taking into account the position fee.
* @param liqMultiplier The liquidation multiplier represented with fixed precision.
* @param isLiquidationPending Whether some ticks are still populated above the current price (left to liquidate).
*/
struct InitiateOpenPositionData {
uint128 adjustedPrice;
PositionId posId;
uint24 liquidationPenalty;
uint128 positionTotalExpo;
uint256 positionValue;
uint256 liqMultiplier;
bool isLiquidationPending;
}
/**
* @notice Structure to hold the state of the protocol.
* @param totalExpo The long total expo.
* @param tradingExpo The long trading expo.
* @param longBalance The long balance.
* @param vaultBalance The vault balance.
* @param liqMultiplierAccumulator The liquidation multiplier accumulator.
*/
struct CachedProtocolState {
uint256 totalExpo;
uint256 tradingExpo;
uint256 longBalance;
uint256 vaultBalance;
HugeUint.Uint512 liqMultiplierAccumulator;
}
/**
* @notice Structure to hold transient data during the {UsdnProtocolActionsLongLibrary._calcRebalancerPositionTick}
* function.
* @param protocolMaxLeverage The protocol maximum leverage.
* @param longImbalanceTargetBps The long imbalance target in basis points.
* @param tradingExpoToFill The trading expo to fill.
* @param highestUsableTradingExpo The highest usable trading expo.
* @param currentLiqPenalty The current liquidation penalty.
* @param liqPriceWithoutPenalty The liquidation price without penalty.
*/
struct CalcRebalancerPositionTickData {
uint256 protocolMaxLeverage;
int256 longImbalanceTargetBps;
uint256 tradingExpoToFill;
uint256 highestUsableTradingExpo;
uint24 currentLiqPenalty;
uint128 liqPriceWithoutPenalty;
}
/**
* @notice Structure to hold the return values of the {UsdnProtocolActionsLongLibrary._calcRebalancerPositionTick}
* function.
* @param tick The tick of the rebalancer position, includes liquidation penalty.
* @param totalExpo The total expo of the rebalancer position.
* @param liquidationPenalty The liquidation penalty of the tick.
*/
struct RebalancerPositionData {
int24 tick;
uint128 totalExpo;
uint24 liquidationPenalty;
}
/**
* @notice Data structure for the {UsdnProtocolCoreLibrary._applyPnlAndFunding} function.
* @param tempLongBalance The new balance of the long side, could be negative (temporarily).
* @param tempVaultBalance The new balance of the vault side, could be negative (temporarily).
* @param lastPrice The last price.
*/
struct ApplyPnlAndFundingData {
int256 tempLongBalance;
int256 tempVaultBalance;
uint128 lastPrice;
}
/**
* @notice Data structure for tick to price conversion functions.
* @param tradingExpo The long side trading expo.
* @param accumulator The liquidation multiplier accumulator.
* @param tickSpacing The tick spacing.
*/
struct TickPriceConversionData {
uint256 tradingExpo;
HugeUint.Uint512 accumulator;
int24 tickSpacing;
}
/**
* @custom:storage-location erc7201:UsdnProtocol.storage.main.
* @notice Structure to hold the state of the protocol.
* @param _tickSpacing The liquidation tick spacing for storing long positions.
* A tick spacing of 1 is equivalent to a 0.01% increase in liquidation price between ticks. A tick spacing of
* 100 is equivalent to a ~1.005% increase in liquidation price between ticks.
* @param _asset The asset ERC20 contract.
* Assets with a blacklist are not supported because the protocol would be DoS if transfers revert.
* @param _assetDecimals The number of decimals used by the `_asset`.
* @param _priceFeedDecimals The price feed decimals (18).
* @param _usdn The USDN ERC20 contract.
* @param _sdex The SDEX ERC20 contract.
* @param _usdnMinDivisor The minimum divisor for USDN.
* @param _oracleMiddleware The oracle middleware contract.
* @param _liquidationRewardsManager The liquidation rewards manager contract.
* @param _rebalancer The rebalancer contract.
* @param _isRebalancer Whether an address is or has been a rebalancer.
* @param _minLeverage The minimum leverage for a position.
* @param _maxLeverage The maximum leverage for a position.
* @param _lowLatencyValidatorDeadline The deadline for a user to confirm their action with a low-latency oracle.
* After this deadline, any user can validate the action with the low-latency oracle until the
* OracleMiddleware's _lowLatencyDelay. This is an offset compared to the timestamp of the initiate action.
* @param _onChainValidatorDeadline The deadline for a user to confirm their action with an on-chain oracle.
* After this deadline, any user can validate the action with the on-chain oracle. This is an offset compared
* to the timestamp of the initiate action + the oracle middleware's _lowLatencyDelay.
* @param _safetyMarginBps Safety margin for the liquidation price of newly open positions, in basis points.
* @param _liquidationIteration The number of iterations to perform during the user's action (in tick).
* @param _protocolFeeBps The protocol fee in basis points.
* @param _rebalancerBonusBps Part of the remaining collateral that is given as a bonus to the Rebalancer upon
* liquidation of a tick, in basis points. The rest is sent to the Vault balance.
* @param _liquidationPenalty The liquidation penalty (in ticks).
* @param _EMAPeriod The moving average period of the funding rate.
* @param _fundingSF The scaling factor (SF) of the funding rate.
* @param _feeThreshold The threshold above which the fee will be sent.
* @param _openExpoImbalanceLimitBps The imbalance limit of the long expo for open actions (in basis points).
* As soon as the difference between the vault expo and the long expo exceeds this basis point limit in favor
* of long the open rebalancing mechanism is triggered, preventing the opening of a new long position.
* @param _withdrawalExpoImbalanceLimitBps The imbalance limit of the long expo for withdrawal actions (in basis
* points). As soon as the difference between vault expo and long expo exceeds this basis point limit in favor of
* long, the withdrawal rebalancing mechanism is triggered, preventing the withdrawal of the existing vault
* position.
* @param _depositExpoImbalanceLimitBps The imbalance limit of the vault expo for deposit actions (in basis points).
* As soon as the difference between the vault expo and the long expo exceeds this basis point limit in favor
* of the vault, the deposit vault rebalancing mechanism is triggered, preventing the opening of a new vault
* position.
* @param _closeExpoImbalanceLimitBps The imbalance limit of the vault expo for close actions (in basis points).
* As soon as the difference between the vault expo and the long expo exceeds this basis point limit in favor
* of the vault, the close rebalancing mechanism is triggered, preventing the close of an existing long position.
* @param _rebalancerCloseExpoImbalanceLimitBps The imbalance limit of the vault expo for close actions from the
* rebalancer (in basis points). As soon as the difference between the vault expo and the long expo exceeds this
* basis point limit in favor of the vault, the close rebalancing mechanism is triggered, preventing the close of an
* existing long position from the rebalancer contract.
* @param _longImbalanceTargetBps The target imbalance on the long side (in basis points)
* This value will be used to calculate how much of the missing trading expo the rebalancer position will try
* to compensate. A negative value means the rebalancer will compensate enough to go above the equilibrium. A
* positive value means the rebalancer will compensate but stay below the equilibrium.
* @param _positionFeeBps The position fee in basis points.
* @param _vaultFeeBps The fee for vault deposits and withdrawals, in basis points.
* @param _sdexRewardsRatioBps The ratio of SDEX rewards to send to the user (in basis points).
* @param _sdexBurnOnDepositRatio The ratio of USDN to SDEX tokens to burn on deposit.
* @param _feeCollector The fee collector's address.
* @param _securityDepositValue The deposit required for a new position.
* @param _targetUsdnPrice The nominal (target) price of USDN (with _priceFeedDecimals).
* @param _usdnRebaseThreshold The USDN price threshold to trigger a rebase (with _priceFeedDecimals).
* @param _minLongPosition The minimum long position size (with `_assetDecimals`).
* @param _lastFundingPerDay The funding rate calculated at the last update timestamp.
* @param _lastPrice The price of the asset during the last balances update (with price feed decimals).
* @param _lastUpdateTimestamp The timestamp of the last balances update.
* @param _pendingProtocolFee The pending protocol fee accumulator.
* @param _pendingActions The pending actions by the user (1 per user max).
* The value stored is an index into the `pendingActionsQueue` deque, shifted by one. A value of 0 means no
* pending action. Since the deque uses uint128 indices, the highest index will not overflow when adding one.
* @param _pendingActionsQueue The queue of pending actions.
* @param _balanceVault The balance of deposits (with `_assetDecimals`).
* @param _pendingBalanceVault The unreflected balance change due to pending vault actions (with `_assetDecimals`).
* @param _EMA The exponential moving average of the funding (0.0003 at initialization).
* @param _balanceLong The balance of long positions (with `_assetDecimals`).
* @param _totalExpo The total exposure of the long positions (with `_assetDecimals`).
* @param _liqMultiplierAccumulator The accumulator used to calculate the liquidation multiplier.
* This is the sum, for all ticks, of the total expo of positions inside the tick, multiplied by the
* unadjusted price of the tick which is `_tickData[tickHash].liquidationPenalty` below
* The unadjusted price is obtained with `TickMath.getPriceAtTick.
* @param _tickVersion The liquidation tick version.
* @param _longPositions The long positions per versioned tick (liquidation price).
* @param _tickData Accumulated data for a given tick and tick version.
* @param _highestPopulatedTick The highest tick with a position.
* @param _totalLongPositions Cache of the total long positions count.
* @param _tickBitmap The bitmap used to quickly find populated ticks.
* @param _protocolFallbackAddr The address of the fallback contract.
* @param _nonce The user EIP712 nonce.
*/
struct Storage {
// immutable
int24 _tickSpacing;
IERC20Metadata _asset;
uint8 _assetDecimals;
uint8 _priceFeedDecimals;
IUsdn _usdn;
IERC20Metadata _sdex;
uint256 _usdnMinDivisor;
// parameters
IBaseOracleMiddleware _oracleMiddleware;
IBaseLiquidationRewardsManager _liquidationRewardsManager;
IBaseRebalancer _rebalancer;
mapping(address => bool) _isRebalancer;
uint256 _minLeverage;
uint256 _maxLeverage;
uint128 _lowLatencyValidatorDeadline;
uint128 _onChainValidatorDeadline;
uint256 _safetyMarginBps;
uint16 _liquidationIteration;
uint16 _protocolFeeBps;
uint16 _rebalancerBonusBps;
uint24 _liquidationPenalty;
uint128 _EMAPeriod;
uint256 _fundingSF;
uint256 _feeThreshold;
int256 _openExpoImbalanceLimitBps;
int256 _withdrawalExpoImbalanceLimitBps;
int256 _depositExpoImbalanceLimitBps;
int256 _closeExpoImbalanceLimitBps;
int256 _rebalancerCloseExpoImbalanceLimitBps;
int256 _longImbalanceTargetBps;
uint16 _positionFeeBps;
uint16 _vaultFeeBps;
uint16 _sdexRewardsRatioBps;
uint32 _sdexBurnOnDepositRatio;
address _feeCollector;
uint64 _securityDepositValue;
uint128 _targetUsdnPrice;
uint128 _usdnRebaseThreshold;
uint256 _minLongPosition;
// state
int256 _lastFundingPerDay;
uint128 _lastPrice;
uint128 _lastUpdateTimestamp;
uint256 _pendingProtocolFee;
// pending actions queue
mapping(address => uint256) _pendingActions;
DoubleEndedQueue.Deque _pendingActionsQueue;
// vault
uint256 _balanceVault;
int256 _pendingBalanceVault;
// long positions
int256 _EMA;
uint256 _balanceLong;
uint256 _totalExpo;
HugeUint.Uint512 _liqMultiplierAccumulator;
mapping(int24 => uint256) _tickVersion;
mapping(bytes32 => Position[]) _longPositions;
mapping(bytes32 => TickData) _tickData;
int24 _highestPopulatedTick;
uint256 _totalLongPositions;
LibBitmap.Bitmap _tickBitmap;
// fallback
address _protocolFallbackAddr;
// EIP712
mapping(address => uint256) _nonce;
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.26;
import { AccessControlDefaultAdminRules } from
"@openzeppelin/contracts/access/extensions/AccessControlDefaultAdminRules.sol";
import { IBaseOracleMiddleware } from "../interfaces/OracleMiddleware/IBaseOracleMiddleware.sol";
import { IOracleMiddleware } from "../interfaces/OracleMiddleware/IOracleMiddleware.sol";
import {
ChainlinkPriceInfo,
ConfidenceInterval,
FormattedPythPrice,
PriceInfo
} from "../interfaces/OracleMiddleware/IOracleMiddlewareTypes.sol";
import { IUsdnProtocol } from "../interfaces/UsdnProtocol/IUsdnProtocol.sol";
import { IUsdnProtocolTypes as Types } from "../interfaces/UsdnProtocol/IUsdnProtocolTypes.sol";
import { ChainlinkOracle } from "./oracles/ChainlinkOracle.sol";
import { PythOracle } from "./oracles/PythOracle.sol";
/**
* @title Middleware Between Oracles And The USDN Protocol
* @notice This contract is used to get the price of an asset from different oracles.
* It is used by the USDN protocol to get the price of the USDN underlying asset.
*/
contract OracleMiddleware is IOracleMiddleware, PythOracle, ChainlinkOracle, AccessControlDefaultAdminRules {
/// @inheritdoc IOracleMiddleware
uint16 public constant BPS_DIVISOR = 10_000;
/// @inheritdoc IOracleMiddleware
uint16 public constant MAX_CONF_RATIO = BPS_DIVISOR * 2;
/// @notice The number of decimals for the returned price.
uint8 internal constant MIDDLEWARE_DECIMALS = 18;
/// @inheritdoc IOracleMiddleware
bytes32 public constant ADMIN_ROLE = keccak256("ADMIN_ROLE");
/**
* @notice The delay (in seconds) between the moment an action is initiated and the timestamp of the
* price data used to validate that action.
*/
uint256 internal _validationDelay = 24 seconds;
/// @notice Ratio to applied to the Pyth confidence interval (in basis points).
uint16 internal _confRatioBps = 4000; // to divide by BPS_DIVISOR
/**
* @notice The amount of time during which a low latency oracle price validation is available.
* @dev This value should be greater than or equal to `_lowLatencyValidatorDeadline` of the USDN protocol.
*/
uint16 internal _lowLatencyDelay = 20 minutes;
/**
* @param pythContract Address of the Pyth contract.
* @param pythFeedId The Pyth price feed ID for the asset.
* @param chainlinkPriceFeed Address of the Chainlink price feed.
* @param chainlinkTimeElapsedLimit The duration after which a Chainlink price is considered stale.
*/
constructor(address pythContract, bytes32 pythFeedId, address chainlinkPriceFeed, uint256 chainlinkTimeElapsedLimit)
PythOracle(pythContract, pythFeedId)
ChainlinkOracle(chainlinkPriceFeed, chainlinkTimeElapsedLimit)
AccessControlDefaultAdminRules(0, msg.sender)
{
_grantRole(ADMIN_ROLE, msg.sender);
}
/* -------------------------------------------------------------------------- */
/* Public view functions */
/* -------------------------------------------------------------------------- */
/// @inheritdoc IBaseOracleMiddleware
function parseAndValidatePrice(bytes32, uint128 targetTimestamp, Types.ProtocolAction action, bytes calldata data)
public
payable
virtual
returns (PriceInfo memory price_)
{
if (action == Types.ProtocolAction.None) {
return
_getLowLatencyPrice(data, targetTimestamp, ConfidenceInterval.None, targetTimestamp + _lowLatencyDelay);
} else if (action == Types.ProtocolAction.Initialize) {
return _getInitiateActionPrice(data, ConfidenceInterval.None);
} else if (action == Types.ProtocolAction.ValidateDeposit) {
// use the lowest price in the confidence interval to ensure a minimum benefit for the user in case
// of price inaccuracies until low latency delay is exceeded then use chainlink specified roundId
return _getValidateActionPrice(data, targetTimestamp, ConfidenceInterval.Down);
} else if (action == Types.ProtocolAction.ValidateWithdrawal) {
// use the highest price in the confidence interval to ensure a minimum benefit for the user in case
// of price inaccuracies until low latency delay is exceeded then use chainlink specified roundId
return _getValidateActionPrice(data, targetTimestamp, ConfidenceInterval.Up);
} else if (action == Types.ProtocolAction.ValidateOpenPosition) {
// use the highest price in the confidence interval to ensure a minimum benefit for the user in case
// of price inaccuracies until low latency delay is exceeded then use chainlink specified roundId
return _getValidateActionPrice(data, targetTimestamp, ConfidenceInterval.Up);
} else if (action == Types.ProtocolAction.ValidateClosePosition) {
// use the lowest price in the confidence interval to ensure a minimum benefit for the user in case
// of price inaccuracies until low latency delay is exceeded then use chainlink specified roundId
return _getValidateActionPrice(data, targetTimestamp, ConfidenceInterval.Down);
} else if (action == Types.ProtocolAction.Liquidation) {
// special case, if we pass a timestamp of zero, then we accept all prices newer than
// `_pythRecentPriceDelay`
return _getLowLatencyPrice(data, 0, ConfidenceInterval.None, 0);
} else if (action == Types.ProtocolAction.InitiateDeposit) {
// if the user chooses to initiate with pyth, the neutral price will be used so no confidence is needed
return _getInitiateActionPrice(data, ConfidenceInterval.None);
} else if (action == Types.ProtocolAction.InitiateWithdrawal) {
// if the user chooses to initiate with pyth, the neutral price will be used so no confidence is needed
return _getInitiateActionPrice(data, ConfidenceInterval.None);
} else if (action == Types.ProtocolAction.InitiateOpenPosition) {
// if the user chooses to initiate with pyth, the neutral price will be used so no confidence is needed
return _getInitiateActionPrice(data, ConfidenceInterval.None);
} else if (action == Types.ProtocolAction.InitiateClosePosition) {
// if the user chooses to initiate with pyth, the neutral price will be used so no confidence is needed
return _getInitiateActionPrice(data, ConfidenceInterval.None);
}
}
/// @inheritdoc IBaseOracleMiddleware
function getValidationDelay() external view returns (uint256 delay_) {
return _validationDelay;
}
/// @inheritdoc IBaseOracleMiddleware
function getDecimals() external pure returns (uint8 decimals_) {
return MIDDLEWARE_DECIMALS;
}
/// @inheritdoc IOracleMiddleware
function getConfRatioBps() external view returns (uint16 ratio_) {
return _confRatioBps;
}
/// @inheritdoc IBaseOracleMiddleware
function getLowLatencyDelay() external view returns (uint16 delay_) {
return _lowLatencyDelay;
}
/// @inheritdoc IBaseOracleMiddleware
function validationCost(bytes calldata data, Types.ProtocolAction) public view virtual returns (uint256 result_) {
if (_isPythData(data)) {
result_ = _getPythUpdateFee(data);
}
}
/* -------------------------------------------------------------------------- */
/* Internal functions */
/* -------------------------------------------------------------------------- */
/**
* @notice Gets the price from the low-latency oracle (Pyth).
* @param data The signed price update data.
* @param actionTimestamp The timestamp of the action corresponding to the price. If zero, then we must accept all
* prices younger than {PythOracle._pythRecentPriceDelay}.
* @param dir The direction for the confidence interval adjusted price.
* @param targetLimit The most recent timestamp a price can have (can be zero if `actionTimestamp` is zero).
* @return price_ The price from the low-latency oracle, adjusted according to the confidence interval direction.
*/
function _getLowLatencyPrice(
bytes calldata data,
uint128 actionTimestamp,
ConfidenceInterval dir,
uint128 targetLimit
) internal virtual returns (PriceInfo memory price_) {
// if actionTimestamp is 0 we're performing a liquidation and we don't add the validation delay
if (actionTimestamp > 0) {
// add the validation delay to the action timestamp to get the timestamp of the price data used to
// validate
actionTimestamp += uint128(_validationDelay);
}
FormattedPythPrice memory pythPrice =
_getFormattedPythPrice(data, actionTimestamp, MIDDLEWARE_DECIMALS, targetLimit);
price_ = _adjustPythPrice(pythPrice, dir);
}
/**
* @notice Gets the price for an `initiate` action of the protocol.
* @dev If the data parameter is not empty, validate the price with {PythOracle}. Else, get the on-chain price from
* {ChainlinkOracle} and compare its timestamp with the latest seen Pyth price (cached). If Pyth is more recent, we
* return it. Otherwise, we return the Chainlink price. For the latter, we don't have a confidence interval, so both
* `neutralPrice` and `price` are equal.
* @param data An optional VAA from Pyth.
* @param dir The direction when applying the confidence interval (when using a Pyth price).
* @return price_ The price to use for the user action.
*/
function _getInitiateActionPrice(bytes calldata data, ConfidenceInterval dir)
internal
returns (PriceInfo memory price_)
{
// if data is not empty, use pyth
if (data.length > 0) {
// since we use this function for `initiate` type actions which pass `targetTimestamp = block.timestamp`,
// we should pass `0` to the function below to signal that we accept any recent price
return _getLowLatencyPrice(data, 0, dir, 0);
}
// chainlink calls do not require a fee
if (msg.value > 0) {
revert OracleMiddlewareIncorrectFee();
}
ChainlinkPriceInfo memory chainlinkOnChainPrice = _getFormattedChainlinkLatestPrice(MIDDLEWARE_DECIMALS);
// check if the cached pyth price is more recent and return it instead
FormattedPythPrice memory latestPythPrice = _getLatestStoredPythPrice(MIDDLEWARE_DECIMALS);
if (chainlinkOnChainPrice.timestamp <= latestPythPrice.publishTime) {
// we use the same price age limit as for chainlink here
if (latestPythPrice.publishTime < block.timestamp - _timeElapsedLimit) {
revert OracleMiddlewarePriceTooOld(latestPythPrice.publishTime);
}
return _adjustPythPrice(latestPythPrice, dir);
}
// if the price equals PRICE_TOO_OLD then the tolerated time elapsed for price validity was exceeded, revert
if (chainlinkOnChainPrice.price == PRICE_TOO_OLD) {
revert OracleMiddlewarePriceTooOld(chainlinkOnChainPrice.timestamp);
}
// if the price is negative or zero, revert
if (chainlinkOnChainPrice.price <= 0) {
revert OracleMiddlewareWrongPrice(chainlinkOnChainPrice.price);
}
price_ = PriceInfo({
price: uint256(chainlinkOnChainPrice.price),
neutralPrice: uint256(chainlinkOnChainPrice.price),
timestamp: chainlinkOnChainPrice.timestamp
});
}
/**
* @notice Applies the confidence interval in the `dir` direction, scaled by the configured {_confRatioBps}.
* @param pythPrice The formatted Pyth price object.
* @param dir The direction of the confidence interval to apply.
* @return price_ The adjusted price according to the confidence interval and confidence ratio.
*/
function _adjustPythPrice(FormattedPythPrice memory pythPrice, ConfidenceInterval dir)
internal
view
returns (PriceInfo memory price_)
{
if (dir == ConfidenceInterval.Down) {
uint256 adjust = (pythPrice.conf * _confRatioBps) / BPS_DIVISOR;
if (adjust >= pythPrice.price) {
// avoid underflow or zero price due to confidence interval adjustment
price_.price = 1;
} else {
// strictly positive
unchecked {
price_.price = pythPrice.price - adjust;
}
}
} else if (dir == ConfidenceInterval.Up) {
price_.price = pythPrice.price + ((pythPrice.conf * _confRatioBps) / BPS_DIVISOR);
} else {
price_.price = pythPrice.price;
}
price_.timestamp = pythPrice.publishTime;
price_.neutralPrice = pythPrice.price;
}
/**
* @notice Gets the price for a validate action of the protocol.
* @dev If the low latency delay is not exceeded, validate the price with the low-latency oracle(s).
* Else, get the specified roundId on-chain price from Chainlink. In case of chainlink price,
* we don't have a confidence interval and so both `neutralPrice` and `price` are equal.
* @param data An optional VAA from Pyth or a chainlink roundId (abi-encoded uint80).
* @param targetTimestamp The timestamp of the initiate action.
* @param dir The direction for applying the confidence interval (in case we use a Pyth price).
* @return price_ The price to use for the user action.
*/
function _getValidateActionPrice(bytes calldata data, uint128 targetTimestamp, ConfidenceInterval dir)
internal
returns (PriceInfo memory price_)
{
uint128 targetLimit = targetTimestamp + _lowLatencyDelay;
if (block.timestamp <= targetLimit) {
return _getLowLatencyPrice(data, targetTimestamp, dir, targetLimit);
}
// chainlink calls do not require a fee
if (msg.value > 0) {
revert OracleMiddlewareIncorrectFee();
}
uint80 validateRoundId = abi.decode(data, (uint80));
// check that the round ID is valid and get its price data
ChainlinkPriceInfo memory chainlinkOnChainPrice = _validateChainlinkRoundId(targetLimit, validateRoundId);
price_ = PriceInfo({
price: uint256(chainlinkOnChainPrice.price),
neutralPrice: uint256(chainlinkOnChainPrice.price),
timestamp: chainlinkOnChainPrice.timestamp
});
}
/**
* @notice Checks that the given round ID is valid and returns its corresponding price data.
* @dev Round IDs are not necessarily consecutive, so additional computing can be necessary to find
* the previous round ID.
* @param targetLimit The timestamp of the initiate action + {_lowLatencyDelay}.
* @param roundId The round ID to validate.
* @return providedRoundPrice_ The price data of the provided round ID.
*/
function _validateChainlinkRoundId(uint128 targetLimit, uint80 roundId)
internal
view
returns (ChainlinkPriceInfo memory providedRoundPrice_)
{
providedRoundPrice_ = _getFormattedChainlinkPrice(MIDDLEWARE_DECIMALS, roundId);
if (providedRoundPrice_.price <= 0) {
revert OracleMiddlewareWrongPrice(providedRoundPrice_.price);
}
(,,, uint256 previousRoundTimestamp,) = _priceFeed.getRoundData(roundId - 1);
// if the provided round's timestamp is 0, it's possible the aggregator recently changed and there is no data
// available for the previous round ID in the aggregator. In that case, we accept the given round ID as the
// sole reference with additional checks to make sure it is not too far from the target timestamp
if (previousRoundTimestamp == 0) {
// calculate the provided round's phase ID
uint80 roundPhaseId = roundId >> 64;
// calculate the first valid round ID for this phase
uint80 firstRoundId = (roundPhaseId << 64) + 1;
// the provided round ID must be the first round ID of the phase, if not, revert
if (firstRoundId != roundId) {
revert OracleMiddlewareInvalidRoundId();
}
// make sure that the provided round ID is not newer than it should be
if (providedRoundPrice_.timestamp > targetLimit + _timeElapsedLimit) {
revert OracleMiddlewareInvalidRoundId();
}
} else if (previousRoundTimestamp > targetLimit) {
// previous round should precede targetLimit
revert OracleMiddlewareInvalidRoundId();
}
if (providedRoundPrice_.timestamp <= targetLimit) {
revert OracleMiddlewareInvalidRoundId();
}
}
/**
* @notice Checks if the passed calldata corresponds to a Pyth message.
* @param data The calldata pointer to the message.
* @return isPythData_ Whether the data is a valid Pyth message or not.
*/
function _isPythData(bytes calldata data) internal pure returns (bool isPythData_) {
if (data.length <= 32) {
return false;
}
// check the first 4 bytes of the data to identify a pyth message
uint32 magic;
assembly {
magic := shr(224, calldataload(data.offset))
}
// Pyth magic stands for PNAU (Pyth Network Accumulator Update)
return magic == 0x504e4155;
}
/* -------------------------------------------------------------------------- */
/* Privileged functions */
/* -------------------------------------------------------------------------- */
/// @inheritdoc IOracleMiddleware
function setValidationDelay(uint256 newValidationDelay) external onlyRole(ADMIN_ROLE) {
_validationDelay = newValidationDelay;
emit ValidationDelayUpdated(newValidationDelay);
}
/// @inheritdoc IOracleMiddleware
function setChainlinkTimeElapsedLimit(uint256 newTimeElapsedLimit) external onlyRole(ADMIN_ROLE) {
_timeElapsedLimit = newTimeElapsedLimit;
emit TimeElapsedLimitUpdated(newTimeElapsedLimit);
}
/// @inheritdoc IOracleMiddleware
function setPythRecentPriceDelay(uint64 newDelay) external onlyRole(ADMIN_ROLE) {
if (newDelay < 10 seconds) {
revert OracleMiddlewareInvalidRecentPriceDelay(newDelay);
}
if (newDelay > 10 minutes) {
revert OracleMiddlewareInvalidRecentPriceDelay(newDelay);
}
_pythRecentPriceDelay = newDelay;
emit PythRecentPriceDelayUpdated(newDelay);
}
/// @inheritdoc IOracleMiddleware
function setConfRatio(uint16 newConfRatio) external onlyRole(ADMIN_ROLE) {
// confidence ratio limit check
if (newConfRatio > MAX_CONF_RATIO) {
revert OracleMiddlewareConfRatioTooHigh();
}
_confRatioBps = newConfRatio;
emit ConfRatioUpdated(newConfRatio);
}
/// @inheritdoc IOracleMiddleware
function setLowLatencyDelay(uint16 newLowLatencyDelay, IUsdnProtocol usdnProtocol) external onlyRole(ADMIN_ROLE) {
if (newLowLatencyDelay > 90 minutes) {
revert OracleMiddlewareInvalidLowLatencyDelay();
}
if (newLowLatencyDelay < usdnProtocol.getLowLatencyValidatorDeadline()) {
revert OracleMiddlewareInvalidLowLatencyDelay();
}
_lowLatencyDelay = newLowLatencyDelay;
emit LowLatencyDelayUpdated(newLowLatencyDelay);
}
/// @inheritdoc IOracleMiddleware
function withdrawEther(address to) external onlyRole(ADMIN_ROLE) {
if (to == address(0)) {
revert OracleMiddlewareTransferToZeroAddress();
}
// slither-disable-next-line arbitrary-send-eth
(bool success,) = payable(to).call{ value: address(this).balance }("");
if (!success) {
revert OracleMiddlewareTransferFailed(to);
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/extensions/IERC20Metadata.sol)
pragma solidity ^0.8.20;
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) (token/ERC20/extensions/IERC20Permit.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC-20 Permit extension allowing approvals to be made via signatures, as defined in
* https://eips.ethereum.org/EIPS/eip-2612[ERC-2612].
*
* Adds the {permit} method, which can be used to change an account's ERC-20 allowance (see {IERC20-allowance}) by
* presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
* need to send a transaction, and thus is not required to hold Ether at all.
*
* ==== Security Considerations
*
* There are two important considerations concerning the use of `permit`. The first is that a valid permit signature
* expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be
* considered as an intention to spend the allowance in any specific way. The second is that because permits have
* built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should
* take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be
* generally recommended is:
*
* ```solidity
* function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public {
* try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {}
* doThing(..., value);
* }
*
* function doThing(..., uint256 value) public {
* token.safeTransferFrom(msg.sender, address(this), value);
* ...
* }
* ```
*
* Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of
* `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also
* {SafeERC20-safeTransferFrom}).
*
* Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so
* contracts should have entry points that don't rely on permit.
*/
interface IERC20Permit {
/**
* @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
* given ``owner``'s signed approval.
*
* IMPORTANT: The same issues {IERC20-approve} has related to transaction
* ordering also apply here.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `spender` cannot be the zero address.
* - `deadline` must be a timestamp in the future.
* - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
* over the EIP712-formatted function arguments.
* - the signature must use ``owner``'s current nonce (see {nonces}).
*
* For more information on the signature format, see the
* https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
* section].
*
* CAUTION: See Security Considerations above.
*/
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external;
/**
* @dev Returns the current nonce for `owner`. This value must be
* included whenever a signature is generated for {permit}.
*
* Every successful call to {permit} increases ``owner``'s nonce by one. This
* prevents a signature from being used multiple times.
*/
function nonces(address owner) external view returns (uint256);
/**
* @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
*/
// solhint-disable-next-line func-name-mixedcase
function DOMAIN_SEPARATOR() external view returns (bytes32);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
/**
* @notice A library for manipulating uint512 quantities.
* @dev The 512-bit unsigned integers are represented as two uint256 "limbs", a `hi` limb for the most significant bits,
* and a `lo` limb for the least-significant bits. The resulting uint512 quantity is obtained with `hi * 2^256 + lo`.
*/
library HugeUint {
/// @notice Indicates that the division failed because the divisor is zero or the result overflows a uint256.
error HugeUintDivisionFailed();
/// @notice Indicates that the addition overflowed a uint512.
error HugeUintAddOverflow();
/// @notice Indicates that the subtraction underflowed.
error HugeUintSubUnderflow();
/// @notice Indicates that the multiplication overflowed a uint512.
error HugeUintMulOverflow();
/**
* @notice A 512-bit integer represented as two 256-bit limbs.
* @dev The integer value can be reconstructed as `hi * 2^256 + lo`.
* @param hi The most-significant bits (higher limb) of the integer.
* @param lo The least-significant bits (lower limb) of the integer.
*/
struct Uint512 {
uint256 hi;
uint256 lo;
}
/**
* @notice Wraps a uint256 into a {Uint512} integer.
* @param x A uint256 integer.
* @return The same value as a 512-bit integer.
*/
function wrap(uint256 x) internal pure returns (Uint512 memory) {
return Uint512({ hi: 0, lo: x });
}
/**
* @notice Calculates the sum `a + b` of two 512-bit unsigned integers.
* @dev This function will revert if the result overflows a uint512.
* @param a The first operand.
* @param b The second operand.
* @return res_ The sum of `a` and `b`.
*/
function add(Uint512 memory a, Uint512 memory b) internal pure returns (Uint512 memory res_) {
(res_.lo, res_.hi) = _add(a.lo, a.hi, b.lo, b.hi);
// check for overflow, i.e. if the result is less than b
if (res_.hi < b.hi || (res_.hi == b.hi && res_.lo < b.lo)) {
revert HugeUintAddOverflow();
}
}
/**
* @notice Calculates the difference `a - b` of two 512-bit unsigned integers.
* @dev This function will revert if `b > a`.
* @param a The first operand.
* @param b The second operand.
* @return res_ The difference `a - b`.
*/
function sub(Uint512 memory a, Uint512 memory b) internal pure returns (Uint512 memory res_) {
// check for underflow
if (a.hi < b.hi || (a.hi == b.hi && a.lo < b.lo)) {
revert HugeUintSubUnderflow();
}
(res_.lo, res_.hi) = _sub(a.lo, a.hi, b.lo, b.hi);
}
/**
* @notice Calculates the product `a * b` of two 256-bit unsigned integers using the Chinese remainder theorem.
* @param a The first operand.
* @param b The second operand.
* @return res_ The product `a * b` of the operands as an unsigned 512-bit integer.
*/
function mul(uint256 a, uint256 b) internal pure returns (Uint512 memory res_) {
(res_.lo, res_.hi) = _mul256(a, b);
}
/**
* @notice Calculates the product `a * b` of a 512-bit unsigned integer and a 256-bit unsigned integer.
* @dev This function reverts if the result overflows a uint512.
* @param a The first operand.
* @param b The second operand.
* @return res_ The product `a * b` of the operands as an unsigned 512-bit integer.
*/
function mul(Uint512 memory a, uint256 b) internal pure returns (Uint512 memory res_) {
if ((a.hi == 0 && a.lo == 0) || b == 0) {
return res_;
}
(res_.lo, res_.hi) = _mul256(a.lo, b);
unchecked {
uint256 p = a.hi * b;
if (p / b != a.hi) {
revert HugeUintMulOverflow();
}
res_.hi += p;
if (res_.hi < p) {
revert HugeUintMulOverflow();
}
}
}
/**
* @notice Calculates the division `floor(a / b)` of a 512-bit unsigned integer by an unsigned 256-bit integer.
* @dev The call will revert if the result doesn't fit inside a uint256 or if the denominator is zero.
* @param a The numerator as a 512-bit unsigned integer.
* @param b The denominator as a 256-bit unsigned integer.
* @return res_ The division `floor(a / b)` of the operands as an unsigned 256-bit integer.
*/
function div(Uint512 memory a, uint256 b) internal pure returns (uint256 res_) {
// make sure the output fits inside a uint256, also prevents b == 0
if (b <= a.hi) {
revert HugeUintDivisionFailed();
}
// if the numerator is smaller than the denominator, the result is zero
if (a.hi == 0 && a.lo < b) {
return 0;
}
// the first operand fits in 256 bits, we can use the Solidity division operator
if (a.hi == 0) {
unchecked {
return a.lo / b;
}
}
res_ = _div256(a.lo, a.hi, b);
}
/**
* @notice Computes the division `floor(a/b)` of two 512-bit integers, knowing the result fits inside a uint256.
* @dev Credits chfast (Apache 2.0 License): <https://github.com/chfast/intx>.
* This function will revert if the second operand is zero or if the result doesn't fit inside a uint256.
* @param a The numerator as a 512-bit integer.
* @param b The denominator as a 512-bit integer.
* @return res_ The quotient floor(a/b).
*/
function div(Uint512 memory a, Uint512 memory b) internal pure returns (uint256 res_) {
res_ = _div(a.lo, a.hi, b.lo, b.hi);
}
/**
* @notice Calculates the sum `a + b` of two 512-bit unsigned integers.
* @dev Credits Remco Bloemen (MIT license): <https://2π.com/17/512-bit-division>.
* The result is not checked for overflow, the caller must ensure that the result fits inside a uint512.
* @param a0 The low limb of the first operand.
* @param a1 The high limb of the first operand.
* @param b0 The low limb of the second operand.
* @param b1 The high limb of the second operand.
* @return lo_ The low limb of the result of `a + b`.
* @return hi_ The high limb of the result of `a + b`.
*/
function _add(uint256 a0, uint256 a1, uint256 b0, uint256 b1) internal pure returns (uint256 lo_, uint256 hi_) {
assembly {
lo_ := add(a0, b0)
hi_ := add(add(a1, b1), lt(lo_, a0))
}
}
/**
* @notice Calculates the difference `a - b` of two 512-bit unsigned integers.
* @dev Credits Remco Bloemen (MIT license): <https://2π.com/17/512-bit-division>.
* The result is not checked for underflow, the caller must ensure that the second operand is less than or equal to
* the first operand.
* @param a0 The low limb of the first operand.
* @param a1 The high limb of the first operand.
* @param b0 The low limb of the second operand.
* @param b1 The high limb of the second operand.
* @return lo_ The low limb of the result of `a - b`.
* @return hi_ The high limb of the result of `a - b`.
*/
function _sub(uint256 a0, uint256 a1, uint256 b0, uint256 b1) internal pure returns (uint256 lo_, uint256 hi_) {
assembly {
lo_ := sub(a0, b0)
hi_ := sub(sub(a1, b1), lt(a0, b0))
}
}
/**
* @notice Calculates the product `a * b` of two 256-bit unsigned integers using the Chinese remainder theorem.
* @dev Credits Remco Bloemen (MIT license): <https://2π.com/17/chinese-remainder-theorem>
* and Solady (MIT license): <https://github.com/Vectorized/solady>.
* @param a The first operand.
* @param b The second operand.
* @return lo_ The low limb of the result of `a * b`.
* @return hi_ The high limb of the result of `a * b`.
*/
function _mul256(uint256 a, uint256 b) internal pure returns (uint256 lo_, uint256 hi_) {
assembly {
lo_ := mul(a, b)
let mm := mulmod(a, b, not(0)) // (a * b) % uint256.max
hi_ := sub(mm, add(lo_, lt(mm, lo_)))
}
}
/**
* @notice Calculates the division `floor(a / b)` of a 512-bit unsigned integer by an unsigned 256-bit integer.
* @dev Credits Solady (MIT license): <https://github.com/Vectorized/solady>.
* The caller must ensure that the result fits inside a uint256 and that the division is non-zero.
* For performance reasons, the caller should ensure that the numerator high limb (hi) is non-zero.
* @param a0 The low limb of the numerator.
* @param a1 The high limb of the numerator.
* @param b The denominator as a 256-bit unsigned integer.
* @return res_ The division `floor(a / b)` of the operands as an unsigned 256-bit integer.
*/
function _div256(uint256 a0, uint256 a1, uint256 b) internal pure returns (uint256 res_) {
uint256 r;
assembly {
// to make the division exact, we find out the remainder of the division of a by b
r := mulmod(a1, not(0), b) // (a1 * uint256.max) % b
r := addmod(r, a1, b) // (r + a1) % b
r := addmod(r, a0, b) // (r + a0) % b
// `t` is the least significant bit of `b`
// always greater or equal to 1
let t := and(b, sub(0, b))
// divide `b` by `t`, which is a power of two
b := div(b, t)
// invert `b mod 2**256`
// now that `b` is an odd number, it has an inverse
// modulo `2**256` such that `b * inv = 1 mod 2**256`
// compute the inverse by starting with a seed that is
// correct for four bits. That is, `b * inv = 1 mod 2**4`
let inv := xor(2, mul(3, b))
// now use Newton-Raphson iteration to improve the precision
// thanks to Hensel's lifting lemma, this also works in modular
// arithmetic, doubling the correct bits in each step
inv := mul(inv, sub(2, mul(b, inv))) // inverse mod 2**8
inv := mul(inv, sub(2, mul(b, inv))) // inverse mod 2**16
inv := mul(inv, sub(2, mul(b, inv))) // inverse mod 2**32
inv := mul(inv, sub(2, mul(b, inv))) // inverse mod 2**64
inv := mul(inv, sub(2, mul(b, inv))) // inverse mod 2**128
res_ :=
mul(
// divide [a1 a0] by the factors of two
// shift in bits from `a1` into `a0`
// for this we need to flip `t` such that it is `2**256 / t`
or(mul(sub(a1, gt(r, a0)), add(div(sub(0, t), t), 1)), div(sub(a0, r), t)),
// inverse mod 2**256
mul(inv, sub(2, mul(b, inv)))
)
}
}
/**
* @notice Computes the division of a 768-bit integer `a` by a 512-bit integer `b`, knowing the reciprocal of `b`.
* @dev Credits chfast (Apache 2.0 License): <https://github.com/chfast/intx>.
* @param a0 The LSB of the numerator.
* @param a1 The middle limb of the numerator.
* @param a2 The MSB of the numerator.
* @param b0 The low limb of the divisor.
* @param b1 The high limb of the divisor.
* @param v The reciprocal `v` as defined in `_reciprocal_2`.
* @return The quotient floor(a/b).
*/
function _div_2(uint256 a0, uint256 a1, uint256 a2, uint256 b0, uint256 b1, uint256 v)
internal
pure
returns (uint256)
{
(uint256 q0, uint256 q1) = _mul256(v, a2);
(q0, q1) = _add(q0, q1, a1, a2);
(uint256 t0, uint256 t1) = _mul256(b0, q1);
uint256 r1;
assembly {
r1 := sub(a1, mul(q1, b1))
}
uint256 r0;
(r0, r1) = _sub(a0, r1, t0, t1);
(r0, r1) = _sub(r0, r1, b0, b1);
assembly {
q1 := add(q1, 1)
}
if (r1 >= q0) {
assembly {
q1 := sub(q1, 1)
}
(r0, r1) = _add(r0, r1, b0, b1);
}
if (r1 > b1 || (r1 == b1 && r0 >= b0)) {
assembly {
q1 := add(q1, 1)
}
// we don't care about the remainder
// (r0, r1) = _sub(r0, r1, b0, b1);
}
return q1;
}
/**
* @notice Computes the division floor(a/b) of two 512-bit integers, knowing the result fits inside a uint256.
* @dev Credits chfast (Apache 2.0 License): <https://github.com/chfast/intx>.
* @param a0 LSB of the numerator.
* @param a1 MSB of the numerator.
* @param b0 LSB of the divisor.
* @param b1 MSB of the divisor.
* @return res_ The quotient floor(a/b).
*/
function _div(uint256 a0, uint256 a1, uint256 b0, uint256 b1) internal pure returns (uint256 res_) {
if (b1 == 0) {
// prevent division by zero
if (b0 == 0) {
revert HugeUintDivisionFailed();
}
// if both operands fit inside a uint256, we can use the Solidity division operator
if (a1 == 0) {
unchecked {
return a0 / b0;
}
}
// if the result fits inside a uint256, we can use the `div(Uint512,uint256)` function
if (b0 > a1) {
return _div256(a0, a1, b0);
}
revert HugeUintDivisionFailed();
}
// if the numerator is smaller than the denominator, the result is zero
if (a1 < b1 || (a1 == b1 && a0 < b0)) {
return 0;
}
// division algo
uint256 lsh = _clz(b1);
if (lsh == 0) {
// numerator is equal or larger than the denominator, and the denominator is at least 0b1000...
// the result is necessarily 1
return 1;
}
uint256 bn_lo;
uint256 bn_hi;
uint256 an_lo;
uint256 an_hi;
uint256 an_ex;
assembly {
let rsh := sub(256, lsh)
bn_lo := shl(lsh, b0)
bn_hi := or(shl(lsh, b1), shr(rsh, b0))
an_lo := shl(lsh, a0)
an_hi := or(shl(lsh, a1), shr(rsh, a0))
an_ex := shr(rsh, a1)
}
uint256 v = _reciprocal_2(bn_lo, bn_hi);
res_ = _div_2(an_lo, an_hi, an_ex, bn_lo, bn_hi, v);
}
/**
* @notice Computes the reciprocal `v = floor((2^512-1) / d) - 2^256`.
* @dev The input must be normalized (d >= 2^255).
* @param d The input value.
* @return v_ The reciprocal of d.
*/
function _reciprocal(uint256 d) internal pure returns (uint256 v_) {
if (d & 0x8000000000000000000000000000000000000000000000000000000000000000 == 0) {
revert HugeUintDivisionFailed();
}
v_ = _div256(type(uint256).max, type(uint256).max - d, d);
}
/**
* @notice Computes the reciprocal `v = floor((2^768-1) / d) - 2^256`, where d is a uint512 integer.
* @dev Credits chfast (Apache 2.0 License): <https://github.com/chfast/intx>.
* @param d0 LSB of the input.
* @param d1 MSB of the input.
* @return v_ The reciprocal of d.
*/
function _reciprocal_2(uint256 d0, uint256 d1) internal pure returns (uint256 v_) {
v_ = _reciprocal(d1);
uint256 p;
assembly {
p := mul(d1, v_)
p := add(p, d0)
if lt(p, d0) {
// carry out
v_ := sub(v_, 1)
if iszero(lt(p, d1)) {
v_ := sub(v_, 1)
p := sub(p, d1)
}
p := sub(p, d1)
}
}
(uint256 t0, uint256 t1) = _mul256(v_, d0);
assembly {
p := add(p, t1)
if lt(p, t1) {
// carry out
v_ := sub(v_, 1)
if and(iszero(lt(p, d1)), or(gt(p, d1), iszero(lt(t0, d0)))) {
// if (<p, t0> >= <d1, d0>)
v_ := sub(v_, 1)
}
}
}
}
/**
* @notice Counts the number of consecutive zero bits, starting from the left.
* @dev Credits Solady (MIT license): <https://github.com/Vectorized/solady>.
* @param x An unsigned integer.
* @return n_ The number of zeroes starting from the most significant bit.
*/
function _clz(uint256 x) internal pure returns (uint256 n_) {
if (x == 0) {
return 256;
}
assembly {
n_ := shl(7, lt(0xffffffffffffffffffffffffffffffff, x))
n_ := or(n_, shl(6, lt(0xffffffffffffffff, shr(n_, x))))
n_ := or(n_, shl(5, lt(0xffffffff, shr(n_, x))))
n_ := or(n_, shl(4, lt(0xffff, shr(n_, x))))
n_ := or(n_, shl(3, lt(0xff, shr(n_, x))))
n_ :=
add(
xor(
n_,
byte(
and(0x1f, shr(shr(n_, x), 0x8421084210842108cc6318c6db6d54be)),
0xf8f9f9faf9fdfafbf9fdfcfdfafbfcfef9fafdfafcfcfbfefafafcfbffffffff
)
),
iszero(x)
)
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
import {LibBit} from "./LibBit.sol";
/// @notice Library for storage of packed unsigned booleans.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/LibBitmap.sol)
/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/LibBitmap.sol)
/// @author Modified from Solidity-Bits (https://github.com/estarriolvetch/solidity-bits/blob/main/contracts/BitMaps.sol)
library LibBitmap {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CONSTANTS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The constant returned when a bitmap scan does not find a result.
uint256 internal constant NOT_FOUND = type(uint256).max;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* STRUCTS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev A bitmap in storage.
struct Bitmap {
mapping(uint256 => uint256) map;
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns the boolean value of the bit at `index` in `bitmap`.
function get(Bitmap storage bitmap, uint256 index) internal view returns (bool isSet) {
// It is better to set `isSet` to either 0 or 1, than zero vs non-zero.
// Both cost the same amount of gas, but the former allows the returned value
// to be reused without cleaning the upper bits.
uint256 b = (bitmap.map[index >> 8] >> (index & 0xff)) & 1;
/// @solidity memory-safe-assembly
assembly {
isSet := b
}
}
/// @dev Updates the bit at `index` in `bitmap` to true.
function set(Bitmap storage bitmap, uint256 index) internal {
bitmap.map[index >> 8] |= (1 << (index & 0xff));
}
/// @dev Updates the bit at `index` in `bitmap` to false.
function unset(Bitmap storage bitmap, uint256 index) internal {
bitmap.map[index >> 8] &= ~(1 << (index & 0xff));
}
/// @dev Flips the bit at `index` in `bitmap`.
/// Returns the boolean result of the flipped bit.
function toggle(Bitmap storage bitmap, uint256 index) internal returns (bool newIsSet) {
/// @solidity memory-safe-assembly
assembly {
mstore(0x20, bitmap.slot)
mstore(0x00, shr(8, index))
let storageSlot := keccak256(0x00, 0x40)
let shift := and(index, 0xff)
let storageValue := xor(sload(storageSlot), shl(shift, 1))
// It makes sense to return the `newIsSet`,
// as it allow us to skip an additional warm `sload`,
// and it costs minimal gas (about 15),
// which may be optimized away if the returned value is unused.
newIsSet := and(1, shr(shift, storageValue))
sstore(storageSlot, storageValue)
}
}
/// @dev Updates the bit at `index` in `bitmap` to `shouldSet`.
function setTo(Bitmap storage bitmap, uint256 index, bool shouldSet) internal {
/// @solidity memory-safe-assembly
assembly {
mstore(0x20, bitmap.slot)
mstore(0x00, shr(8, index))
let storageSlot := keccak256(0x00, 0x40)
let storageValue := sload(storageSlot)
let shift := and(index, 0xff)
sstore(
storageSlot,
// Unsets the bit at `shift` via `and`, then sets its new value via `or`.
or(and(storageValue, not(shl(shift, 1))), shl(shift, iszero(iszero(shouldSet))))
)
}
}
/// @dev Consecutively sets `amount` of bits starting from the bit at `start`.
function setBatch(Bitmap storage bitmap, uint256 start, uint256 amount) internal {
/// @solidity memory-safe-assembly
assembly {
let max := not(0)
let shift := and(start, 0xff)
mstore(0x20, bitmap.slot)
mstore(0x00, shr(8, start))
if iszero(lt(add(shift, amount), 257)) {
let storageSlot := keccak256(0x00, 0x40)
sstore(storageSlot, or(sload(storageSlot), shl(shift, max)))
let bucket := add(mload(0x00), 1)
let bucketEnd := add(mload(0x00), shr(8, add(amount, shift)))
amount := and(add(amount, shift), 0xff)
shift := 0
for {} iszero(eq(bucket, bucketEnd)) { bucket := add(bucket, 1) } {
mstore(0x00, bucket)
sstore(keccak256(0x00, 0x40), max)
}
mstore(0x00, bucket)
}
let storageSlot := keccak256(0x00, 0x40)
sstore(storageSlot, or(sload(storageSlot), shl(shift, shr(sub(256, amount), max))))
}
}
/// @dev Consecutively unsets `amount` of bits starting from the bit at `start`.
function unsetBatch(Bitmap storage bitmap, uint256 start, uint256 amount) internal {
/// @solidity memory-safe-assembly
assembly {
let shift := and(start, 0xff)
mstore(0x20, bitmap.slot)
mstore(0x00, shr(8, start))
if iszero(lt(add(shift, amount), 257)) {
let storageSlot := keccak256(0x00, 0x40)
sstore(storageSlot, and(sload(storageSlot), not(shl(shift, not(0)))))
let bucket := add(mload(0x00), 1)
let bucketEnd := add(mload(0x00), shr(8, add(amount, shift)))
amount := and(add(amount, shift), 0xff)
shift := 0
for {} iszero(eq(bucket, bucketEnd)) { bucket := add(bucket, 1) } {
mstore(0x00, bucket)
sstore(keccak256(0x00, 0x40), 0)
}
mstore(0x00, bucket)
}
let storageSlot := keccak256(0x00, 0x40)
sstore(
storageSlot, and(sload(storageSlot), not(shl(shift, shr(sub(256, amount), not(0)))))
)
}
}
/// @dev Returns number of set bits within a range by
/// scanning `amount` of bits starting from the bit at `start`.
function popCount(Bitmap storage bitmap, uint256 start, uint256 amount)
internal
view
returns (uint256 count)
{
unchecked {
uint256 bucket = start >> 8;
uint256 shift = start & 0xff;
if (!(amount + shift < 257)) {
count = LibBit.popCount(bitmap.map[bucket] >> shift);
uint256 bucketEnd = bucket + ((amount + shift) >> 8);
amount = (amount + shift) & 0xff;
shift = 0;
for (++bucket; bucket != bucketEnd; ++bucket) {
count += LibBit.popCount(bitmap.map[bucket]);
}
}
count += LibBit.popCount((bitmap.map[bucket] >> shift) << (256 - amount));
}
}
/// @dev Returns the index of the most significant set bit in `[0..upTo]`.
/// If no set bit is found, returns `NOT_FOUND`.
function findLastSet(Bitmap storage bitmap, uint256 upTo)
internal
view
returns (uint256 setBitIndex)
{
setBitIndex = NOT_FOUND;
uint256 bucket = upTo >> 8;
uint256 bits;
/// @solidity memory-safe-assembly
assembly {
mstore(0x00, bucket)
mstore(0x20, bitmap.slot)
let offset := and(0xff, not(upTo)) // `256 - (255 & upTo) - 1`.
bits := shr(offset, shl(offset, sload(keccak256(0x00, 0x40))))
if iszero(or(bits, iszero(bucket))) {
for {} 1 {} {
bucket := add(bucket, setBitIndex) // `sub(bucket, 1)`.
mstore(0x00, bucket)
bits := sload(keccak256(0x00, 0x40))
if or(bits, iszero(bucket)) { break }
}
}
}
if (bits != 0) {
setBitIndex = (bucket << 8) | LibBit.fls(bits);
/// @solidity memory-safe-assembly
assembly {
setBitIndex := or(setBitIndex, sub(0, gt(setBitIndex, upTo)))
}
}
}
/// @dev Returns the index of the least significant unset bit in `[begin..upTo]`.
/// If no unset bit is found, returns `NOT_FOUND`.
function findFirstUnset(Bitmap storage bitmap, uint256 begin, uint256 upTo)
internal
view
returns (uint256 unsetBitIndex)
{
unsetBitIndex = NOT_FOUND;
uint256 bucket = begin >> 8;
uint256 negBits;
/// @solidity memory-safe-assembly
assembly {
mstore(0x00, bucket)
mstore(0x20, bitmap.slot)
let offset := and(0xff, begin)
negBits := shl(offset, shr(offset, not(sload(keccak256(0x00, 0x40)))))
if iszero(negBits) {
let lastBucket := shr(8, upTo)
for {} 1 {} {
bucket := add(bucket, 1)
mstore(0x00, bucket)
negBits := not(sload(keccak256(0x00, 0x40)))
if or(negBits, gt(bucket, lastBucket)) { break }
}
if gt(bucket, lastBucket) {
negBits := shl(and(0xff, not(upTo)), shr(and(0xff, not(upTo)), negBits))
}
}
}
if (negBits != 0) {
uint256 r = (bucket << 8) | LibBit.ffs(negBits);
/// @solidity memory-safe-assembly
assembly {
unsetBitIndex := or(r, sub(0, or(gt(r, upTo), lt(r, begin))))
}
}
}
}// SPDX-License-Identifier: MIT
// based on the OpenZeppelin implementation
pragma solidity ^0.8.20;
import { IUsdnProtocolTypes as Types } from "../interfaces/UsdnProtocol/IUsdnProtocolTypes.sol";
/**
* @notice A sequence of items with the ability to efficiently push and pop items (i.e. insert and remove) on both ends
* of the sequence (called front and back).
* @dev Storage use is optimized, and all operations are O(1) constant time.
*
* The struct is called `Deque` and holds {IUsdnProtocolTypes.PendingAction}'s. This data structure can only be used in
* storage, and not in memory.
*/
library DoubleEndedQueue {
/// @dev An operation (e.g. {front}) couldn't be completed due to the queue being empty.
error QueueEmpty();
/// @dev A push operation couldn't be completed due to the queue being full.
error QueueFull();
/// @dev An operation (e.g. {atRaw}) couldn't be completed due to an index being out of bounds.
error QueueOutOfBounds();
/**
* @dev Indices are 128 bits so begin and end are packed in a single storage slot for efficient access.
*
* Struct members have an underscore prefix indicating that they are "private" and should not be read or written to
* directly. Use the functions provided below instead. Modifying the struct manually may violate assumptions and
* lead to unexpected behavior.
*
* The first item is at `data[begin]` and the last item is at `data[end - 1]`. This range can wrap around.
* @param _begin The index of the first item in the queue.
* @param _end The index of the item after the last item in the queue.
* @param _data The items in the queue.
*/
struct Deque {
uint128 _begin;
uint128 _end;
mapping(uint128 index => Types.PendingAction) _data;
}
/**
* @dev Inserts an item at the end of the queue.
* Reverts with {QueueFull} if the queue is full.
* @param deque The queue.
* @param value The item to insert.
* @return backIndex_ The raw index of the inserted item.
*/
function pushBack(Deque storage deque, Types.PendingAction memory value) external returns (uint128 backIndex_) {
unchecked {
backIndex_ = deque._end;
if (backIndex_ + 1 == deque._begin) {
revert QueueFull();
}
deque._data[backIndex_] = value;
deque._end = backIndex_ + 1;
}
}
/**
* @dev Removes the item at the end of the queue and returns it.
* Reverts with {QueueEmpty} if the queue is empty.
* @param deque The queue.
* @return value_ The removed item.
*/
function popBack(Deque storage deque) public returns (Types.PendingAction memory value_) {
unchecked {
uint128 backIndex = deque._end;
if (backIndex == deque._begin) {
revert QueueEmpty();
}
--backIndex;
value_ = deque._data[backIndex];
delete deque._data[backIndex];
deque._end = backIndex;
}
}
/**
* @dev Inserts an item at the beginning of the queue.
* Reverts with {QueueFull} if the queue is full.
* @param deque The queue.
* @param value The item to insert.
* @return frontIndex_ The raw index of the inserted item.
*/
function pushFront(Deque storage deque, Types.PendingAction memory value) external returns (uint128 frontIndex_) {
unchecked {
frontIndex_ = deque._begin - 1;
if (frontIndex_ == deque._end) {
revert QueueFull();
}
deque._data[frontIndex_] = value;
deque._begin = frontIndex_;
}
}
/**
* @dev Removes the item at the beginning of the queue and returns it.
* Reverts with {QueueEmpty} if the queue is empty.
* @param deque The queue.
* @return value_ The removed item.
*/
function popFront(Deque storage deque) public returns (Types.PendingAction memory value_) {
unchecked {
uint128 frontIndex = deque._begin;
if (frontIndex == deque._end) {
revert QueueEmpty();
}
value_ = deque._data[frontIndex];
delete deque._data[frontIndex];
deque._begin = frontIndex + 1;
}
}
/**
* @dev Returns the item at the beginning of the queue.
* Reverts with {QueueEmpty} if the queue is empty.
* @param deque The queue.
* @return value_ The item at the front of the queue.
* @return rawIndex_ The raw index of the returned item.
*/
function front(Deque storage deque) external view returns (Types.PendingAction memory value_, uint128 rawIndex_) {
if (empty(deque)) {
revert QueueEmpty();
}
rawIndex_ = deque._begin;
value_ = deque._data[rawIndex_];
}
/**
* @dev Returns the item at the end of the queue.
* Reverts with {QueueEmpty} if the queue is empty.
* @param deque The queue.
* @return value_ The item at the back of the queue.
* @return rawIndex_ The raw index of the returned item.
*/
function back(Deque storage deque) external view returns (Types.PendingAction memory value_, uint128 rawIndex_) {
if (empty(deque)) {
revert QueueEmpty();
}
unchecked {
rawIndex_ = deque._end - 1;
value_ = deque._data[rawIndex_];
}
}
/**
* @dev Returns the item at a position in the queue given by `index`, with the first item at 0 and the last item at
* `length(deque) - 1`.
* Reverts with {QueueOutOfBounds} if the index is out of bounds.
* @param deque The queue.
* @param index The index of the item to return.
* @return value_ The item at the given index.
* @return rawIndex_ The raw index of the item.
*/
function at(Deque storage deque, uint256 index)
external
view
returns (Types.PendingAction memory value_, uint128 rawIndex_)
{
if (index >= length(deque)) {
revert QueueOutOfBounds();
}
// by construction, length is a uint128, so the check above ensures that
// the index can be safely downcast to a uint128
unchecked {
rawIndex_ = deque._begin + uint128(index);
value_ = deque._data[rawIndex_];
}
}
/**
* @dev Returns the item at a position in the queue given by `rawIndex`, indexing into the underlying storage array
* directly.
* Reverts with {QueueOutOfBounds} if the index is out of bounds.
* @param deque The queue.
* @param rawIndex The index of the item to return.
* @return value_ The item at the given index.
*/
function atRaw(Deque storage deque, uint128 rawIndex) external view returns (Types.PendingAction memory value_) {
if (!isValid(deque, rawIndex)) {
revert QueueOutOfBounds();
}
value_ = deque._data[rawIndex];
}
/**
* @dev Deletes the item at a position in the queue given by `rawIndex`, indexing into the underlying storage array
* directly. If clearing the front or back item, then the bounds are updated. Otherwise, the values are simply set
* to zero and the queue's begin and end indices are not updated.
* @param deque The queue.
* @param rawIndex The index of the item to delete.
*/
function clearAt(Deque storage deque, uint128 rawIndex) external {
uint128 backIndex = deque._end;
unchecked {
backIndex--;
}
if (rawIndex == deque._begin) {
popFront(deque); // reverts if empty
} else if (rawIndex == backIndex) {
popBack(deque); // reverts if empty
} else {
// we don't care to revert if this is not a valid index, since we're just clearing it
delete deque._data[rawIndex];
}
}
/**
* @dev Checks if the raw index is valid (in bounds).
* @param deque The queue.
* @param rawIndex The raw index to check.
* @return valid_ Whether the raw index is valid.
*/
function isValid(Deque storage deque, uint128 rawIndex) public view returns (bool valid_) {
if (deque._begin > deque._end) {
// here the values are split at the beginning and end of the range, so invalid indices are in the middle
if (rawIndex < deque._begin && rawIndex >= deque._end) {
return false;
}
} else if (rawIndex < deque._begin || rawIndex >= deque._end) {
return false;
}
valid_ = true;
}
/**
* @dev Returns the number of items in the queue.
* @param deque The queue.
* @return length_ The number of items in the queue.
*/
function length(Deque storage deque) public view returns (uint256 length_) {
unchecked {
length_ = uint256(deque._end - deque._begin);
}
}
/**
* @dev Returns true if the queue is empty.
* @param deque The queue.
* @return empty_ True if the queue is empty.
*/
function empty(Deque storage deque) internal view returns (bool empty_) {
empty_ = deque._end == deque._begin;
}
}// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
import { IUsdnProtocolTypes as Types } from "../UsdnProtocol/IUsdnProtocolTypes.sol";
/**
* @title IBaseLiquidationRewardsManager
* @notice This interface exposes the only function used by the UsdnProtocol.
* @dev Future implementations of the rewards manager must implement this interface without modifications.
*/
interface IBaseLiquidationRewardsManager {
/**
* @notice Computes the amount of assets to reward a liquidator.
* @param liquidatedTicks Information about the liquidated ticks.
* @param currentPrice The current price of the asset.
* @param rebased Indicates whether a USDN rebase was performed.
* @param rebalancerAction The action performed by the {UsdnProtocolLongLibrary._triggerRebalancer} function.
* @param action The type of protocol action that triggered the liquidation.
* @param rebaseCallbackResult The result of the rebase callback, if any.
* @param priceData The oracle price data, if any. This can be used to differentiate rewards based on the oracle
* used to provide the liquidation price.
* @return assetRewards_ The amount of asset tokens to reward the liquidator.
*/
function getLiquidationRewards(
Types.LiqTickInfo[] calldata liquidatedTicks,
uint256 currentPrice,
bool rebased,
Types.RebalancerAction rebalancerAction,
Types.ProtocolAction action,
bytes calldata rebaseCallbackResult,
bytes calldata priceData
) external view returns (uint256 assetRewards_);
}// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
import { IUsdnProtocolTypes as Types } from "../UsdnProtocol/IUsdnProtocolTypes.sol";
import { PriceInfo } from "./IOracleMiddlewareTypes.sol";
/**
* @title Base Oracle Middleware interface
* @notice This interface exposes the only functions used or required by the USDN Protocol.
* @dev Any current or future implementation of the oracle middleware must be compatible with
* this interface without any modification.
*/
interface IBaseOracleMiddleware {
/**
* @notice Parse and validate `data` and returns the corresponding price data.
* @dev The data format is specific to the middleware and is simply forwarded from the user transaction's calldata.
* A fee amounting to exactly {validationCost} (with the same `data` and `action`) must be sent or the transaction
* will revert.
* @param actionId A unique identifier for the current action. This identifier can be used to link an `Initiate`
* call with the corresponding `Validate` call.
* @param targetTimestamp The target timestamp for validating the price data. For validation actions, this is the
* timestamp of the initiation.
* @param action Type of action for which the price is requested. The middleware may use this to alter the
* validation of the price or the returned price.
* @param data The data to be used to communicate with oracles, the format varies from middleware to middleware and
* can be different depending on the action.
* @return result_ The price and timestamp as {IOracleMiddlewareTypes.PriceInfo}.
*/
function parseAndValidatePrice(
bytes32 actionId,
uint128 targetTimestamp,
Types.ProtocolAction action,
bytes calldata data
) external payable returns (PriceInfo memory result_);
/**
* @notice Gets the required delay (in seconds) between the moment an action is initiated and the timestamp of the
* price data used to validate that action.
* @return delay_ The validation delay.
*/
function getValidationDelay() external view returns (uint256 delay_);
/**
* @notice Gets The maximum amount of time (in seconds) after initiation during which a low-latency price oracle can
* be used for validation.
* @return delay_ The maximum delay for low-latency validation.
*/
function getLowLatencyDelay() external view returns (uint16 delay_);
/**
* @notice Gets the number of decimals for the price.
* @return decimals_ The number of decimals.
*/
function getDecimals() external view returns (uint8 decimals_);
/**
* @notice Returns the cost of one price validation for the given action (in native token).
* @param data Price data for which to get the fee.
* @param action Type of the action for which the price is requested.
* @return cost_ The cost of one price validation (in native token).
*/
function validationCost(bytes calldata data, Types.ProtocolAction action) external view returns (uint256 cost_);
}// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
import { IUsdnProtocolTypes as Types } from "../UsdnProtocol/IUsdnProtocolTypes.sol";
import { IRebalancerTypes } from "./IRebalancerTypes.sol";
interface IBaseRebalancer {
/**
* @notice Returns the necessary data for the USDN protocol to update the position.
* @return pendingAssets_ The amount of assets that are pending inclusion in the protocol.
* @return maxLeverage_ The maximum leverage of the rebalancer.
* @return currentPosId_ The ID of the current position (`tick` == `NO_POSITION_TICK` if no position).
*/
function getCurrentStateData()
external
view
returns (uint128 pendingAssets_, uint256 maxLeverage_, Types.PositionId memory currentPosId_);
/**
* @notice Returns the minimum amount of assets a user can deposit in the rebalancer.
* @return minAssetDeposit_ The minimum amount of assets that can be deposited by a user.
*/
function getMinAssetDeposit() external view returns (uint256 minAssetDeposit_);
/**
* @notice Returns the data regarding the assets deposited by the provided user.
* @param user The address of the user.
* @return data_ The data regarding the assets deposited by the provided user.
*/
function getUserDepositData(address user) external view returns (IRebalancerTypes.UserDeposit memory data_);
/**
* @notice Indicates that the previous version of the position was closed and a new one was opened.
* @dev If `previousPosValue` equals 0, it means the previous version got liquidated.
* @param newPosId The position ID of the new position.
* @param previousPosValue The amount of assets left in the previous position.
*/
function updatePosition(Types.PositionId calldata newPosId, uint128 previousPosValue) external;
/* -------------------------------------------------------------------------- */
/* Admin */
/* -------------------------------------------------------------------------- */
/**
* @notice Sets the minimum amount of assets to be deposited by a user.
* @dev The new minimum amount must be greater than or equal to the minimum long position of the USDN protocol.
* This function can only be called by the owner or the USDN protocol.
* @param minAssetDeposit The new minimum amount of assets to be deposited.
*/
function setMinAssetDeposit(uint256 minAssetDeposit) external;
}// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { IERC20Metadata } from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import { IERC20Permit } from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Permit.sol";
import { IRebaseCallback } from "./IRebaseCallback.sol";
import { IUsdnErrors } from "./IUsdnErrors.sol";
import { IUsdnEvents } from "./IUsdnEvents.sol";
/**
* @title USDN token interface
* @notice Implements the ERC-20 token standard as well as the EIP-2612 permit extension. Additional functions related
* to the specifics of this token are included below.
*/
interface IUsdn is IERC20, IERC20Metadata, IERC20Permit, IUsdnEvents, IUsdnErrors {
/**
* @notice Returns the total number of shares in existence.
* @return shares_ The number of shares.
*/
function totalShares() external view returns (uint256 shares_);
/**
* @notice Returns the number of shares owned by `account`.
* @param account The account to query.
* @return shares_ The number of shares.
*/
function sharesOf(address account) external view returns (uint256 shares_);
/**
* @notice Transfers a given amount of shares from the `msg.sender` to `to`.
* @param to Recipient of the shares.
* @param value Number of shares to transfer.
* @return success_ Indicates whether the transfer was successfully executed.
*/
function transferShares(address to, uint256 value) external returns (bool success_);
/**
* @notice Transfers a given amount of shares from the `from` to `to`.
* @dev There should be sufficient allowance for the spender. Be mindful of the rebase logic. The allowance is in
* tokens. So, after a rebase, the same amount of shares will be worth a higher amount of tokens. In that case,
* the allowance of the initial approval will not be enough to transfer the new amount of tokens. This can
* also happen when your transaction is in the mempool and the rebase happens before your transaction. Also note
* that the amount of tokens deduced from the allowance is rounded up, so the `convertToTokensRoundUp` function
* should be used when converting shares into an allowance value.
* @param from The owner of the shares.
* @param to Recipient of the shares.
* @param value Number of shares to transfer.
* @return success_ Indicates whether the transfer was successfully executed.
*/
function transferSharesFrom(address from, address to, uint256 value) external returns (bool success_);
/**
* @notice Mints new shares, providing a token value.
* @dev Caller must have the MINTER_ROLE.
* @param to Account to receive the new shares.
* @param amount Amount of tokens to mint, is internally converted to the proper shares amounts.
*/
function mint(address to, uint256 amount) external;
/**
* @notice Mints new shares, providing a share value.
* @dev Caller must have the MINTER_ROLE.
* @param to Account to receive the new shares.
* @param amount Amount of shares to mint.
* @return mintedTokens_ Amount of tokens that were minted (informational).
*/
function mintShares(address to, uint256 amount) external returns (uint256 mintedTokens_);
/**
* @notice Destroys a `value` amount of tokens from the caller, reducing the total supply.
* @param value Amount of tokens to burn, is internally converted to the proper shares amounts.
*/
function burn(uint256 value) external;
/**
* @notice Destroys a `value` amount of tokens from `account`, deducting from the caller's allowance.
* @param account Account to burn tokens from.
* @param value Amount of tokens to burn, is internally converted to the proper shares amounts.
*/
function burnFrom(address account, uint256 value) external;
/**
* @notice Destroys a `value` amount of shares from the caller, reducing the total supply.
* @param value Amount of shares to burn.
*/
function burnShares(uint256 value) external;
/**
* @notice Destroys a `value` amount of shares from `account`, deducting from the caller's allowance.
* @dev There should be sufficient allowance for the spender. Be mindful of the rebase logic. The allowance is in
* tokens. So, after a rebase, the same amount of shares will be worth a higher amount of tokens. In that case,
* the allowance of the initial approval will not be enough to transfer the new amount of tokens. This can
* also happen when your transaction is in the mempool and the rebase happens before your transaction. Also note
* that the amount of tokens deduced from the allowance is rounded up, so the `convertToTokensRoundUp` function
* should be used when converting shares into an allowance value.
* @param account Account to burn shares from.
* @param value Amount of shares to burn.
*/
function burnSharesFrom(address account, uint256 value) external;
/**
* @notice Converts a number of tokens to the corresponding amount of shares.
* @dev The conversion reverts with `UsdnMaxTokensExceeded` if the corresponding amount of shares overflows.
* @param amountTokens The amount of tokens to convert to shares.
* @return shares_ The corresponding amount of shares.
*/
function convertToShares(uint256 amountTokens) external view returns (uint256 shares_);
/**
* @notice Converts a number of shares to the corresponding amount of tokens.
* @dev The conversion never overflows as we are performing a division. The conversion rounds to the nearest amount
* of tokens that minimizes the error when converting back to shares.
* @param amountShares The amount of shares to convert to tokens.
* @return tokens_ The corresponding amount of tokens.
*/
function convertToTokens(uint256 amountShares) external view returns (uint256 tokens_);
/**
* @notice Converts a number of shares to the corresponding amount of tokens, rounding up.
* @dev Use this function to determine the amount of a token approval, as we always round up when deducting from
* a token transfer allowance.
* @param amountShares The amount of shares to convert to tokens.
* @return tokens_ The corresponding amount of tokens, rounded up.
*/
function convertToTokensRoundUp(uint256 amountShares) external view returns (uint256 tokens_);
/**
* @notice Returns the current maximum tokens supply, given the current divisor.
* @dev This function is used to check if a conversion operation would overflow.
* @return maxTokens_ The maximum number of tokens that can exist.
*/
function maxTokens() external view returns (uint256 maxTokens_);
/**
* @notice Decreases the global divisor, which effectively grows all balances and the total supply.
* @dev If the provided divisor is larger than or equal to the current divisor value, no rebase will happen
* If the new divisor is smaller than `MIN_DIVISOR`, the value will be clamped to `MIN_DIVISOR`.
* Caller must have the `REBASER_ROLE`.
* @param newDivisor The new divisor, should be strictly smaller than the current one and greater or equal to
* `MIN_DIVISOR`.
* @return rebased_ Whether a rebase happened.
* @return oldDivisor_ The previous value of the divisor.
* @return callbackResult_ The result of the callback, if a rebase happened and a callback handler is defined.
*/
function rebase(uint256 newDivisor)
external
returns (bool rebased_, uint256 oldDivisor_, bytes memory callbackResult_);
/**
* @notice Sets the rebase handler address.
* @dev Emits a `RebaseHandlerUpdated` event.
* If set to the zero address, no handler will be called after a rebase.
* Caller must have the `DEFAULT_ADMIN_ROLE`.
* @param newHandler The new handler address.
*/
function setRebaseHandler(IRebaseCallback newHandler) external;
/* -------------------------------------------------------------------------- */
/* Dev view functions */
/* -------------------------------------------------------------------------- */
/**
* @notice Gets the current value of the divisor that converts between tokens and shares.
* @return divisor_ The current divisor.
*/
function divisor() external view returns (uint256 divisor_);
/**
* @notice Gets the rebase handler address, which is called whenever a rebase happens.
* @return rebaseHandler_ The rebase handler address.
*/
function rebaseHandler() external view returns (IRebaseCallback rebaseHandler_);
/**
* @notice Gets the minter role signature.
* @return minter_role_ The role signature.
*/
function MINTER_ROLE() external pure returns (bytes32 minter_role_);
/**
* @notice Gets the rebaser role signature.
* @return rebaser_role_ The role signature.
*/
function REBASER_ROLE() external pure returns (bytes32 rebaser_role_);
/**
* @notice Gets the maximum value of the divisor, which is also the initial value.
* @return maxDivisor_ The maximum divisor.
*/
function MAX_DIVISOR() external pure returns (uint256 maxDivisor_);
/**
* @notice Gets the minimum acceptable value of the divisor.
* @dev The minimum divisor that can be set. This corresponds to a growth of 1B times. Technically, 1e5 would still
* work without precision errors.
* @return minDivisor_ The minimum divisor.
*/
function MIN_DIVISOR() external pure returns (uint256 minDivisor_);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/extensions/AccessControlDefaultAdminRules.sol)
pragma solidity ^0.8.20;
import {IAccessControlDefaultAdminRules} from "./IAccessControlDefaultAdminRules.sol";
import {AccessControl, IAccessControl} from "../AccessControl.sol";
import {SafeCast} from "../../utils/math/SafeCast.sol";
import {Math} from "../../utils/math/Math.sol";
import {IERC5313} from "../../interfaces/IERC5313.sol";
/**
* @dev Extension of {AccessControl} that allows specifying special rules to manage
* the `DEFAULT_ADMIN_ROLE` holder, which is a sensitive role with special permissions
* over other roles that may potentially have privileged rights in the system.
*
* If a specific role doesn't have an admin role assigned, the holder of the
* `DEFAULT_ADMIN_ROLE` will have the ability to grant it and revoke it.
*
* This contract implements the following risk mitigations on top of {AccessControl}:
*
* * Only one account holds the `DEFAULT_ADMIN_ROLE` since deployment until it's potentially renounced.
* * Enforces a 2-step process to transfer the `DEFAULT_ADMIN_ROLE` to another account.
* * Enforces a configurable delay between the two steps, with the ability to cancel before the transfer is accepted.
* * The delay can be changed by scheduling, see {changeDefaultAdminDelay}.
* * It is not possible to use another role to manage the `DEFAULT_ADMIN_ROLE`.
*
* Example usage:
*
* ```solidity
* contract MyToken is AccessControlDefaultAdminRules {
* constructor() AccessControlDefaultAdminRules(
* 3 days,
* msg.sender // Explicit initial `DEFAULT_ADMIN_ROLE` holder
* ) {}
* }
* ```
*/
abstract contract AccessControlDefaultAdminRules is IAccessControlDefaultAdminRules, IERC5313, AccessControl {
// pending admin pair read/written together frequently
address private _pendingDefaultAdmin;
uint48 private _pendingDefaultAdminSchedule; // 0 == unset
uint48 private _currentDelay;
address private _currentDefaultAdmin;
// pending delay pair read/written together frequently
uint48 private _pendingDelay;
uint48 private _pendingDelaySchedule; // 0 == unset
/**
* @dev Sets the initial values for {defaultAdminDelay} and {defaultAdmin} address.
*/
constructor(uint48 initialDelay, address initialDefaultAdmin) {
if (initialDefaultAdmin == address(0)) {
revert AccessControlInvalidDefaultAdmin(address(0));
}
_currentDelay = initialDelay;
_grantRole(DEFAULT_ADMIN_ROLE, initialDefaultAdmin);
}
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
return interfaceId == type(IAccessControlDefaultAdminRules).interfaceId || super.supportsInterface(interfaceId);
}
/**
* @dev See {IERC5313-owner}.
*/
function owner() public view virtual returns (address) {
return defaultAdmin();
}
///
/// Override AccessControl role management
///
/**
* @dev See {AccessControl-grantRole}. Reverts for `DEFAULT_ADMIN_ROLE`.
*/
function grantRole(bytes32 role, address account) public virtual override(AccessControl, IAccessControl) {
if (role == DEFAULT_ADMIN_ROLE) {
revert AccessControlEnforcedDefaultAdminRules();
}
super.grantRole(role, account);
}
/**
* @dev See {AccessControl-revokeRole}. Reverts for `DEFAULT_ADMIN_ROLE`.
*/
function revokeRole(bytes32 role, address account) public virtual override(AccessControl, IAccessControl) {
if (role == DEFAULT_ADMIN_ROLE) {
revert AccessControlEnforcedDefaultAdminRules();
}
super.revokeRole(role, account);
}
/**
* @dev See {AccessControl-renounceRole}.
*
* For the `DEFAULT_ADMIN_ROLE`, it only allows renouncing in two steps by first calling
* {beginDefaultAdminTransfer} to the `address(0)`, so it's required that the {pendingDefaultAdmin} schedule
* has also passed when calling this function.
*
* After its execution, it will not be possible to call `onlyRole(DEFAULT_ADMIN_ROLE)` functions.
*
* NOTE: Renouncing `DEFAULT_ADMIN_ROLE` will leave the contract without a {defaultAdmin},
* thereby disabling any functionality that is only available for it, and the possibility of reassigning a
* non-administrated role.
*/
function renounceRole(bytes32 role, address account) public virtual override(AccessControl, IAccessControl) {
if (role == DEFAULT_ADMIN_ROLE && account == defaultAdmin()) {
(address newDefaultAdmin, uint48 schedule) = pendingDefaultAdmin();
if (newDefaultAdmin != address(0) || !_isScheduleSet(schedule) || !_hasSchedulePassed(schedule)) {
revert AccessControlEnforcedDefaultAdminDelay(schedule);
}
delete _pendingDefaultAdminSchedule;
}
super.renounceRole(role, account);
}
/**
* @dev See {AccessControl-_grantRole}.
*
* For `DEFAULT_ADMIN_ROLE`, it only allows granting if there isn't already a {defaultAdmin} or if the
* role has been previously renounced.
*
* NOTE: Exposing this function through another mechanism may make the `DEFAULT_ADMIN_ROLE`
* assignable again. Make sure to guarantee this is the expected behavior in your implementation.
*/
function _grantRole(bytes32 role, address account) internal virtual override returns (bool) {
if (role == DEFAULT_ADMIN_ROLE) {
if (defaultAdmin() != address(0)) {
revert AccessControlEnforcedDefaultAdminRules();
}
_currentDefaultAdmin = account;
}
return super._grantRole(role, account);
}
/**
* @dev See {AccessControl-_revokeRole}.
*/
function _revokeRole(bytes32 role, address account) internal virtual override returns (bool) {
if (role == DEFAULT_ADMIN_ROLE && account == defaultAdmin()) {
delete _currentDefaultAdmin;
}
return super._revokeRole(role, account);
}
/**
* @dev See {AccessControl-_setRoleAdmin}. Reverts for `DEFAULT_ADMIN_ROLE`.
*/
function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual override {
if (role == DEFAULT_ADMIN_ROLE) {
revert AccessControlEnforcedDefaultAdminRules();
}
super._setRoleAdmin(role, adminRole);
}
///
/// AccessControlDefaultAdminRules accessors
///
/**
* @inheritdoc IAccessControlDefaultAdminRules
*/
function defaultAdmin() public view virtual returns (address) {
return _currentDefaultAdmin;
}
/**
* @inheritdoc IAccessControlDefaultAdminRules
*/
function pendingDefaultAdmin() public view virtual returns (address newAdmin, uint48 schedule) {
return (_pendingDefaultAdmin, _pendingDefaultAdminSchedule);
}
/**
* @inheritdoc IAccessControlDefaultAdminRules
*/
function defaultAdminDelay() public view virtual returns (uint48) {
uint48 schedule = _pendingDelaySchedule;
return (_isScheduleSet(schedule) && _hasSchedulePassed(schedule)) ? _pendingDelay : _currentDelay;
}
/**
* @inheritdoc IAccessControlDefaultAdminRules
*/
function pendingDefaultAdminDelay() public view virtual returns (uint48 newDelay, uint48 schedule) {
schedule = _pendingDelaySchedule;
return (_isScheduleSet(schedule) && !_hasSchedulePassed(schedule)) ? (_pendingDelay, schedule) : (0, 0);
}
/**
* @inheritdoc IAccessControlDefaultAdminRules
*/
function defaultAdminDelayIncreaseWait() public view virtual returns (uint48) {
return 5 days;
}
///
/// AccessControlDefaultAdminRules public and internal setters for defaultAdmin/pendingDefaultAdmin
///
/**
* @inheritdoc IAccessControlDefaultAdminRules
*/
function beginDefaultAdminTransfer(address newAdmin) public virtual onlyRole(DEFAULT_ADMIN_ROLE) {
_beginDefaultAdminTransfer(newAdmin);
}
/**
* @dev See {beginDefaultAdminTransfer}.
*
* Internal function without access restriction.
*/
function _beginDefaultAdminTransfer(address newAdmin) internal virtual {
uint48 newSchedule = SafeCast.toUint48(block.timestamp) + defaultAdminDelay();
_setPendingDefaultAdmin(newAdmin, newSchedule);
emit DefaultAdminTransferScheduled(newAdmin, newSchedule);
}
/**
* @inheritdoc IAccessControlDefaultAdminRules
*/
function cancelDefaultAdminTransfer() public virtual onlyRole(DEFAULT_ADMIN_ROLE) {
_cancelDefaultAdminTransfer();
}
/**
* @dev See {cancelDefaultAdminTransfer}.
*
* Internal function without access restriction.
*/
function _cancelDefaultAdminTransfer() internal virtual {
_setPendingDefaultAdmin(address(0), 0);
}
/**
* @inheritdoc IAccessControlDefaultAdminRules
*/
function acceptDefaultAdminTransfer() public virtual {
(address newDefaultAdmin, ) = pendingDefaultAdmin();
if (_msgSender() != newDefaultAdmin) {
// Enforce newDefaultAdmin explicit acceptance.
revert AccessControlInvalidDefaultAdmin(_msgSender());
}
_acceptDefaultAdminTransfer();
}
/**
* @dev See {acceptDefaultAdminTransfer}.
*
* Internal function without access restriction.
*/
function _acceptDefaultAdminTransfer() internal virtual {
(address newAdmin, uint48 schedule) = pendingDefaultAdmin();
if (!_isScheduleSet(schedule) || !_hasSchedulePassed(schedule)) {
revert AccessControlEnforcedDefaultAdminDelay(schedule);
}
_revokeRole(DEFAULT_ADMIN_ROLE, defaultAdmin());
_grantRole(DEFAULT_ADMIN_ROLE, newAdmin);
delete _pendingDefaultAdmin;
delete _pendingDefaultAdminSchedule;
}
///
/// AccessControlDefaultAdminRules public and internal setters for defaultAdminDelay/pendingDefaultAdminDelay
///
/**
* @inheritdoc IAccessControlDefaultAdminRules
*/
function changeDefaultAdminDelay(uint48 newDelay) public virtual onlyRole(DEFAULT_ADMIN_ROLE) {
_changeDefaultAdminDelay(newDelay);
}
/**
* @dev See {changeDefaultAdminDelay}.
*
* Internal function without access restriction.
*/
function _changeDefaultAdminDelay(uint48 newDelay) internal virtual {
uint48 newSchedule = SafeCast.toUint48(block.timestamp) + _delayChangeWait(newDelay);
_setPendingDelay(newDelay, newSchedule);
emit DefaultAdminDelayChangeScheduled(newDelay, newSchedule);
}
/**
* @inheritdoc IAccessControlDefaultAdminRules
*/
function rollbackDefaultAdminDelay() public virtual onlyRole(DEFAULT_ADMIN_ROLE) {
_rollbackDefaultAdminDelay();
}
/**
* @dev See {rollbackDefaultAdminDelay}.
*
* Internal function without access restriction.
*/
function _rollbackDefaultAdminDelay() internal virtual {
_setPendingDelay(0, 0);
}
/**
* @dev Returns the amount of seconds to wait after the `newDelay` will
* become the new {defaultAdminDelay}.
*
* The value returned guarantees that if the delay is reduced, it will go into effect
* after a wait that honors the previously set delay.
*
* See {defaultAdminDelayIncreaseWait}.
*/
function _delayChangeWait(uint48 newDelay) internal view virtual returns (uint48) {
uint48 currentDelay = defaultAdminDelay();
// When increasing the delay, we schedule the delay change to occur after a period of "new delay" has passed, up
// to a maximum given by defaultAdminDelayIncreaseWait, by default 5 days. For example, if increasing from 1 day
// to 3 days, the new delay will come into effect after 3 days. If increasing from 1 day to 10 days, the new
// delay will come into effect after 5 days. The 5 day wait period is intended to be able to fix an error like
// using milliseconds instead of seconds.
//
// When decreasing the delay, we wait the difference between "current delay" and "new delay". This guarantees
// that an admin transfer cannot be made faster than "current delay" at the time the delay change is scheduled.
// For example, if decreasing from 10 days to 3 days, the new delay will come into effect after 7 days.
return
newDelay > currentDelay
? uint48(Math.min(newDelay, defaultAdminDelayIncreaseWait())) // no need to safecast, both inputs are uint48
: currentDelay - newDelay;
}
///
/// Private setters
///
/**
* @dev Setter of the tuple for pending admin and its schedule.
*
* May emit a DefaultAdminTransferCanceled event.
*/
function _setPendingDefaultAdmin(address newAdmin, uint48 newSchedule) private {
(, uint48 oldSchedule) = pendingDefaultAdmin();
_pendingDefaultAdmin = newAdmin;
_pendingDefaultAdminSchedule = newSchedule;
// An `oldSchedule` from `pendingDefaultAdmin()` is only set if it hasn't been accepted.
if (_isScheduleSet(oldSchedule)) {
// Emit for implicit cancellations when another default admin was scheduled.
emit DefaultAdminTransferCanceled();
}
}
/**
* @dev Setter of the tuple for pending delay and its schedule.
*
* May emit a DefaultAdminDelayChangeCanceled event.
*/
function _setPendingDelay(uint48 newDelay, uint48 newSchedule) private {
uint48 oldSchedule = _pendingDelaySchedule;
if (_isScheduleSet(oldSchedule)) {
if (_hasSchedulePassed(oldSchedule)) {
// Materialize a virtual delay
_currentDelay = _pendingDelay;
} else {
// Emit for implicit cancellations when another delay was scheduled.
emit DefaultAdminDelayChangeCanceled();
}
}
_pendingDelay = newDelay;
_pendingDelaySchedule = newSchedule;
}
///
/// Private helpers
///
/**
* @dev Defines if an `schedule` is considered set. For consistency purposes.
*/
function _isScheduleSet(uint48 schedule) private pure returns (bool) {
return schedule != 0;
}
/**
* @dev Defines if an `schedule` is considered passed. For consistency purposes.
*/
function _hasSchedulePassed(uint48 schedule) private view returns (bool) {
return schedule < block.timestamp;
}
}// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
import { IUsdnProtocol } from "../UsdnProtocol/IUsdnProtocol.sol";
import { IBaseOracleMiddleware } from "./IBaseOracleMiddleware.sol";
import { IChainlinkOracle } from "./IChainlinkOracle.sol";
import { IOracleMiddlewareErrors } from "./IOracleMiddlewareErrors.sol";
import { IOracleMiddlewareEvents } from "./IOracleMiddlewareEvents.sol";
import { IPythOracle } from "./IPythOracle.sol";
/**
* @notice The oracle middleware is a contract that is used by the USDN protocol to validate price data.
* Using a middleware allows the protocol to later upgrade to a new oracle logic without having to modify
* the protocol's contracts.
*/
interface IOracleMiddleware is
IChainlinkOracle,
IPythOracle,
IBaseOracleMiddleware,
IOracleMiddlewareErrors,
IOracleMiddlewareEvents
{
/* -------------------------------------------------------------------------- */
/* Roles */
/* -------------------------------------------------------------------------- */
/**
* @notice Gets the admin role's signature.
* @return role_ Get the role signature.
*/
function ADMIN_ROLE() external pure returns (bytes32 role_);
/* -------------------------------------------------------------------------- */
/* Constants */
/* -------------------------------------------------------------------------- */
/**
* @notice Gets the denominator for the variables using basis points as a unit.
* @return denominator_ The BPS divisor.
*/
function BPS_DIVISOR() external pure returns (uint16 denominator_);
/**
* @notice Gets the maximum value for `_confRatioBps`.
* @return ratio_ The max allowed confidence ratio.
*/
function MAX_CONF_RATIO() external pure returns (uint16 ratio_);
/* -------------------------------------------------------------------------- */
/* Generic features */
/* -------------------------------------------------------------------------- */
/**
* @notice Gets the confidence ratio.
* @dev This ratio is used to apply a specific portion of the confidence interval provided by an oracle, which is
* used to adjust the precision of predictions or estimations.
* @return ratio_ The confidence ratio (in basis points).
*/
function getConfRatioBps() external view returns (uint16 ratio_);
/* -------------------------------------------------------------------------- */
/* Owner features */
/* -------------------------------------------------------------------------- */
/**
* @notice Sets the confidence ratio.
* @dev The new value should be lower than {MAX_CONF_RATIO}.
* @param newConfRatio the new confidence ratio.
*/
function setConfRatio(uint16 newConfRatio) external;
/**
* @notice Sets the elapsed time tolerated before we consider the price from Chainlink invalid.
* @param newTimeElapsedLimit The new time elapsed limit.
*/
function setChainlinkTimeElapsedLimit(uint256 newTimeElapsedLimit) external;
/**
* @notice Sets the amount of time after which we do not consider a price as recent.
* @param newDelay The maximum age of a price to be considered recent.
*/
function setPythRecentPriceDelay(uint64 newDelay) external;
/**
* @notice Sets the validation delay (in seconds) between an action timestamp and the price
* data timestamp used to validate that action.
* @param newValidationDelay The new validation delay.
*/
function setValidationDelay(uint256 newValidationDelay) external;
/**
* @notice Sets the new low latency delay.
* @param newLowLatencyDelay The new low latency delay.
* @param usdnProtocol The address of the USDN protocol.
*/
function setLowLatencyDelay(uint16 newLowLatencyDelay, IUsdnProtocol usdnProtocol) external;
/**
* @notice Withdraws the ether balance of this contract.
* @dev This contract can receive funds but is not designed to hold them.
* So this function can be used if there's an error and funds remain after a call.
* @param to The address to send the ether to.
*/
function withdrawEther(address to) external;
}// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
import { IUsdnProtocolFallback } from "./IUsdnProtocolFallback.sol";
import { IUsdnProtocolImpl } from "./IUsdnProtocolImpl.sol";
/**
* @title IUsdnProtocol
* @notice Interface for the USDN protocol and fallback.
*/
interface IUsdnProtocol is IUsdnProtocolImpl, IUsdnProtocolFallback {
/**
* @notice Upgrades the protocol to a new implementation (check
* [UUPSUpgradeable](https://docs.openzeppelin.com/contracts/5.x/api/proxy#UUPSUpgradeable)).
* @dev This function should be called by the role with the PROXY_UPGRADE_ROLE.
* @param newImplementation The address of the new implementation.
* @param data The data to call when upgrading to the new implementation. Passing in empty data skips the
* delegatecall to `newImplementation`.
*/
function upgradeToAndCall(address newImplementation, bytes calldata data) external payable;
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.26;
import { AggregatorV3Interface } from "@chainlink/contracts/src/v0.8/shared/interfaces/AggregatorV3Interface.sol";
import { IChainlinkOracle } from "../../interfaces/OracleMiddleware/IChainlinkOracle.sol";
import { IOracleMiddlewareErrors } from "../../interfaces/OracleMiddleware/IOracleMiddlewareErrors.sol";
import { ChainlinkPriceInfo } from "../../interfaces/OracleMiddleware/IOracleMiddlewareTypes.sol";
/**
* @title The Contract Communicating With the Chainlink Oracle Price Feed Contracts.
* @notice This contract is used to get the price of an asset from Chainlink. It is used by the USDN protocol to get the
* price of the USDN underlying asset, and by the LiquidationRewardsManager to get the price of the gas.
*/
abstract contract ChainlinkOracle is IChainlinkOracle, IOracleMiddlewareErrors {
/// @inheritdoc IChainlinkOracle
int256 public constant PRICE_TOO_OLD = type(int256).min;
/// @notice The Chainlink price feed aggregator contract.
AggregatorV3Interface internal immutable _priceFeed;
/// @notice Tolerated elapsed time until we consider the data too old.
// slither-disable-next-line immutable-states
uint256 internal _timeElapsedLimit;
/**
* @param chainlinkPriceFeed Address of the price feed.
* @param timeElapsedLimit Tolerated elapsed time before the data is considered invalid.
*/
constructor(address chainlinkPriceFeed, uint256 timeElapsedLimit) {
_priceFeed = AggregatorV3Interface(chainlinkPriceFeed);
_timeElapsedLimit = timeElapsedLimit;
}
/// @inheritdoc IChainlinkOracle
function getChainlinkDecimals() public view returns (uint256 decimals_) {
return _priceFeed.decimals();
}
/// @inheritdoc IChainlinkOracle
function getPriceFeed() public view returns (AggregatorV3Interface priceFeed_) {
return _priceFeed;
}
/// @inheritdoc IChainlinkOracle
function getChainlinkTimeElapsedLimit() external view returns (uint256 limit_) {
return _timeElapsedLimit;
}
/**
* @notice Gets the latest price of the asset from Chainlink.
* @dev If the price is too old, the returned price will be equal to `PRICE_TOO_OLD`.
* @return price_ The price of the asset.
*/
function _getChainlinkLatestPrice() internal view virtual returns (ChainlinkPriceInfo memory price_) {
(, int256 price,, uint256 timestamp,) = _priceFeed.latestRoundData();
if (timestamp < block.timestamp - _timeElapsedLimit) {
price = PRICE_TOO_OLD;
}
price_ = ChainlinkPriceInfo({ price: price, timestamp: timestamp });
}
/**
* @notice Gets the latest price of the asset from Chainlink, formatted to the specified number of decimals.
* @param middlewareDecimals The number of decimals to format the price to.
* @return formattedPrice_ The formatted price of the asset.
*/
function _getFormattedChainlinkLatestPrice(uint256 middlewareDecimals)
internal
view
returns (ChainlinkPriceInfo memory formattedPrice_)
{
uint8 oracleDecimals = _priceFeed.decimals();
formattedPrice_ = _getChainlinkLatestPrice();
if (formattedPrice_.price == PRICE_TOO_OLD) {
return formattedPrice_;
}
formattedPrice_.price = formattedPrice_.price * int256(10 ** middlewareDecimals) / int256(10 ** oracleDecimals);
}
/**
* @notice Gets the price of the asset at the specified round ID, formatted to the specified number of decimals.
* @param middlewareDecimals The number of decimals to format the price to.
* @param roundId The targeted round ID.
* @return formattedPrice_ The formatted price of the asset.
*/
function _getFormattedChainlinkPrice(uint256 middlewareDecimals, uint80 roundId)
internal
view
returns (ChainlinkPriceInfo memory formattedPrice_)
{
uint8 oracleDecimals = _priceFeed.decimals();
formattedPrice_ = _getChainlinkPrice(roundId);
if (formattedPrice_.price <= 0) {
return formattedPrice_;
}
formattedPrice_.price = formattedPrice_.price * int256(10 ** middlewareDecimals) / int256(10 ** oracleDecimals);
}
/**
* @notice Gets the price of the asset at the specified round ID.
* @param roundId The Chainlink roundId price.
* @return price_ The price of the asset.
*/
function _getChainlinkPrice(uint80 roundId) internal view virtual returns (ChainlinkPriceInfo memory price_) {
(, price_.price,, price_.timestamp,) = _priceFeed.getRoundData(roundId);
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.26;
import { IPyth } from "@pythnetwork/pyth-sdk-solidity/IPyth.sol";
import { PythStructs } from "@pythnetwork/pyth-sdk-solidity/PythStructs.sol";
import { IOracleMiddlewareErrors } from "../../interfaces/OracleMiddleware/IOracleMiddlewareErrors.sol";
import { FormattedPythPrice } from "../../interfaces/OracleMiddleware/IOracleMiddlewareTypes.sol";
import { IPythOracle } from "../../interfaces/OracleMiddleware/IPythOracle.sol";
/**
* @title Contract To Communicate With The Pyth Oracle
* @notice This contract is used to get the price of the asset that corresponds to the stored feed ID.
* @dev Is implemented by the {OracleMiddleware} contract.
*/
abstract contract PythOracle is IPythOracle, IOracleMiddlewareErrors {
/// @notice The ID of the Pyth price feed.
bytes32 internal immutable _pythFeedId;
/// @notice The address of the Pyth contract.
IPyth internal immutable _pyth;
/// @notice The maximum age of a recent price to be considered valid.
uint64 internal _pythRecentPriceDelay = 45 seconds;
/**
* @param pythAddress The address of the Pyth contract.
* @param pythFeedId The ID of the Pyth price feed.
*/
constructor(address pythAddress, bytes32 pythFeedId) {
_pyth = IPyth(pythAddress);
_pythFeedId = pythFeedId;
}
/// @inheritdoc IPythOracle
function getPyth() external view returns (IPyth) {
return _pyth;
}
/// @inheritdoc IPythOracle
function getPythFeedId() external view returns (bytes32) {
return _pythFeedId;
}
/// @inheritdoc IPythOracle
function getPythRecentPriceDelay() external view returns (uint64) {
return _pythRecentPriceDelay;
}
/**
* @notice Gets the price of the asset from the stored Pyth price feed.
* @param priceUpdateData The data required to update the price feed.
* @param targetTimestamp The timestamp of the price in the given `priceUpdateData`.
* If zero, then we accept all recent prices.
* @param targetLimit The most recent timestamp a price can have.
* Can be zero if `targetTimestamp` is zero.
* @return price_ The raw price of the asset returned by Pyth.
*/
function _getPythPrice(bytes calldata priceUpdateData, uint128 targetTimestamp, uint128 targetLimit)
internal
returns (PythStructs.Price memory)
{
// parse the price feed update and get the price feed
bytes32[] memory feedIds = new bytes32[](1);
feedIds[0] = _pythFeedId;
bytes[] memory pricesUpdateData = new bytes[](1);
pricesUpdateData[0] = priceUpdateData;
uint256 pythFee = _pyth.getUpdateFee(pricesUpdateData);
// sanity check on the fee requested by Pyth
if (pythFee > 0.01 ether) {
revert OracleMiddlewarePythFeeSafeguard(pythFee);
}
if (msg.value != pythFee) {
revert OracleMiddlewareIncorrectFee();
}
PythStructs.PriceFeed[] memory priceFeeds;
if (targetTimestamp == 0) {
// we want to validate that the price is recent
// we don't enforce that the price update is the first one in a given second
// slither-disable-next-line arbitrary-send-eth
priceFeeds = _pyth.parsePriceFeedUpdates{ value: pythFee }(
pricesUpdateData, feedIds, uint64(block.timestamp) - _pythRecentPriceDelay, uint64(block.timestamp)
);
} else {
// we want to validate that the price is exactly at `targetTimestamp` (first in the second) or the next
// available price in the future, as identified by the prevPublishTime being strictly less than
// targetTimestamp
// we add a sanity check that this price update cannot be too late (more than `_lowLatencyDelay` seconds
// late) compared to the desired targetTimestamp
priceFeeds = _pyth.parsePriceFeedUpdatesUnique{ value: pythFee }(
pricesUpdateData, feedIds, uint64(targetTimestamp), uint64(targetLimit)
);
}
if (priceFeeds[0].price.price <= 0) {
revert OracleMiddlewareWrongPrice(priceFeeds[0].price.price);
}
return priceFeeds[0].price;
}
/**
* @notice Gets the price of the asset from Pyth, formatted to the specified number of decimals.
* @param priceUpdateData The data required to update the price feed.
* @param targetTimestamp The timestamp of the price in the given `priceUpdateData`.
* If zero, then we accept all recent prices.
* @param middlewareDecimals The number of decimals to format the price to.
* @param targetLimit The most recent timestamp a price can have.
* Can be zero if `targetTimestamp` is zero.
* @return price_ The Pyth price formatted with `middlewareDecimals`.
*/
function _getFormattedPythPrice(
bytes calldata priceUpdateData,
uint128 targetTimestamp,
uint256 middlewareDecimals,
uint128 targetLimit
) internal returns (FormattedPythPrice memory price_) {
// this call checks that the price is strictly positive
PythStructs.Price memory pythPrice = _getPythPrice(priceUpdateData, targetTimestamp, targetLimit);
if (pythPrice.expo > 0) {
revert OracleMiddlewarePythPositiveExponent(pythPrice.expo);
}
price_ = _formatPythPrice(pythPrice, middlewareDecimals);
}
/**
* @notice Formats a Pyth price object to normalize to the specified number of decimals.
* @param pythPrice A Pyth price object.
* @param middlewareDecimals The number of decimals to format the price to.
* @return price_ The Pyth price formatted with `middlewareDecimals`.
*/
function _formatPythPrice(PythStructs.Price memory pythPrice, uint256 middlewareDecimals)
internal
pure
returns (FormattedPythPrice memory price_)
{
uint256 pythDecimals = uint32(-pythPrice.expo);
price_ = FormattedPythPrice({
price: uint256(int256(pythPrice.price)) * 10 ** middlewareDecimals / 10 ** pythDecimals,
conf: uint256(pythPrice.conf) * 10 ** middlewareDecimals / 10 ** pythDecimals,
publishTime: pythPrice.publishTime
});
}
/**
* @notice Gets the fee required to update the price feed.
* @param priceUpdateData The data required to update the price feed.
* @return updateFee_ The fee required to update the price feed.
*/
function _getPythUpdateFee(bytes calldata priceUpdateData) internal view returns (uint256) {
bytes[] memory pricesUpdateData = new bytes[](1);
pricesUpdateData[0] = priceUpdateData;
return _pyth.getUpdateFee(pricesUpdateData);
}
/**
* @notice Gets the latest seen (cached) price from the Pyth contract.
* @param middlewareDecimals The number of decimals for the returned price.
* @return price_ The formatted cached Pyth price, or all-zero values if there was no valid Pyth price on-chain.
*/
function _getLatestStoredPythPrice(uint256 middlewareDecimals)
internal
view
returns (FormattedPythPrice memory price_)
{
// we use getPriceUnsafe to get the latest price without reverting, no matter how old
PythStructs.Price memory pythPrice;
// if the proxy implementation changes, this can revert
try _pyth.getPriceUnsafe(_pythFeedId) returns (PythStructs.Price memory unsafePrice_) {
pythPrice = unsafePrice_;
} catch { }
// negative or zero prices are considered invalid, we return zero
if (pythPrice.price <= 0) {
return price_;
}
if (pythPrice.expo > 0) {
revert OracleMiddlewarePythPositiveExponent(pythPrice.expo);
}
price_ = _formatPythPrice(pythPrice, middlewareDecimals);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.20;
/**
* @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.4;
/// @notice Library for bit twiddling and boolean operations.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/LibBit.sol)
/// @author Inspired by (https://graphics.stanford.edu/~seander/bithacks.html)
library LibBit {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* BIT TWIDDLING OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Find last set.
/// Returns the index of the most significant bit of `x`,
/// counting from the least significant bit position.
/// If `x` is zero, returns 256.
function fls(uint256 x) internal pure returns (uint256 r) {
/// @solidity memory-safe-assembly
assembly {
r := or(shl(8, iszero(x)), shl(7, lt(0xffffffffffffffffffffffffffffffff, x)))
r := or(r, shl(6, lt(0xffffffffffffffff, shr(r, x))))
r := or(r, shl(5, lt(0xffffffff, shr(r, x))))
r := or(r, shl(4, lt(0xffff, shr(r, x))))
r := or(r, shl(3, lt(0xff, shr(r, x))))
// forgefmt: disable-next-item
r := or(r, byte(and(0x1f, shr(shr(r, x), 0x8421084210842108cc6318c6db6d54be)),
0x0706060506020504060203020504030106050205030304010505030400000000))
}
}
/// @dev Count leading zeros.
/// Returns the number of zeros preceding the most significant one bit.
/// If `x` is zero, returns 256.
function clz(uint256 x) internal pure returns (uint256 r) {
/// @solidity memory-safe-assembly
assembly {
r := shl(7, lt(0xffffffffffffffffffffffffffffffff, x))
r := or(r, shl(6, lt(0xffffffffffffffff, shr(r, x))))
r := or(r, shl(5, lt(0xffffffff, shr(r, x))))
r := or(r, shl(4, lt(0xffff, shr(r, x))))
r := or(r, shl(3, lt(0xff, shr(r, x))))
// forgefmt: disable-next-item
r := add(xor(r, byte(and(0x1f, shr(shr(r, x), 0x8421084210842108cc6318c6db6d54be)),
0xf8f9f9faf9fdfafbf9fdfcfdfafbfcfef9fafdfafcfcfbfefafafcfbffffffff)), iszero(x))
}
}
/// @dev Find first set.
/// Returns the index of the least significant bit of `x`,
/// counting from the least significant bit position.
/// If `x` is zero, returns 256.
/// Equivalent to `ctz` (count trailing zeros), which gives
/// the number of zeros following the least significant one bit.
function ffs(uint256 x) internal pure returns (uint256 r) {
/// @solidity memory-safe-assembly
assembly {
// Isolate the least significant bit.
x := and(x, add(not(x), 1))
// For the upper 3 bits of the result, use a De Bruijn-like lookup.
// Credit to adhusson: https://blog.adhusson.com/cheap-find-first-set-evm/
// forgefmt: disable-next-item
r := shl(5, shr(252, shl(shl(2, shr(250, mul(x,
0xb6db6db6ddddddddd34d34d349249249210842108c6318c639ce739cffffffff))),
0x8040405543005266443200005020610674053026020000107506200176117077)))
// For the lower 5 bits of the result, use a De Bruijn lookup.
// forgefmt: disable-next-item
r := or(r, byte(and(div(0xd76453e0, shr(r, x)), 0x1f),
0x001f0d1e100c1d070f090b19131c1706010e11080a1a141802121b1503160405))
}
}
/// @dev Returns the number of set bits in `x`.
function popCount(uint256 x) internal pure returns (uint256 c) {
/// @solidity memory-safe-assembly
assembly {
let max := not(0)
let isMax := eq(x, max)
x := sub(x, and(shr(1, x), div(max, 3)))
x := add(and(x, div(max, 5)), and(shr(2, x), div(max, 5)))
x := and(add(x, shr(4, x)), div(max, 17))
c := or(shl(8, isMax), shr(248, mul(x, div(max, 255))))
}
}
/// @dev Returns whether `x` is a power of 2.
function isPo2(uint256 x) internal pure returns (bool result) {
/// @solidity memory-safe-assembly
assembly {
// Equivalent to `x && !(x & (x - 1))`.
result := iszero(add(and(x, sub(x, 1)), iszero(x)))
}
}
/// @dev Returns `x` reversed at the bit level.
function reverseBits(uint256 x) internal pure returns (uint256 r) {
uint256 m0 = 0x0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f;
uint256 m1 = m0 ^ (m0 << 2);
uint256 m2 = m1 ^ (m1 << 1);
r = reverseBytes(x);
r = (m2 & (r >> 1)) | ((m2 & r) << 1);
r = (m1 & (r >> 2)) | ((m1 & r) << 2);
r = (m0 & (r >> 4)) | ((m0 & r) << 4);
}
/// @dev Returns `x` reversed at the byte level.
function reverseBytes(uint256 x) internal pure returns (uint256 r) {
unchecked {
// Computing masks on-the-fly reduces bytecode size by about 200 bytes.
uint256 m0 = 0x100000000000000000000000000000001 * (~toUint(x == uint256(0)) >> 192);
uint256 m1 = m0 ^ (m0 << 32);
uint256 m2 = m1 ^ (m1 << 16);
uint256 m3 = m2 ^ (m2 << 8);
r = (m3 & (x >> 8)) | ((m3 & x) << 8);
r = (m2 & (r >> 16)) | ((m2 & r) << 16);
r = (m1 & (r >> 32)) | ((m1 & r) << 32);
r = (m0 & (r >> 64)) | ((m0 & r) << 64);
r = (r >> 128) | (r << 128);
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* BOOLEAN OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
// A Solidity bool on the stack or memory is represented as a 256-bit word.
// Non-zero values are true, zero is false.
// A clean bool is either 0 (false) or 1 (true) under the hood.
// Usually, if not always, the bool result of a regular Solidity expression,
// or the argument of a public/external function will be a clean bool.
// You can usually use the raw variants for more performance.
// If uncertain, test (best with exact compiler settings).
// Or use the non-raw variants (compiler can sometimes optimize out the double `iszero`s).
/// @dev Returns `x & y`. Inputs must be clean.
function rawAnd(bool x, bool y) internal pure returns (bool z) {
/// @solidity memory-safe-assembly
assembly {
z := and(x, y)
}
}
/// @dev Returns `x & y`.
function and(bool x, bool y) internal pure returns (bool z) {
/// @solidity memory-safe-assembly
assembly {
z := and(iszero(iszero(x)), iszero(iszero(y)))
}
}
/// @dev Returns `x | y`. Inputs must be clean.
function rawOr(bool x, bool y) internal pure returns (bool z) {
/// @solidity memory-safe-assembly
assembly {
z := or(x, y)
}
}
/// @dev Returns `x | y`.
function or(bool x, bool y) internal pure returns (bool z) {
/// @solidity memory-safe-assembly
assembly {
z := or(iszero(iszero(x)), iszero(iszero(y)))
}
}
/// @dev Returns 1 if `b` is true, else 0. Input must be clean.
function rawToUint(bool b) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
z := b
}
}
/// @dev Returns 1 if `b` is true, else 0.
function toUint(bool b) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
z := iszero(iszero(b))
}
}
}// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
/**
* @title Rebalancer Types
* @notice Defines all custom types used by the Rebalancer contract.
*/
interface IRebalancerTypes {
/**
* @notice Represents the deposit data of a user.
* @dev A value of zero for `initiateTimestamp` indicates that the deposit or withdrawal has been validated.
* @param initiateTimestamp The timestamp when the deposit or withdrawal was initiated.
* @param amount The amount of assets deposited by the user.
* @param entryPositionVersion The version of the position the user entered.
*/
struct UserDeposit {
uint40 initiateTimestamp;
uint88 amount; // maximum 309'485'009 tokens with 18 decimals
uint128 entryPositionVersion;
}
/**
* @notice Represents data for a specific version of a position.
* @dev The difference between `amount` here and the amount saved in the USDN protocol is the liquidation bonus.
* @param amount The amount of assets used as collateral to open the position.
* @param tick The tick of the position.
* @param tickVersion The version of the tick.
* @param index The index of the position in the tick list.
* @param entryAccMultiplier The accumulated PnL multiplier of all positions up to this one.
*/
struct PositionData {
uint128 amount;
int24 tick;
uint256 tickVersion;
uint256 index;
uint256 entryAccMultiplier;
}
/**
* @notice Defines parameters related to the validation process for rebalancer deposits and withdrawals.
* @dev If `validationDeadline` has passed, the user must wait until the cooldown duration has elapsed. Then, for
* deposit actions, the user must retrieve its funds using {IRebalancer.resetDepositAssets}. For withdrawal actions,
* the user can simply initiate a new withdrawal.
* @param validationDelay The minimum duration in seconds between an initiate action and the corresponding validate
* action.
* @param validationDeadline The maximum duration in seconds between an initiate action and the corresponding
* validate action.
* @param actionCooldown The duration in seconds from the initiate action during which the user can't interact with
* the rebalancer if the `validationDeadline` is exceeded.
* @param closeDelay The Duration in seconds from the last rebalancer long position opening during which the user
* can't perform an {IRebalancer.initiateClosePosition}.
*/
struct TimeLimits {
uint64 validationDelay;
uint64 validationDeadline;
uint64 actionCooldown;
uint64 closeDelay;
}
}// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
interface IRebaseCallback {
/**
* @notice Called by the USDN token after a rebase has happened.
* @param oldDivisor The value of the divisor before the rebase.
* @param newDivisor The value of the divisor after the rebase (necessarily smaller than `oldDivisor`).
* @return result_ Arbitrary data that will be forwarded to the caller of `rebase`.
*/
function rebaseCallback(uint256 oldDivisor, uint256 newDivisor) external returns (bytes memory result_);
}// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
/**
* @title Errors for the USDN token contract
* @notice Defines all custom errors emitted by the USDN token contract.
*/
interface IUsdnErrors {
/**
* @dev The amount of tokens exceeds the maximum allowed limit.
* @param value The invalid token value.
*/
error UsdnMaxTokensExceeded(uint256 value);
/**
* @dev The sender's share balance is insufficient.
* @param sender The sender's address.
* @param balance The current share balance of the sender.
* @param needed The required amount of shares for the transfer.
*/
error UsdnInsufficientSharesBalance(address sender, uint256 balance, uint256 needed);
/// @dev The divisor value in storage is invalid (< 1).
error UsdnInvalidDivisor();
}// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
import { IRebaseCallback } from "./IRebaseCallback.sol";
/**
* @title Events for the USDN token contract
* @notice Defines all custom events emitted by the USDN token contract.
*/
interface IUsdnEvents {
/**
* @notice The divisor was updated, emitted during a rebase.
* @param oldDivisor The divisor value before the rebase.
* @param newDivisor The new divisor value.
*/
event Rebase(uint256 oldDivisor, uint256 newDivisor);
/**
* @notice The rebase handler address was updated.
* @dev The rebase handler is a contract that is called when a rebase occurs.
* @param newHandler The address of the new rebase handler contract.
*/
event RebaseHandlerUpdated(IRebaseCallback newHandler);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (access/extensions/IAccessControlDefaultAdminRules.sol)
pragma solidity ^0.8.20;
import {IAccessControl} from "../IAccessControl.sol";
/**
* @dev External interface of AccessControlDefaultAdminRules declared to support ERC-165 detection.
*/
interface IAccessControlDefaultAdminRules is IAccessControl {
/**
* @dev The new default admin is not a valid default admin.
*/
error AccessControlInvalidDefaultAdmin(address defaultAdmin);
/**
* @dev At least one of the following rules was violated:
*
* - The `DEFAULT_ADMIN_ROLE` must only be managed by itself.
* - The `DEFAULT_ADMIN_ROLE` must only be held by one account at the time.
* - Any `DEFAULT_ADMIN_ROLE` transfer must be in two delayed steps.
*/
error AccessControlEnforcedDefaultAdminRules();
/**
* @dev The delay for transferring the default admin delay is enforced and
* the operation must wait until `schedule`.
*
* NOTE: `schedule` can be 0 indicating there's no transfer scheduled.
*/
error AccessControlEnforcedDefaultAdminDelay(uint48 schedule);
/**
* @dev Emitted when a {defaultAdmin} transfer is started, setting `newAdmin` as the next
* address to become the {defaultAdmin} by calling {acceptDefaultAdminTransfer} only after `acceptSchedule`
* passes.
*/
event DefaultAdminTransferScheduled(address indexed newAdmin, uint48 acceptSchedule);
/**
* @dev Emitted when a {pendingDefaultAdmin} is reset if it was never accepted, regardless of its schedule.
*/
event DefaultAdminTransferCanceled();
/**
* @dev Emitted when a {defaultAdminDelay} change is started, setting `newDelay` as the next
* delay to be applied between default admin transfer after `effectSchedule` has passed.
*/
event DefaultAdminDelayChangeScheduled(uint48 newDelay, uint48 effectSchedule);
/**
* @dev Emitted when a {pendingDefaultAdminDelay} is reset if its schedule didn't pass.
*/
event DefaultAdminDelayChangeCanceled();
/**
* @dev Returns the address of the current `DEFAULT_ADMIN_ROLE` holder.
*/
function defaultAdmin() external view returns (address);
/**
* @dev Returns a tuple of a `newAdmin` and an accept schedule.
*
* After the `schedule` passes, the `newAdmin` will be able to accept the {defaultAdmin} role
* by calling {acceptDefaultAdminTransfer}, completing the role transfer.
*
* A zero value only in `acceptSchedule` indicates no pending admin transfer.
*
* NOTE: A zero address `newAdmin` means that {defaultAdmin} is being renounced.
*/
function pendingDefaultAdmin() external view returns (address newAdmin, uint48 acceptSchedule);
/**
* @dev Returns the delay required to schedule the acceptance of a {defaultAdmin} transfer started.
*
* This delay will be added to the current timestamp when calling {beginDefaultAdminTransfer} to set
* the acceptance schedule.
*
* NOTE: If a delay change has been scheduled, it will take effect as soon as the schedule passes, making this
* function returns the new delay. See {changeDefaultAdminDelay}.
*/
function defaultAdminDelay() external view returns (uint48);
/**
* @dev Returns a tuple of `newDelay` and an effect schedule.
*
* After the `schedule` passes, the `newDelay` will get into effect immediately for every
* new {defaultAdmin} transfer started with {beginDefaultAdminTransfer}.
*
* A zero value only in `effectSchedule` indicates no pending delay change.
*
* NOTE: A zero value only for `newDelay` means that the next {defaultAdminDelay}
* will be zero after the effect schedule.
*/
function pendingDefaultAdminDelay() external view returns (uint48 newDelay, uint48 effectSchedule);
/**
* @dev Starts a {defaultAdmin} transfer by setting a {pendingDefaultAdmin} scheduled for acceptance
* after the current timestamp plus a {defaultAdminDelay}.
*
* Requirements:
*
* - Only can be called by the current {defaultAdmin}.
*
* Emits a DefaultAdminRoleChangeStarted event.
*/
function beginDefaultAdminTransfer(address newAdmin) external;
/**
* @dev Cancels a {defaultAdmin} transfer previously started with {beginDefaultAdminTransfer}.
*
* A {pendingDefaultAdmin} not yet accepted can also be cancelled with this function.
*
* Requirements:
*
* - Only can be called by the current {defaultAdmin}.
*
* May emit a DefaultAdminTransferCanceled event.
*/
function cancelDefaultAdminTransfer() external;
/**
* @dev Completes a {defaultAdmin} transfer previously started with {beginDefaultAdminTransfer}.
*
* After calling the function:
*
* - `DEFAULT_ADMIN_ROLE` should be granted to the caller.
* - `DEFAULT_ADMIN_ROLE` should be revoked from the previous holder.
* - {pendingDefaultAdmin} should be reset to zero values.
*
* Requirements:
*
* - Only can be called by the {pendingDefaultAdmin}'s `newAdmin`.
* - The {pendingDefaultAdmin}'s `acceptSchedule` should've passed.
*/
function acceptDefaultAdminTransfer() external;
/**
* @dev Initiates a {defaultAdminDelay} update by setting a {pendingDefaultAdminDelay} scheduled for getting
* into effect after the current timestamp plus a {defaultAdminDelay}.
*
* This function guarantees that any call to {beginDefaultAdminTransfer} done between the timestamp this
* method is called and the {pendingDefaultAdminDelay} effect schedule will use the current {defaultAdminDelay}
* set before calling.
*
* The {pendingDefaultAdminDelay}'s effect schedule is defined in a way that waiting until the schedule and then
* calling {beginDefaultAdminTransfer} with the new delay will take at least the same as another {defaultAdmin}
* complete transfer (including acceptance).
*
* The schedule is designed for two scenarios:
*
* - When the delay is changed for a larger one the schedule is `block.timestamp + newDelay` capped by
* {defaultAdminDelayIncreaseWait}.
* - When the delay is changed for a shorter one, the schedule is `block.timestamp + (current delay - new delay)`.
*
* A {pendingDefaultAdminDelay} that never got into effect will be canceled in favor of a new scheduled change.
*
* Requirements:
*
* - Only can be called by the current {defaultAdmin}.
*
* Emits a DefaultAdminDelayChangeScheduled event and may emit a DefaultAdminDelayChangeCanceled event.
*/
function changeDefaultAdminDelay(uint48 newDelay) external;
/**
* @dev Cancels a scheduled {defaultAdminDelay} change.
*
* Requirements:
*
* - Only can be called by the current {defaultAdmin}.
*
* May emit a DefaultAdminDelayChangeCanceled event.
*/
function rollbackDefaultAdminDelay() external;
/**
* @dev Maximum time in seconds for an increase to {defaultAdminDelay} (that is scheduled using {changeDefaultAdminDelay})
* to take effect. Default to 5 days.
*
* When the {defaultAdminDelay} is scheduled to be increased, it goes into effect after the new delay has passed with
* the purpose of giving enough time for reverting any accidental change (i.e. using milliseconds instead of seconds)
* that may lock the contract. However, to avoid excessive schedules, the wait is capped by this function and it can
* be overrode for a custom {defaultAdminDelay} increase scheduling.
*
* IMPORTANT: Make sure to add a reasonable amount of time while overriding this value, otherwise,
* there's a risk of setting a high new delay that goes into effect almost immediately without the
* possibility of human intervention in the case of an input error (eg. set milliseconds instead of seconds).
*/
function defaultAdminDelayIncreaseWait() external view returns (uint48);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/AccessControl.sol)
pragma solidity ^0.8.20;
import {IAccessControl} from "./IAccessControl.sol";
import {Context} from "../utils/Context.sol";
import {ERC165} from "../utils/introspection/ERC165.sol";
/**
* @dev Contract module that allows children to implement role-based access
* control mechanisms. This is a lightweight version that doesn't allow enumerating role
* members except through off-chain means by accessing the contract event logs. Some
* applications may benefit from on-chain enumerability, for those cases see
* {AccessControlEnumerable}.
*
* Roles are referred to by their `bytes32` identifier. These should be exposed
* in the external API and be unique. The best way to achieve this is by
* using `public constant` hash digests:
*
* ```solidity
* bytes32 public constant MY_ROLE = keccak256("MY_ROLE");
* ```
*
* Roles can be used to represent a set of permissions. To restrict access to a
* function call, use {hasRole}:
*
* ```solidity
* function foo() public {
* require(hasRole(MY_ROLE, msg.sender));
* ...
* }
* ```
*
* Roles can be granted and revoked dynamically via the {grantRole} and
* {revokeRole} functions. Each role has an associated admin role, and only
* accounts that have a role's admin role can call {grantRole} and {revokeRole}.
*
* By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means
* that only accounts with this role will be able to grant or revoke other
* roles. More complex role relationships can be created by using
* {_setRoleAdmin}.
*
* WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to
* grant and revoke this role. Extra precautions should be taken to secure
* accounts that have been granted it. We recommend using {AccessControlDefaultAdminRules}
* to enforce additional security measures for this role.
*/
abstract contract AccessControl is Context, IAccessControl, ERC165 {
struct RoleData {
mapping(address account => bool) hasRole;
bytes32 adminRole;
}
mapping(bytes32 role => RoleData) private _roles;
bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;
/**
* @dev Modifier that checks that an account has a specific role. Reverts
* with an {AccessControlUnauthorizedAccount} error including the required role.
*/
modifier onlyRole(bytes32 role) {
_checkRole(role);
_;
}
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId);
}
/**
* @dev Returns `true` if `account` has been granted `role`.
*/
function hasRole(bytes32 role, address account) public view virtual returns (bool) {
return _roles[role].hasRole[account];
}
/**
* @dev Reverts with an {AccessControlUnauthorizedAccount} error if `_msgSender()`
* is missing `role`. Overriding this function changes the behavior of the {onlyRole} modifier.
*/
function _checkRole(bytes32 role) internal view virtual {
_checkRole(role, _msgSender());
}
/**
* @dev Reverts with an {AccessControlUnauthorizedAccount} error if `account`
* is missing `role`.
*/
function _checkRole(bytes32 role, address account) internal view virtual {
if (!hasRole(role, account)) {
revert AccessControlUnauthorizedAccount(account, role);
}
}
/**
* @dev Returns the admin role that controls `role`. See {grantRole} and
* {revokeRole}.
*
* To change a role's admin, use {_setRoleAdmin}.
*/
function getRoleAdmin(bytes32 role) public view virtual returns (bytes32) {
return _roles[role].adminRole;
}
/**
* @dev Grants `role` to `account`.
*
* If `account` had not been already granted `role`, emits a {RoleGranted}
* event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*
* May emit a {RoleGranted} event.
*/
function grantRole(bytes32 role, address account) public virtual onlyRole(getRoleAdmin(role)) {
_grantRole(role, account);
}
/**
* @dev Revokes `role` from `account`.
*
* If `account` had been granted `role`, emits a {RoleRevoked} event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*
* May emit a {RoleRevoked} event.
*/
function revokeRole(bytes32 role, address account) public virtual onlyRole(getRoleAdmin(role)) {
_revokeRole(role, account);
}
/**
* @dev Revokes `role` from the calling account.
*
* Roles are often managed via {grantRole} and {revokeRole}: this function's
* purpose is to provide a mechanism for accounts to lose their privileges
* if they are compromised (such as when a trusted device is misplaced).
*
* If the calling account had been revoked `role`, emits a {RoleRevoked}
* event.
*
* Requirements:
*
* - the caller must be `callerConfirmation`.
*
* May emit a {RoleRevoked} event.
*/
function renounceRole(bytes32 role, address callerConfirmation) public virtual {
if (callerConfirmation != _msgSender()) {
revert AccessControlBadConfirmation();
}
_revokeRole(role, callerConfirmation);
}
/**
* @dev Sets `adminRole` as ``role``'s admin role.
*
* Emits a {RoleAdminChanged} event.
*/
function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
bytes32 previousAdminRole = getRoleAdmin(role);
_roles[role].adminRole = adminRole;
emit RoleAdminChanged(role, previousAdminRole, adminRole);
}
/**
* @dev Attempts to grant `role` to `account` and returns a boolean indicating if `role` was granted.
*
* Internal function without access restriction.
*
* May emit a {RoleGranted} event.
*/
function _grantRole(bytes32 role, address account) internal virtual returns (bool) {
if (!hasRole(role, account)) {
_roles[role].hasRole[account] = true;
emit RoleGranted(role, account, _msgSender());
return true;
} else {
return false;
}
}
/**
* @dev Attempts to revoke `role` to `account` and returns a boolean indicating if `role` was revoked.
*
* Internal function without access restriction.
*
* May emit a {RoleRevoked} event.
*/
function _revokeRole(bytes32 role, address account) internal virtual returns (bool) {
if (hasRole(role, account)) {
_roles[role].hasRole[account] = false;
emit RoleRevoked(role, account, _msgSender());
return true;
} else {
return false;
}
}
}// 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) (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 Returns the addition of two unsigned integers, with an success flag (no overflow).
*/
function tryAdd(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {
unchecked {
uint256 c = a + b;
if (c < a) return (false, 0);
return (true, c);
}
}
/**
* @dev Returns the subtraction of two unsigned integers, with an success flag (no overflow).
*/
function trySub(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {
unchecked {
if (b > a) return (false, 0);
return (true, a - b);
}
}
/**
* @dev Returns the multiplication of two unsigned integers, with an success flag (no overflow).
*/
function tryMul(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {
unchecked {
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the
// benefit is lost if 'b' is also tested.
// See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
if (a == 0) return (true, 0);
uint256 c = a * b;
if (c / a != b) return (false, 0);
return (true, c);
}
}
/**
* @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 {
if (b == 0) return (false, 0);
return (true, 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 {
if (b == 0) return (false, 0);
return (true, a % b);
}
}
/**
* @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 {
// 512-bit multiply [prod1 prod0] = 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 = prod1 * 2²⁵⁶ + prod0.
uint256 prod0 = x * y; // Least significant 256 bits of the product
uint256 prod1; // Most significant 256 bits of the product
assembly {
let mm := mulmod(x, y, not(0))
prod1 := sub(sub(mm, prod0), lt(mm, prod0))
}
// Handle non-overflow cases, 256 by 256 division.
if (prod1 == 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 prod0 / denominator;
}
// Make sure the result is less than 2²⁵⁶. Also prevents denominator == 0.
if (denominator <= prod1) {
Panic.panic(ternary(denominator == 0, Panic.DIVISION_BY_ZERO, Panic.UNDER_OVERFLOW));
}
///////////////////////////////////////////////
// 512 by 256 division.
///////////////////////////////////////////////
// Make division exact by subtracting the remainder from [prod1 prod0].
uint256 remainder;
assembly {
// Compute remainder using mulmod.
remainder := mulmod(x, y, denominator)
// Subtract 256 bit number from 512 bit number.
prod1 := sub(prod1, gt(remainder, prod0))
prod0 := sub(prod0, 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 {
// Divide denominator by twos.
denominator := div(denominator, twos)
// Divide [prod1 prod0] by twos.
prod0 := div(prod0, 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 prod1 into prod0.
prod0 |= prod1 * 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 prod1
// is no longer required.
result = prod0 * 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 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 value) internal pure returns (uint256) {
uint256 result = 0;
uint256 exp;
unchecked {
exp = 128 * SafeCast.toUint(value > (1 << 128) - 1);
value >>= exp;
result += exp;
exp = 64 * SafeCast.toUint(value > (1 << 64) - 1);
value >>= exp;
result += exp;
exp = 32 * SafeCast.toUint(value > (1 << 32) - 1);
value >>= exp;
result += exp;
exp = 16 * SafeCast.toUint(value > (1 << 16) - 1);
value >>= exp;
result += exp;
exp = 8 * SafeCast.toUint(value > (1 << 8) - 1);
value >>= exp;
result += exp;
exp = 4 * SafeCast.toUint(value > (1 << 4) - 1);
value >>= exp;
result += exp;
exp = 2 * SafeCast.toUint(value > (1 << 2) - 1);
value >>= exp;
result += exp;
result += SafeCast.toUint(value > 1);
}
return result;
}
/**
* @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 value) internal pure returns (uint256) {
uint256 result = 0;
uint256 isGt;
unchecked {
isGt = SafeCast.toUint(value > (1 << 128) - 1);
value >>= isGt * 128;
result += isGt * 16;
isGt = SafeCast.toUint(value > (1 << 64) - 1);
value >>= isGt * 64;
result += isGt * 8;
isGt = SafeCast.toUint(value > (1 << 32) - 1);
value >>= isGt * 32;
result += isGt * 4;
isGt = SafeCast.toUint(value > (1 << 16) - 1);
value >>= isGt * 16;
result += isGt * 2;
result += SafeCast.toUint(value > (1 << 8) - 1);
}
return result;
}
/**
* @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.0.0) (interfaces/IERC5313.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface for the Light Contract Ownership Standard.
*
* A standardized minimal interface required to identify an account that controls a contract
*/
interface IERC5313 {
/**
* @dev Gets the address of the owner.
*/
function owner() external view returns (address);
}// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
import { AggregatorV3Interface } from "@chainlink/contracts/src/v0.8/shared/interfaces/AggregatorV3Interface.sol";
interface IChainlinkOracle {
/**
* @notice The sentinel value returned instead of the price if the data from the oracle is too old.
* @return sentinelValue_ The sentinel value for "price too old".
*/
function PRICE_TOO_OLD() external pure returns (int256 sentinelValue_);
/**
* @notice Gets the number of decimals of the asset from Chainlink.
* @return decimals_ The number of decimals of the asset.
*/
function getChainlinkDecimals() external view returns (uint256 decimals_);
/**
* @notice Gets the Chainlink price feed aggregator contract address.
* @return priceFeed_ The address of the Chainlink price feed contract.
*/
function getPriceFeed() external view returns (AggregatorV3Interface priceFeed_);
/**
* @notice Gets the duration after which the Chainlink data is considered stale or invalid.
* @return limit_ The price validity duration.
*/
function getChainlinkTimeElapsedLimit() external view returns (uint256 limit_);
}// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
/**
* @title Errors For The Middleware And Oracle Related Contracts
* @notice Defines all the custom errors thrown by the contracts related to the OracleMiddleware contract.
*/
interface IOracleMiddlewareErrors {
/**
* @notice The price returned by an oracle is negative.
* @param price The price returned by the oracle.
*/
error OracleMiddlewareWrongPrice(int256 price);
/**
* @notice The price returned by an oracle is too old.
* @param timestamp The timestamp of the price given by the oracle.
*/
error OracleMiddlewarePriceTooOld(uint256 timestamp);
/**
* @notice The price returned by an oracle is too recent.
* @param timestamp The timestamp of the price given by the oracle.
*/
error OracleMiddlewarePriceTooRecent(uint256 timestamp);
/**
* @notice The Pyth price reported a positive exponent (negative decimals).
* @param expo The price exponent.
*/
error OracleMiddlewarePythPositiveExponent(int32 expo);
/// @notice Indicates that the confidence ratio is too high.
error OracleMiddlewareConfRatioTooHigh();
/// @notice Indicates that an incorrect amount of tokens was provided to cover the cost of price validation.
error OracleMiddlewareIncorrectFee();
/**
* @notice The validation fee returned by the Pyth contract exceeded the safeguard value.
* @param fee The required fee returned by Pyth.
*/
error OracleMiddlewarePythFeeSafeguard(uint256 fee);
/// @notice The Redstone price is to divergent from the Chainlink price.
error OracleMiddlewareRedstoneSafeguard();
/**
* @notice The withdrawal of the Ether in the contract failed.
* @param to The address of the intended recipient.
*/
error OracleMiddlewareTransferFailed(address to);
/// @notice The recipient of a transfer is the zero address.
error OracleMiddlewareTransferToZeroAddress();
/**
* @notice The recent price delay is outside of the limits.
* @param newDelay The delay that was provided.
*/
error OracleMiddlewareInvalidRecentPriceDelay(uint64 newDelay);
/// @dev The new penalty is invalid.
error OracleMiddlewareInvalidPenaltyBps();
/// @notice The provided Chainlink round ID is invalid.
error OracleMiddlewareInvalidRoundId();
/// @notice The new low latency delay is invalid.
error OracleMiddlewareInvalidLowLatencyDelay();
}// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
/**
* @title Events For The Middleware And Oracle Related Contracts
* @notice Defines all the custom events emitted by the contracts related to the OracleMiddleware contract.
*/
interface IOracleMiddlewareEvents {
/**
* @notice The time elapsed limit was updated.
* @param newTimeElapsedLimit The new limit.
*/
event TimeElapsedLimitUpdated(uint256 newTimeElapsedLimit);
/**
* @notice The validation delay was updated.
* @param newValidationDelay The new validation delay.
*/
event ValidationDelayUpdated(uint256 newValidationDelay);
/**
* @notice The recent price delay for Pyth was updated.
* @param newDelay The new recent price delay.
*/
event PythRecentPriceDelayUpdated(uint64 newDelay);
/**
* @notice The recent price delay for Redstone was updated.
* @param newDelay The new recent price delay.
*/
event RedstoneRecentPriceDelayUpdated(uint48 newDelay);
/**
* @notice The confidence ratio was updated.
* @param newConfRatio The new confidence ratio.
*/
event ConfRatioUpdated(uint256 newConfRatio);
/**
* @notice The penalty for Redstone prices was updated.
* @param newPenaltyBps The new penalty.
*/
event PenaltyBpsUpdated(uint16 newPenaltyBps);
/**
* @notice The low latency delay was updated.
* @param newLowLatencyDelay The new low latency delay.
*/
event LowLatencyDelayUpdated(uint16 newLowLatencyDelay);
}// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
import { IPyth } from "@pythnetwork/pyth-sdk-solidity/IPyth.sol";
interface IPythOracle {
/**
* @notice Gets the Pyth contract address.
* @return pyth_ The Pyth contract address.
*/
function getPyth() external view returns (IPyth pyth_);
/**
* @notice Gets the ID of the price feed queried by this contract.
* @return feedId_ The Pyth price feed ID.
*/
function getPythFeedId() external view returns (bytes32 feedId_);
/**
* @notice Gets the recent price delay.
* @return recentPriceDelay_ The maximum age of a recent price to be considered valid.
*/
function getPythRecentPriceDelay() external view returns (uint64 recentPriceDelay_);
}// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
import { IERC20Metadata } from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import { HugeUint } from "@smardex-solidity-libraries-1/HugeUint.sol";
import { IBaseLiquidationRewardsManager } from "../LiquidationRewardsManager/IBaseLiquidationRewardsManager.sol";
import { IBaseOracleMiddleware } from "../OracleMiddleware/IBaseOracleMiddleware.sol";
import { IBaseRebalancer } from "../Rebalancer/IBaseRebalancer.sol";
import { IUsdn } from "../Usdn/IUsdn.sol";
import { IUsdnProtocolTypes } from "./IUsdnProtocolTypes.sol";
/**
* @title IUsdnProtocolFallback
* @notice Interface for the USDN protocol fallback functions
*/
interface IUsdnProtocolFallback is IUsdnProtocolTypes {
/**
* @notice Retrieves the list of pending actions that must be validated by the next user action in the protocol.
* @dev If this function returns a non-empty list of pending actions, then the next user action MUST include the
* corresponding list of price update data and raw indices as the last parameter. The user that processes those
* pending actions will receive the corresponding security deposit.
* @param currentUser The address of the user that will submit the price signatures for third-party actions
* validations. This is used to filter out their actions from the returned list.
* @param lookAhead Additionally to pending actions which are actionable at this moment `block.timestamp`, the
* function will also return pending actions which will be actionable `lookAhead` seconds later. It is recommended
* to use a non-zero value in order to account for the interval where the validation transaction will be pending. A
* value of 30 seconds should already account for most situations and avoid reverts in case an action becomes
* actionable after a user submits their transaction.
* @param maxIter The maximum number of iterations when looking through the queue to find actionable pending
* actions. This value will be clamped to [MIN_ACTIONABLE_PENDING_ACTIONS_ITER,_pendingActionsQueue.length()].
* @return actions_ The pending actions if any, otherwise an empty array.
* @return rawIndices_ The raw indices of the actionable pending actions in the queue if any, otherwise an empty
* array. Each entry corresponds to the action in the `actions_` array, at the same index.
*/
function getActionablePendingActions(address currentUser, uint256 lookAhead, uint256 maxIter)
external
view
returns (PendingAction[] memory actions_, uint128[] memory rawIndices_);
/**
* @notice Retrieves the pending action with `user` as the given validator.
* @param user The user's address.
* @return action_ The pending action if any, otherwise a struct with all fields set to zero and
* `ProtocolAction.None`.
*/
function getUserPendingAction(address user) external view returns (PendingAction memory action_);
/**
* @notice Computes the hash generated from the given tick number and version.
* @param tick The tick number.
* @param version The tick version.
* @return hash_ The hash of the given tick number and version.
*/
function tickHash(int24 tick, uint256 version) external pure returns (bytes32 hash_);
/**
* @notice Computes the liquidation price of the given tick number, taking into account the effects of funding.
* @dev Uses the values from storage for the various variables. Note that ticks that are
* not a multiple of the tick spacing cannot contain a long position.
* @param tick The tick number.
* @return price_ The liquidation price.
*/
function getEffectivePriceForTick(int24 tick) external view returns (uint128 price_);
/**
* @notice Computes the liquidation price of the given tick number, taking into account the effects of funding.
* @dev Uses the given values instead of the ones from the storage. Note that ticks that are not a multiple of the
* tick spacing cannot contain a long position.
* @param tick The tick number.
* @param assetPrice The current/projected price of the asset.
* @param longTradingExpo The trading exposure of the long side (total expo - balance long).
* @param accumulator The liquidation multiplier accumulator.
* @return price_ The liquidation price.
*/
function getEffectivePriceForTick(
int24 tick,
uint256 assetPrice,
uint256 longTradingExpo,
HugeUint.Uint512 memory accumulator
) external view returns (uint128 price_);
/**
* @notice Computes an estimate of the amount of assets received when withdrawing.
* @dev The result is a rough estimate and does not take into account rebases and liquidations.
* @param usdnShares The amount of USDN shares to use in the withdrawal.
* @param price The current/projected price of the asset.
* @param timestamp The The timestamp corresponding to `price`.
* @return assetExpected_ The expected amount of assets to be received.
*/
function previewWithdraw(uint256 usdnShares, uint128 price, uint128 timestamp)
external
view
returns (uint256 assetExpected_);
/**
* @notice Computes an estimate of USDN tokens to be minted and SDEX tokens to be burned when depositing.
* @dev The result is a rough estimate and does not take into account rebases and liquidations.
* @param amount The amount of assets to deposit.
* @param price The current/projected price of the asset.
* @param timestamp The timestamp corresponding to `price`.
* @return usdnSharesExpected_ The amount of USDN shares to be minted.
* @return sdexToBurn_ The amount of SDEX tokens to be burned.
*/
function previewDeposit(uint256 amount, uint128 price, uint128 timestamp)
external
view
returns (uint256 usdnSharesExpected_, uint256 sdexToBurn_);
/**
* @notice Refunds the security deposit to the given validator if it has a liquidated initiated long position.
* @dev The security deposit is always sent to the validator even if the pending action is actionable.
* @param validator The address of the validator (must be payable as it will receive some native currency).
*/
function refundSecurityDeposit(address payable validator) external;
/// @notice Sends the accumulated SDEX token fees to the dead address. This function can be called by anyone.
function burnSdex() external;
/* -------------------------------------------------------------------------- */
/* Admin functions */
/* -------------------------------------------------------------------------- */
/**
* @notice Removes a stuck pending action and performs the minimal amount of cleanup necessary.
* @dev This function can only be called by the owner of the protocol, it serves as an escape hatch if a
* pending action ever gets stuck due to something internal reverting unexpectedly.
* It will not refund any fees or burned SDEX.
* @param validator The address of the validator of the stuck pending action.
* @param to Where the retrieved funds should be sent (security deposit, assets, usdn). Must be payable.
*/
function removeBlockedPendingAction(address validator, address payable to) external;
/**
* @notice Removes a stuck pending action with no cleanup.
* @dev This function can only be called by the owner of the protocol, it serves as an escape hatch if a
* pending action ever gets stuck due to something internal reverting unexpectedly.
* Always try to use `removeBlockedPendingAction` first, and only call this function if the other one fails.
* It will not refund any fees or burned SDEX.
* @param validator The address of the validator of the stuck pending action.
* @param to Where the retrieved funds should be sent (security deposit, assets, usdn). Must be payable.
*/
function removeBlockedPendingActionNoCleanup(address validator, address payable to) external;
/**
* @notice Removes a stuck pending action and performs the minimal amount of cleanup necessary.
* @dev This function can only be called by the owner of the protocol, it serves as an escape hatch if a
* pending action ever gets stuck due to something internal reverting unexpectedly.
* It will not refund any fees or burned SDEX.
* @param rawIndex The raw index of the stuck pending action.
* @param to Where the retrieved funds should be sent (security deposit, assets, usdn). Must be payable.
*/
function removeBlockedPendingAction(uint128 rawIndex, address payable to) external;
/**
* @notice Removes a stuck pending action with no cleanup.
* @dev This function can only be called by the owner of the protocol, it serves as an escape hatch if a
* pending action ever gets stuck due to something internal reverting unexpectedly.
* Always try to use `removeBlockedPendingAction` first, and only call this function if the other one fails.
* It will not refund any fees or burned SDEX.
* @param rawIndex The raw index of the stuck pending action.
* @param to Where the retrieved funds should be sent (security deposit, assets, usdn). Must be payable.
*/
function removeBlockedPendingActionNoCleanup(uint128 rawIndex, address payable to) external;
/* -------------------------------------------------------------------------- */
/* Immutables getters */
/* -------------------------------------------------------------------------- */
/**
* @notice The number of ticks between usable ticks. Only tick numbers that are a multiple of the tick spacing can
* be used for storing long positions.
* @dev A tick spacing of 1 is equivalent to a 0.01% increase in price between ticks. A tick spacing of 100 is.
* equivalent to a ~1.005% increase in price between ticks.
* @return tickSpacing_ The tick spacing.
*/
function getTickSpacing() external view returns (int24 tickSpacing_);
/**
* @notice Gets the address of the protocol's underlying asset (ERC20 token).
* @return asset_ The address of the asset token.
*/
function getAsset() external view returns (IERC20Metadata asset_);
/**
* @notice Gets the address of the SDEX ERC20 token.
* @return sdex_ The address of the SDEX token.
*/
function getSdex() external view returns (IERC20Metadata sdex_);
/**
* @notice Gets the number of decimals of the asset's price feed.
* @return decimals_ The number of decimals of the asset's price feed.
*/
function getPriceFeedDecimals() external view returns (uint8 decimals_);
/**
* @notice Gets the number of decimals of the underlying asset token.
* @return decimals_ The number of decimals of the asset token.
*/
function getAssetDecimals() external view returns (uint8 decimals_);
/**
* @notice Gets the address of the USDN ERC20 token.
* @return usdn_ The address of USDN ERC20 token.
*/
function getUsdn() external view returns (IUsdn usdn_);
/**
* @notice Gets the `MIN_DIVISOR` constant of the USDN token.
* @dev Check the USDN contract for more information.
* @return minDivisor_ The `MIN_DIVISOR` constant of the USDN token.
*/
function getUsdnMinDivisor() external view returns (uint256 minDivisor_);
/* -------------------------------------------------------------------------- */
/* Parameters getters */
/* -------------------------------------------------------------------------- */
/**
* @notice Gets the oracle middleware contract.
* @return oracleMiddleware_ The address of the oracle middleware contract.
*/
function getOracleMiddleware() external view returns (IBaseOracleMiddleware oracleMiddleware_);
/**
* @notice Gets the liquidation rewards manager contract.
* @return liquidationRewardsManager_ The address of the liquidation rewards manager contract.
*/
function getLiquidationRewardsManager()
external
view
returns (IBaseLiquidationRewardsManager liquidationRewardsManager_);
/**
* @notice Gets the rebalancer contract.
* @return rebalancer_ The address of the rebalancer contract.
*/
function getRebalancer() external view returns (IBaseRebalancer rebalancer_);
/**
* @notice Gets the lowest leverage that can be used to open a long position.
* @return minLeverage_ The minimum leverage (with `LEVERAGE_DECIMALS` decimals).
*/
function getMinLeverage() external view returns (uint256 minLeverage_);
/**
* @notice Gets the highest leverage that can be used to open a long position.
* @dev A position can have a leverage a bit higher than this value under specific conditions involving
* a change to the liquidation penalty setting.
* @return maxLeverage_ The maximum leverage value (with `LEVERAGE_DECIMALS` decimals).
*/
function getMaxLeverage() external view returns (uint256 maxLeverage_);
/**
* @notice Gets the deadline of the exclusivity period for the validator of a pending action with a low-latency
* oracle.
* @dev After this deadline, any user can validate the action with the low-latency oracle until the
* OracleMiddleware's `_lowLatencyDelay`, and retrieve the security deposit for the pending action.
* @return deadline_ The low-latency validation deadline of a validator (in seconds).
*/
function getLowLatencyValidatorDeadline() external view returns (uint128 deadline_);
/**
* @notice Gets the deadline of the exclusivity period for the validator to confirm their action with the on-chain
* oracle.
* @dev After this deadline, any user can validate the pending action with the on-chain oracle and retrieve its
* security deposit.
* @return deadline_ The on-chain validation deadline of a validator (in seconds)
*/
function getOnChainValidatorDeadline() external view returns (uint128 deadline_);
/**
* @notice Gets the liquidation penalty applied to the liquidation price when opening a position.
* @return liquidationPenalty_ The liquidation penalty (in ticks).
*/
function getLiquidationPenalty() external view returns (uint24 liquidationPenalty_);
/**
* @notice Gets the safety margin for the liquidation price of newly open positions.
* @return safetyMarginBps_ The safety margin (in basis points).
*/
function getSafetyMarginBps() external view returns (uint256 safetyMarginBps_);
/**
* @notice Gets the number of tick liquidations to perform when attempting to
* liquidate positions during user actions.
* @return iterations_ The number of iterations for liquidations during user actions.
*/
function getLiquidationIteration() external view returns (uint16 iterations_);
/**
* @notice Gets the time frame for the EMA calculations.
* @dev The EMA is set to the last funding rate when the time elapsed between 2 actions is greater than this value.
* @return period_ The time frame of the EMA (in seconds).
*/
function getEMAPeriod() external view returns (uint128 period_);
/**
* @notice Gets the scaling factor (SF) of the funding rate.
* @return scalingFactor_ The scaling factor (with `FUNDING_SF_DECIMALS` decimals).
*/
function getFundingSF() external view returns (uint256 scalingFactor_);
/**
* @notice Gets the fee taken by the protocol during the application of funding.
* @return feeBps_ The fee applied to the funding (in basis points).
*/
function getProtocolFeeBps() external view returns (uint16 feeBps_);
/**
* @notice Gets the fee applied when a long position is opened or closed.
* @return feeBps_ The fee applied to a long position (in basis points).
*/
function getPositionFeeBps() external view returns (uint16 feeBps_);
/**
* @notice Gets the fee applied during a vault deposit or withdrawal.
* @return feeBps_ The fee applied to a vault action (in basis points).
*/
function getVaultFeeBps() external view returns (uint16 feeBps_);
/**
* @notice Gets the rewards ratio given to the caller when burning SDEX tokens.
* @return rewardsBps_ The rewards ratio (in basis points).
*/
function getSdexRewardsRatioBps() external view returns (uint16 rewardsBps_);
/**
* @notice Gets the part of the remaining collateral given as a bonus to the Rebalancer upon liquidation of a tick.
* @return bonusBps_ The fraction of the remaining collateral for the Rebalancer bonus (in basis points).
*/
function getRebalancerBonusBps() external view returns (uint16 bonusBps_);
/**
* @notice Gets the ratio of SDEX tokens to burn per minted USDN.
* @return ratio_ The ratio (to be divided by SDEX_BURN_ON_DEPOSIT_DIVISOR).
*/
function getSdexBurnOnDepositRatio() external view returns (uint32 ratio_);
/**
* @notice Gets the amount of native tokens used as security deposit when opening a new position.
* @return securityDeposit_ The amount of assets to use as a security deposit (in ether).
*/
function getSecurityDepositValue() external view returns (uint64 securityDeposit_);
/**
* @notice Gets the threshold to reach to send accumulated fees to the fee collector.
* @return threshold_ The amount of accumulated fees to reach (in `_assetDecimals`).
*/
function getFeeThreshold() external view returns (uint256 threshold_);
/**
* @notice Gets the address of the fee collector.
* @return feeCollector_ The address of the fee collector.
*/
function getFeeCollector() external view returns (address feeCollector_);
/**
* @notice Returns the amount of time to wait before an action can be validated.
* @dev This is also the amount of time to add to the initiate action timestamp to fetch the correct price data to
* validate said action with a low-latency oracle.
* @return delay_ The validation delay (in seconds).
*/
function getMiddlewareValidationDelay() external view returns (uint256 delay_);
/**
* @notice Gets the expo imbalance limit when depositing assets (in basis points).
* @return depositExpoImbalanceLimitBps_ The deposit expo imbalance limit.
*/
function getDepositExpoImbalanceLimitBps() external view returns (int256 depositExpoImbalanceLimitBps_);
/**
* @notice Gets the expo imbalance limit when withdrawing assets (in basis points).
* @return withdrawalExpoImbalanceLimitBps_ The withdrawal expo imbalance limit.
*/
function getWithdrawalExpoImbalanceLimitBps() external view returns (int256 withdrawalExpoImbalanceLimitBps_);
/**
* @notice Gets the expo imbalance limit when opening a position (in basis points).
* @return openExpoImbalanceLimitBps_ The open expo imbalance limit.
*/
function getOpenExpoImbalanceLimitBps() external view returns (int256 openExpoImbalanceLimitBps_);
/**
* @notice Gets the expo imbalance limit when closing a position (in basis points).
* @return closeExpoImbalanceLimitBps_ The close expo imbalance limit.
*/
function getCloseExpoImbalanceLimitBps() external view returns (int256 closeExpoImbalanceLimitBps_);
/**
* @notice Returns the limit of the imbalance in bps to close the rebalancer position.
* @return rebalancerCloseExpoImbalanceLimitBps_ The limit of the imbalance in bps to close the rebalancer position.
*/
function getRebalancerCloseExpoImbalanceLimitBps()
external
view
returns (int256 rebalancerCloseExpoImbalanceLimitBps_);
/**
* @notice Returns the imbalance desired on the long side after the creation of a rebalancer position.
* @dev The creation of the rebalancer position aims for this target but does not guarantee reaching it.
* @return targetLongImbalance_ The target long imbalance.
*/
function getLongImbalanceTargetBps() external view returns (int256 targetLongImbalance_);
/**
* @notice Gets the nominal (target) price of USDN.
* @return price_ The price of the USDN token after a rebase (in `_priceFeedDecimals`).
*/
function getTargetUsdnPrice() external view returns (uint128 price_);
/**
* @notice Gets the USDN token price above which a rebase should occur.
* @return threshold_ The rebase threshold (in `_priceFeedDecimals`).
*/
function getUsdnRebaseThreshold() external view returns (uint128 threshold_);
/**
* @notice Gets the minimum collateral amount when opening a long position.
* @return minLongPosition_ The minimum amount (with `_assetDecimals`).
*/
function getMinLongPosition() external view returns (uint256 minLongPosition_);
/* -------------------------------------------------------------------------- */
/* State getters */
/* -------------------------------------------------------------------------- */
/**
* @notice Gets the value of the funding rate at the last timestamp (`getLastUpdateTimestamp`).
* @return lastFunding_ The last value of the funding rate (per day) with `FUNDING_RATE_DECIMALS` decimals.
*/
function getLastFundingPerDay() external view returns (int256 lastFunding_);
/**
* @notice Gets the neutral price of the asset used during the last update of the vault and long balances.
* @return lastPrice_ The most recent known price of the asset (in `_priceFeedDecimals`).
*/
function getLastPrice() external view returns (uint128 lastPrice_);
/**
* @notice Gets the timestamp of the last time a fresh price was provided.
* @return lastTimestamp_ The timestamp of the last update.
*/
function getLastUpdateTimestamp() external view returns (uint128 lastTimestamp_);
/**
* @notice Gets the fees that were accumulated by the contract and are yet to be sent
* to the fee collector (in `_assetDecimals`).
* @return protocolFees_ The amount of accumulated fees still in the contract.
*/
function getPendingProtocolFee() external view returns (uint256 protocolFees_);
/**
* @notice Gets the amount of assets backing the USDN token.
* @return balanceVault_ The amount of assets on the vault side (in `_assetDecimals`).
*/
function getBalanceVault() external view returns (uint256 balanceVault_);
/**
* @notice Gets the pending balance updates due to pending vault actions.
* @return pendingBalanceVault_ The unreflected balance change due to pending vault actions (in `_assetDecimals`).
*/
function getPendingBalanceVault() external view returns (int256 pendingBalanceVault_);
/**
* @notice Gets the exponential moving average of the funding rate per day.
* @return ema_ The exponential moving average of the funding rate per day.
*/
function getEMA() external view returns (int256 ema_);
/**
* @notice Gets the summed value of all the currently open long positions at `_lastUpdateTimestamp`.
* @return balanceLong_ The balance of the long side (in `_assetDecimals`).
*/
function getBalanceLong() external view returns (uint256 balanceLong_);
/**
* @notice Gets the total exposure of all currently open long positions.
* @return totalExpo_ The total exposure of the longs (in `_assetDecimals`).
*/
function getTotalExpo() external view returns (uint256 totalExpo_);
/**
* @notice Gets the accumulator used to calculate the liquidation multiplier.
* @return accumulator_ The liquidation multiplier accumulator.
*/
function getLiqMultiplierAccumulator() external view returns (HugeUint.Uint512 memory accumulator_);
/**
* @notice Gets the current version of the given tick.
* @param tick The tick number.
* @return tickVersion_ The version of the tick.
*/
function getTickVersion(int24 tick) external view returns (uint256 tickVersion_);
/**
* @notice Gets the tick data for the current tick version.
* @param tick The tick number.
* @return tickData_ The tick data.
*/
function getTickData(int24 tick) external view returns (TickData memory tickData_);
/**
* @notice Gets the long position at the provided tick and index.
* @param tick The tick number.
* @param index The position index.
* @return position_ The long position.
*/
function getCurrentLongPosition(int24 tick, uint256 index) external view returns (Position memory position_);
/**
* @notice Gets the highest tick that has an open position.
* @return tick_ The highest populated tick.
*/
function getHighestPopulatedTick() external view returns (int24 tick_);
/**
* @notice Gets the total number of long positions currently open.
* @return totalLongPositions_ The number of long positions.
*/
function getTotalLongPositions() external view returns (uint256 totalLongPositions_);
/**
* @notice Gets the address of the fallback contract.
* @return fallback_ The address of the fallback contract.
*/
function getFallbackAddress() external view returns (address fallback_);
/**
* @notice Gets the pause status of the USDN protocol.
* @return isPaused_ True if it's paused, false otherwise.
*/
function isPaused() external view returns (bool isPaused_);
/**
* @notice Gets the nonce a user can use to generate a delegation signature.
* @dev This is to prevent replay attacks when using an eip712 delegation signature.
* @param user The address of the user.
* @return nonce_ The user's nonce.
*/
function getNonce(address user) external view returns (uint256 nonce_);
/* -------------------------------------------------------------------------- */
/* Setters */
/* -------------------------------------------------------------------------- */
/**
* @notice Replaces the OracleMiddleware contract with a new implementation.
* @dev Cannot be the 0 address.
* @param newOracleMiddleware The address of the new contract.
*/
function setOracleMiddleware(IBaseOracleMiddleware newOracleMiddleware) external;
/**
* @notice Sets the fee collector address.
* @dev Cannot be the zero address.
* @param newFeeCollector The address of the fee collector.
*/
function setFeeCollector(address newFeeCollector) external;
/**
* @notice Replaces the LiquidationRewardsManager contract with a new implementation.
* @dev Cannot be the 0 address.
* @param newLiquidationRewardsManager The address of the new contract.
*/
function setLiquidationRewardsManager(IBaseLiquidationRewardsManager newLiquidationRewardsManager) external;
/**
* @notice Replaces the Rebalancer contract with a new implementation.
* @param newRebalancer The address of the new contract.
*/
function setRebalancer(IBaseRebalancer newRebalancer) external;
/**
* @notice Sets the new deadlines of the exclusivity period for the validator to confirm its action and get its
* security deposit back.
* @param newLowLatencyValidatorDeadline The new exclusivity deadline for low-latency validation (offset from
* initiate timestamp).
* @param newOnChainValidatorDeadline The new exclusivity deadline for on-chain validation (offset from initiate
* timestamp + oracle middleware's low latency delay).
*/
function setValidatorDeadlines(uint128 newLowLatencyValidatorDeadline, uint128 newOnChainValidatorDeadline)
external;
/**
* @notice Sets the minimum long position size.
* @dev This value is used to prevent users from opening positions that are too small and not worth liquidating.
* @param newMinLongPosition The new minimum long position size (with `_assetDecimals`).
*/
function setMinLongPosition(uint256 newMinLongPosition) external;
/**
* @notice Sets the new minimum leverage for a position.
* @param newMinLeverage The new minimum leverage.
*/
function setMinLeverage(uint256 newMinLeverage) external;
/**
* @notice Sets the new maximum leverage for a position.
* @param newMaxLeverage The new maximum leverage.
*/
function setMaxLeverage(uint256 newMaxLeverage) external;
/**
* @notice Sets the new liquidation penalty (in ticks).
* @param newLiquidationPenalty The new liquidation penalty.
*/
function setLiquidationPenalty(uint24 newLiquidationPenalty) external;
/**
* @notice Sets the new exponential moving average period of the funding rate.
* @param newEMAPeriod The new EMA period.
*/
function setEMAPeriod(uint128 newEMAPeriod) external;
/**
* @notice Sets the new scaling factor (SF) of the funding rate.
* @param newFundingSF The new scaling factor (SF) of the funding rate.
*/
function setFundingSF(uint256 newFundingSF) external;
/**
* @notice Sets the protocol fee.
* @dev Fees are charged when the funding is applied (Example: 50 bps -> 0.5%).
* @param newFeeBps The fee to be charged (in basis points).
*/
function setProtocolFeeBps(uint16 newFeeBps) external;
/**
* @notice Sets the position fee.
* @param newPositionFee The new position fee (in basis points).
*/
function setPositionFeeBps(uint16 newPositionFee) external;
/**
* @notice Sets the vault fee.
* @param newVaultFee The new vault fee (in basis points).
*/
function setVaultFeeBps(uint16 newVaultFee) external;
/**
* @notice Sets the rewards ratio given to the caller when burning SDEX tokens.
* @param newRewardsBps The new rewards ratio (in basis points).
*/
function setSdexRewardsRatioBps(uint16 newRewardsBps) external;
/**
* @notice Sets the rebalancer bonus.
* @param newBonus The bonus (in basis points).
*/
function setRebalancerBonusBps(uint16 newBonus) external;
/**
* @notice Sets the ratio of SDEX tokens to burn per minted USDN.
* @param newRatio The new ratio.
*/
function setSdexBurnOnDepositRatio(uint32 newRatio) external;
/**
* @notice Sets the security deposit value.
* @dev The maximum value of the security deposit is 2^64 - 1 = 18446744073709551615 = 18.4 ethers.
* @param securityDepositValue The security deposit value.
* This value cannot be greater than MAX_SECURITY_DEPOSIT.
*/
function setSecurityDepositValue(uint64 securityDepositValue) external;
/**
* @notice Sets the imbalance limits (in basis point).
* @dev `newLongImbalanceTargetBps` needs to be lower than `newCloseLimitBps` and
* higher than the additive inverse of `newWithdrawalLimitBps`.
* @param newOpenLimitBps The new open limit.
* @param newDepositLimitBps The new deposit limit.
* @param newWithdrawalLimitBps The new withdrawal limit.
* @param newCloseLimitBps The new close limit.
* @param newRebalancerCloseLimitBps The new rebalancer close limit.
* @param newLongImbalanceTargetBps The new target imbalance limit for the long side.
* A positive value will target below equilibrium, a negative one will target above equilibrium.
* If negative, the rebalancerCloseLimit will be useless since the minimum value is 1.
*/
function setExpoImbalanceLimits(
uint256 newOpenLimitBps,
uint256 newDepositLimitBps,
uint256 newWithdrawalLimitBps,
uint256 newCloseLimitBps,
uint256 newRebalancerCloseLimitBps,
int256 newLongImbalanceTargetBps
) external;
/**
* @notice Sets the new safety margin for the liquidation price of newly open positions.
* @param newSafetyMarginBps The new safety margin (in basis points).
*/
function setSafetyMarginBps(uint256 newSafetyMarginBps) external;
/**
* @notice Sets the new number of liquidations iteration for user actions.
* @param newLiquidationIteration The new number of liquidation iteration.
*/
function setLiquidationIteration(uint16 newLiquidationIteration) external;
/**
* @notice Sets the minimum amount of fees to be collected before they can be withdrawn.
* @param newFeeThreshold The minimum amount of fees to be collected before they can be withdrawn.
*/
function setFeeThreshold(uint256 newFeeThreshold) external;
/**
* @notice Sets the target USDN price.
* @dev When a rebase of USDN occurs, it will bring the price back down to this value.
* @param newPrice The new target price (with `_priceFeedDecimals`).
* This value cannot be greater than `_usdnRebaseThreshold`.
*/
function setTargetUsdnPrice(uint128 newPrice) external;
/**
* @notice Sets the USDN rebase threshold.
* @dev When the price of USDN exceeds this value, a rebase will be triggered.
* @param newThreshold The new threshold value (with `_priceFeedDecimals`).
* This value cannot be smaller than `_targetUsdnPrice` or greater than uint128(2 * 10 ** s._priceFeedDecimals)
*/
function setUsdnRebaseThreshold(uint128 newThreshold) external;
/**
* @notice Pauses related USDN protocol functions.
* @dev Pauses simultaneously all initiate/validate, refundSecurityDeposit and transferPositionOwnership functions.
* Before pausing, this function will call `_applyPnlAndFunding` with `_lastPrice` and the current timestamp.
* This is done to stop the funding rate from accumulating while the protocol is paused. Be sure to call {unpause}
* to update `_lastUpdateTimestamp` when unpausing.
*/
function pause() external;
/**
* @notice Pauses related USDN protocol functions without applying PnLs and the funding.
* @dev Pauses simultaneously all initiate/validate, refundSecurityDeposit and transferPositionOwnership functions.
* This safe version will not call `_applyPnlAndFunding` before pausing.
*/
function pauseSafe() external;
/**
* @notice Unpauses related USDN protocol functions.
* @dev Unpauses simultaneously all initiate/validate, refundSecurityDeposit and transferPositionOwnership
* functions. This function will set `_lastUpdateTimestamp` to the current timestamp to prevent any funding during
* the pause. Only meant to be called after a {pause} call.
*/
function unpause() external;
/**
* @notice Unpauses related USDN protocol functions without updating `_lastUpdateTimestamp`.
* @dev Unpauses simultaneously all initiate/validate, refundSecurityDeposit and transferPositionOwnership
* functions. This safe version will not set `_lastUpdateTimestamp` to the current timestamp.
*/
function unpauseSafe() external;
}// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
import { IAccessControlDefaultAdminRules } from
"@openzeppelin/contracts/access/extensions/IAccessControlDefaultAdminRules.sol";
import { IERC5267 } from "@openzeppelin/contracts/interfaces/IERC5267.sol";
import { IERC20Metadata } from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import { IBaseLiquidationRewardsManager } from "../LiquidationRewardsManager/IBaseLiquidationRewardsManager.sol";
import { IBaseOracleMiddleware } from "../OracleMiddleware/IBaseOracleMiddleware.sol";
import { IUsdn } from "../Usdn/IUsdn.sol";
import { IUsdnProtocolActions } from "./IUsdnProtocolActions.sol";
import { IUsdnProtocolCore } from "./IUsdnProtocolCore.sol";
import { IUsdnProtocolFallback } from "./IUsdnProtocolFallback.sol";
import { IUsdnProtocolLong } from "./IUsdnProtocolLong.sol";
import { IUsdnProtocolVault } from "./IUsdnProtocolVault.sol";
/**
* @title IUsdnProtocolImpl
* @notice Interface for the implementation of the USDN protocol (completed with {IUsdnProtocolFallback})
*/
interface IUsdnProtocolImpl is
IUsdnProtocolActions,
IUsdnProtocolVault,
IUsdnProtocolLong,
IUsdnProtocolCore,
IAccessControlDefaultAdminRules,
IERC5267
{
/**
* @notice Initializes the protocol's storage with the given values.
* @dev This function should be called on deployment when creating the proxy.
* It can only be called once.
* @param usdn The USDN ERC20 contract address (must have a total supply of 0).
* @param sdex The SDEX ERC20 contract address.
* @param asset The ERC20 contract address of the token held in the vault.
* @param oracleMiddleware The oracle middleware contract address.
* @param liquidationRewardsManager The liquidation rewards manager contract address.
* @param tickSpacing The number of ticks between usable ticks.
* @param feeCollector The address that will receive the protocol fees.
* @param protocolFallback The address of the contract that contains the remaining functions of the protocol.
* Any call with a function signature not present in this contract will be delegated to the fallback contract.
*/
function initializeStorage(
IUsdn usdn,
IERC20Metadata sdex,
IERC20Metadata asset,
IBaseOracleMiddleware oracleMiddleware,
IBaseLiquidationRewardsManager liquidationRewardsManager,
int24 tickSpacing,
address feeCollector,
IUsdnProtocolFallback protocolFallback
) external;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// solhint-disable-next-line interface-starts-with-i
interface AggregatorV3Interface {
function decimals() external view returns (uint8);
function description() external view returns (string memory);
function version() external view returns (uint256);
function getRoundData(
uint80 _roundId
) external view returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound);
function latestRoundData()
external
view
returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound);
}// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.0;
import "./PythStructs.sol";
import "./IPythEvents.sol";
/// @title Consume prices from the Pyth Network (https://pyth.network/).
/// @dev Please refer to the guidance at https://docs.pyth.network/documentation/pythnet-price-feeds/best-practices for how to consume prices safely.
/// @author Pyth Data Association
interface IPyth is IPythEvents {
/// @notice Returns the period (in seconds) that a price feed is considered valid since its publish time
function getValidTimePeriod() external view returns (uint validTimePeriod);
/// @notice Returns the price and confidence interval.
/// @dev Reverts if the price has not been updated within the last `getValidTimePeriod()` seconds.
/// @param id The Pyth Price Feed ID of which to fetch the price and confidence interval.
/// @return price - please read the documentation of PythStructs.Price to understand how to use this safely.
function getPrice(
bytes32 id
) external view returns (PythStructs.Price memory price);
/// @notice Returns the exponentially-weighted moving average price and confidence interval.
/// @dev Reverts if the EMA price is not available.
/// @param id The Pyth Price Feed ID of which to fetch the EMA price and confidence interval.
/// @return price - please read the documentation of PythStructs.Price to understand how to use this safely.
function getEmaPrice(
bytes32 id
) external view returns (PythStructs.Price memory price);
/// @notice Returns the price of a price feed without any sanity checks.
/// @dev This function returns the most recent price update in this contract without any recency checks.
/// This function is unsafe as the returned price update may be arbitrarily far in the past.
///
/// Users of this function should check the `publishTime` in the price to ensure that the returned price is
/// sufficiently recent for their application. If you are considering using this function, it may be
/// safer / easier to use either `getPrice` or `getPriceNoOlderThan`.
/// @return price - please read the documentation of PythStructs.Price to understand how to use this safely.
function getPriceUnsafe(
bytes32 id
) external view returns (PythStructs.Price memory price);
/// @notice Returns the price that is no older than `age` seconds of the current time.
/// @dev This function is a sanity-checked version of `getPriceUnsafe` which is useful in
/// applications that require a sufficiently-recent price. Reverts if the price wasn't updated sufficiently
/// recently.
/// @return price - please read the documentation of PythStructs.Price to understand how to use this safely.
function getPriceNoOlderThan(
bytes32 id,
uint age
) external view returns (PythStructs.Price memory price);
/// @notice Returns the exponentially-weighted moving average price of a price feed without any sanity checks.
/// @dev This function returns the same price as `getEmaPrice` in the case where the price is available.
/// However, if the price is not recent this function returns the latest available price.
///
/// The returned price can be from arbitrarily far in the past; this function makes no guarantees that
/// the returned price is recent or useful for any particular application.
///
/// Users of this function should check the `publishTime` in the price to ensure that the returned price is
/// sufficiently recent for their application. If you are considering using this function, it may be
/// safer / easier to use either `getEmaPrice` or `getEmaPriceNoOlderThan`.
/// @return price - please read the documentation of PythStructs.Price to understand how to use this safely.
function getEmaPriceUnsafe(
bytes32 id
) external view returns (PythStructs.Price memory price);
/// @notice Returns the exponentially-weighted moving average price that is no older than `age` seconds
/// of the current time.
/// @dev This function is a sanity-checked version of `getEmaPriceUnsafe` which is useful in
/// applications that require a sufficiently-recent price. Reverts if the price wasn't updated sufficiently
/// recently.
/// @return price - please read the documentation of PythStructs.Price to understand how to use this safely.
function getEmaPriceNoOlderThan(
bytes32 id,
uint age
) external view returns (PythStructs.Price memory price);
/// @notice Update price feeds with given update messages.
/// This method requires the caller to pay a fee in wei; the required fee can be computed by calling
/// `getUpdateFee` with the length of the `updateData` array.
/// Prices will be updated if they are more recent than the current stored prices.
/// The call will succeed even if the update is not the most recent.
/// @dev Reverts if the transferred fee is not sufficient or the updateData is invalid.
/// @param updateData Array of price update data.
function updatePriceFeeds(bytes[] calldata updateData) external payable;
/// @notice Wrapper around updatePriceFeeds that rejects fast if a price update is not necessary. A price update is
/// necessary if the current on-chain publishTime is older than the given publishTime. It relies solely on the
/// given `publishTimes` for the price feeds and does not read the actual price update publish time within `updateData`.
///
/// This method requires the caller to pay a fee in wei; the required fee can be computed by calling
/// `getUpdateFee` with the length of the `updateData` array.
///
/// `priceIds` and `publishTimes` are two arrays with the same size that correspond to senders known publishTime
/// of each priceId when calling this method. If all of price feeds within `priceIds` have updated and have
/// a newer or equal publish time than the given publish time, it will reject the transaction to save gas.
/// Otherwise, it calls updatePriceFeeds method to update the prices.
///
/// @dev Reverts if update is not needed or the transferred fee is not sufficient or the updateData is invalid.
/// @param updateData Array of price update data.
/// @param priceIds Array of price ids.
/// @param publishTimes Array of publishTimes. `publishTimes[i]` corresponds to known `publishTime` of `priceIds[i]`
function updatePriceFeedsIfNecessary(
bytes[] calldata updateData,
bytes32[] calldata priceIds,
uint64[] calldata publishTimes
) external payable;
/// @notice Returns the required fee to update an array of price updates.
/// @param updateData Array of price update data.
/// @return feeAmount The required fee in Wei.
function getUpdateFee(
bytes[] calldata updateData
) external view returns (uint feeAmount);
/// @notice Parse `updateData` and return price feeds of the given `priceIds` if they are all published
/// within `minPublishTime` and `maxPublishTime`.
///
/// You can use this method if you want to use a Pyth price at a fixed time and not the most recent price;
/// otherwise, please consider using `updatePriceFeeds`. This method may store the price updates on-chain, if they
/// are more recent than the current stored prices.
///
/// This method requires the caller to pay a fee in wei; the required fee can be computed by calling
/// `getUpdateFee` with the length of the `updateData` array.
///
///
/// @dev Reverts if the transferred fee is not sufficient or the updateData is invalid or there is
/// no update for any of the given `priceIds` within the given time range.
/// @param updateData Array of price update data.
/// @param priceIds Array of price ids.
/// @param minPublishTime minimum acceptable publishTime for the given `priceIds`.
/// @param maxPublishTime maximum acceptable publishTime for the given `priceIds`.
/// @return priceFeeds Array of the price feeds corresponding to the given `priceIds` (with the same order).
function parsePriceFeedUpdates(
bytes[] calldata updateData,
bytes32[] calldata priceIds,
uint64 minPublishTime,
uint64 maxPublishTime
) external payable returns (PythStructs.PriceFeed[] memory priceFeeds);
/// @notice Similar to `parsePriceFeedUpdates` but ensures the updates returned are
/// the first updates published in minPublishTime. That is, if there are multiple updates for a given timestamp,
/// this method will return the first update. This method may store the price updates on-chain, if they
/// are more recent than the current stored prices.
///
///
/// @dev Reverts if the transferred fee is not sufficient or the updateData is invalid or there is
/// no update for any of the given `priceIds` within the given time range and uniqueness condition.
/// @param updateData Array of price update data.
/// @param priceIds Array of price ids.
/// @param minPublishTime minimum acceptable publishTime for the given `priceIds`.
/// @param maxPublishTime maximum acceptable publishTime for the given `priceIds`.
/// @return priceFeeds Array of the price feeds corresponding to the given `priceIds` (with the same order).
function parsePriceFeedUpdatesUnique(
bytes[] calldata updateData,
bytes32[] calldata priceIds,
uint64 minPublishTime,
uint64 maxPublishTime
) external payable returns (PythStructs.PriceFeed[] memory priceFeeds);
}// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.0;
contract PythStructs {
// A price with a degree of uncertainty, represented as a price +- a confidence interval.
//
// The confidence interval roughly corresponds to the standard error of a normal distribution.
// Both the price and confidence are stored in a fixed-point numeric representation,
// `x * (10^expo)`, where `expo` is the exponent.
//
// Please refer to the documentation at https://docs.pyth.network/documentation/pythnet-price-feeds/best-practices for how
// to how this price safely.
struct Price {
// Price
int64 price;
// Confidence interval around the price
uint64 conf;
// Price exponent
int32 expo;
// Unix timestamp describing when the price was published
uint publishTime;
}
// PriceFeed represents a current aggregate price from pyth publisher feeds.
struct PriceFeed {
// The price ID.
bytes32 id;
// Latest available price
Price price;
// Latest available exponentially-weighted moving average price
Price emaPrice;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (access/IAccessControl.sol)
pragma solidity ^0.8.20;
/**
* @dev External interface of AccessControl declared to support ERC-165 detection.
*/
interface IAccessControl {
/**
* @dev The `account` is missing a role.
*/
error AccessControlUnauthorizedAccount(address account, bytes32 neededRole);
/**
* @dev The caller of a function is not the expected one.
*
* NOTE: Don't confuse with {AccessControlUnauthorizedAccount}.
*/
error AccessControlBadConfirmation();
/**
* @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`
*
* `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite
* {RoleAdminChanged} not being emitted signaling this.
*/
event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);
/**
* @dev Emitted when `account` is granted `role`.
*
* `sender` is the account that originated the contract call. This account bears the admin role (for the granted role).
* Expected in cases where the role was granted using the internal {AccessControl-_grantRole}.
*/
event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);
/**
* @dev Emitted when `account` is revoked `role`.
*
* `sender` is the account that originated the contract call:
* - if using `revokeRole`, it is the admin role bearer
* - if using `renounceRole`, it is the role bearer (i.e. `account`)
*/
event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);
/**
* @dev Returns `true` if `account` has been granted `role`.
*/
function hasRole(bytes32 role, address account) external view returns (bool);
/**
* @dev Returns the admin role that controls `role`. See {grantRole} and
* {revokeRole}.
*
* To change a role's admin, use {AccessControl-_setRoleAdmin}.
*/
function getRoleAdmin(bytes32 role) external view returns (bytes32);
/**
* @dev Grants `role` to `account`.
*
* If `account` had not been already granted `role`, emits a {RoleGranted}
* event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*/
function grantRole(bytes32 role, address account) external;
/**
* @dev Revokes `role` from `account`.
*
* If `account` had been granted `role`, emits a {RoleRevoked} event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*/
function revokeRole(bytes32 role, address account) external;
/**
* @dev Revokes `role` from the calling account.
*
* Roles are often managed via {grantRole} and {revokeRole}: this function's
* purpose is to provide a mechanism for accounts to lose their privileges
* if they are compromised (such as when a trusted device is misplaced).
*
* If the calling account had been granted `role`, emits a {RoleRevoked}
* event.
*
* Requirements:
*
* - the caller must be `callerConfirmation`.
*/
function renounceRole(bytes32 role, address callerConfirmation) external;
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)
pragma solidity ^0.8.20;
/**
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
function _contextSuffixLength() internal view virtual returns (uint256) {
return 0;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/introspection/ERC165.sol)
pragma solidity ^0.8.20;
import {IERC165} from "./IERC165.sol";
/**
* @dev Implementation of the {IERC165} interface.
*
* Contracts that want to implement ERC-165 should inherit from this contract and override {supportsInterface} to check
* for the additional interface id that will be supported. For example:
*
* ```solidity
* function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
* return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
* }
* ```
*/
abstract contract ERC165 is IERC165 {
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {
return interfaceId == type(IERC165).interfaceId;
}
}// 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.0.0) (interfaces/IERC5267.sol)
pragma solidity ^0.8.20;
interface IERC5267 {
/**
* @dev MAY be emitted to signal that the domain could have changed.
*/
event EIP712DomainChanged();
/**
* @dev returns the fields and values that describe the domain separator used by this contract for EIP-712
* signature.
*/
function eip712Domain()
external
view
returns (
bytes1 fields,
string memory name,
string memory version,
uint256 chainId,
address verifyingContract,
bytes32 salt,
uint256[] memory extensions
);
}// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
import { IUsdnProtocolTypes } from "./IUsdnProtocolTypes.sol";
/**
* @title IUsdnProtocolActions
* @notice Interface for the USDN Protocol Actions.
*/
interface IUsdnProtocolActions is IUsdnProtocolTypes {
/**
* @notice Initiates an open position action.
* @dev Consult the current oracle middleware for price data format and possible oracle fee.
* Requires `_securityDepositValue` to be included in the transaction value. In case of pending liquidations, this
* function will not initiate the position (`isInitiated_` would be false).
* The user's input for price and leverage is not guaranteed due to the price difference between the initiate and
* validate actions.
* @param amount The amount of assets to deposit.
* @param desiredLiqPrice The desired liquidation price, including the penalty.
* @param userMaxPrice The user's wanted maximum price at which the position can be opened.
* @param userMaxLeverage The user's wanted maximum leverage for the new position.
* @param to The address that will owns of the position.
* @param validator The address that is supposed to validate the opening and receive the security deposit. If not
* an EOA, it must be a contract that implements a `receive` function.
* @param deadline The deadline for initiating the open position.
* @param currentPriceData The price data used for temporary leverage and entry price computations.
* @param previousActionsData The data needed to validate actionable pending actions.
* @return isInitiated_ Whether the position was successfully initiated. If false, the security deposit was refunded
* @return posId_ The unique position identifier. If the position was not initiated, the tick number will be
* `NO_POSITION_TICK`.
*/
function initiateOpenPosition(
uint128 amount,
uint128 desiredLiqPrice,
uint128 userMaxPrice,
uint256 userMaxLeverage,
address to,
address payable validator,
uint256 deadline,
bytes calldata currentPriceData,
PreviousActionsData calldata previousActionsData
) external payable returns (bool isInitiated_, PositionId memory posId_);
/**
* @notice Validates a pending open position action.
* @dev Consult the current oracle middleware for price data format and possible oracle fee.
* It is possible for this operation to change the tick, tick version and index of the position, in which case we emit
* the `LiquidationPriceUpdated` event.
* This function always sends the security deposit to the validator. So users wanting to earn the corresponding
* security deposit must use `validateActionablePendingActions`.
* In case liquidations are pending (`outcome_ == LongActionOutcome.PendingLiquidations`), the pending action will
* not be removed from the queue, and the user will have to try again.
* In case the position was liquidated by this call (`outcome_ == LongActionOutcome.Liquidated`), this function will
* refund the security deposit and remove the pending action from the queue.
* @param validator The address associated with the pending open position. If not an EOA, it must be a contract that
* implements a `receive` function.
* @param openPriceData The price data for the pending open position.
* @param previousActionsData The data needed to validate actionable pending actions.
* @return outcome_ The effect on the pending action (processed, liquidated, or pending liquidations).
* @return posId_ The position ID after validation (or `NO_POSITION_TICK` if liquidated).
*/
function validateOpenPosition(
address payable validator,
bytes calldata openPriceData,
PreviousActionsData calldata previousActionsData
) external payable returns (LongActionOutcome outcome_, PositionId memory posId_);
/**
* @notice Initiates a close position action.
* @dev Consult the current oracle middleware for price data format and possible oracle fee.
* Requires `_securityDepositValue` to be included in the transaction value.
* If the current tick version is greater than the tick version of the position (when it was opened), then the
* position has been liquidated and the transaction will revert.
* In case liquidations are pending (`outcome_ == LongActionOutcome.PendingLiquidations`), the pending action will
* not be removed from the queue, and the user will have to try again.
* In case the position was liquidated by this call (`outcome_ == LongActionOutcome.Liquidated`), this function will
* refund the security deposit and remove the pending action from the queue.
* The user's input for the price is not guaranteed due to the price difference between the initiate and validate
* actions.
* @param posId The unique identifier of the position to close.
* @param amountToClose The amount of collateral to remove.
* @param userMinPrice The user's wanted minimum price for closing the position.
* @param to The address that will receive the assets.
* @param validator The address that is supposed to validate the closing and receive the security deposit. If not an
* EOA, it must be a contract that implements a `receive` function.
* @param deadline The deadline for initiating the close position.
* @param currentPriceData The price data for temporary calculations.
* @param previousActionsData The data needed to validate actionable pending actions.
* @param delegationSignature Optional EIP712 signature for delegated action.
* @return outcome_ The effect on the pending action (processed, liquidated, or pending liquidations).
*/
function initiateClosePosition(
PositionId calldata posId,
uint128 amountToClose,
uint256 userMinPrice,
address to,
address payable validator,
uint256 deadline,
bytes calldata currentPriceData,
PreviousActionsData calldata previousActionsData,
bytes calldata delegationSignature
) external payable returns (LongActionOutcome outcome_);
/**
* @notice Validates a pending close position action.
* @dev Consult the current oracle middleware for price data format and possible oracle fee.
* This function calculates the final exit price, determines the profit of the long position, and performs the
* payout.
* This function always sends the security deposit to the validator. So users wanting to earn the corresponding
* security deposit must use `validateActionablePendingActions`.
* In case liquidations are pending (`outcome_ == LongActionOutcome.PendingLiquidations`),
* the pending action will not be removed from the queue, and the user will have to try again.
* In case the position was liquidated by this call (`outcome_ == LongActionOutcome.Liquidated`),
* this function will refund the security deposit and remove the pending action from the queue.
* @param validator The address associated with the pending close position. If not an EOA, it must be a contract
* that implements a `receive` function.
* @param closePriceData The price data for the pending close position action.
* @param previousActionsData The data required to validate actionable pending actions.
* @return outcome_ The outcome of the action (processed, liquidated, or pending liquidations).
*/
function validateClosePosition(
address payable validator,
bytes calldata closePriceData,
PreviousActionsData calldata previousActionsData
) external payable returns (LongActionOutcome outcome_);
/**
* @notice Initiates a deposit of assets into the vault to mint USDN.
* @dev Consult the current oracle middleware for price data format and possible oracle fee.
* Requires `_securityDepositValue` to be included in the transaction value.
* In case liquidations are pending, this function might not initiate the deposit, and `success_` would be false.
* The user's input for the shares is not guaranteed due to the price difference between the initiate and validate
* actions.
* @param amount The amount of assets to deposit.
* @param sharesOutMin The minimum amount of USDN shares to receive.
* @param to The address that will receive the USDN tokens.
* @param validator The address that is supposed to validate the deposit and receive the security deposit. If not an
* EOA, it must be a contract that implements a `receive` function.
* @param deadline The deadline for initiating the deposit.
* @param currentPriceData The current price data.
* @param previousActionsData The data required to validate actionable pending actions.
* @return success_ Indicates whether the deposit was successfully initiated.
*/
function initiateDeposit(
uint128 amount,
uint256 sharesOutMin,
address to,
address payable validator,
uint256 deadline,
bytes calldata currentPriceData,
PreviousActionsData calldata previousActionsData
) external payable returns (bool success_);
/**
* @notice Validates a pending deposit action.
* @dev Consult the current oracle middleware for price data format and possible oracle fee.
* This function always sends the security deposit to the validator. So users wanting to earn the corresponding
* security deposit must use `validateActionablePendingActions`.
* If liquidations are pending, the validation may fail, and `success_` would be false.
* @param validator The address associated with the pending deposit action. If not an EOA, it must be a contract
* that implements a `receive` function.
* @param depositPriceData The price data for the pending deposit action.
* @param previousActionsData The data required to validate actionable pending actions.
* @return success_ Indicates whether the deposit was successfully validated.
*/
function validateDeposit(
address payable validator,
bytes calldata depositPriceData,
PreviousActionsData calldata previousActionsData
) external payable returns (bool success_);
/**
* @notice Initiates a withdrawal of assets from the vault using USDN tokens.
* @dev Consult the current oracle middleware for price data format and possible oracle fee.
* Requires `_securityDepositValue` to be included in the transaction value.
* Note that in case liquidations are pending, this function might not initiate the withdrawal, and `success_` would
* be false.
* The user's input for the minimum amount is not guaranteed due to the price difference between the initiate and
* validate actions.
* @param usdnShares The amount of USDN shares to burn.
* @param amountOutMin The minimum amount of assets to receive.
* @param to The address that will receive the assets.
* @param validator The address that is supposed to validate the withdrawal and receive the security deposit. If not
* an EOA, it must be a contract that implements a `receive` function.
* @param deadline The deadline for initiating the withdrawal.
* @param currentPriceData The current price data.
* @param previousActionsData The data required to validate actionable pending actions.
* @return success_ Indicates whether the withdrawal was successfully initiated.
*/
function initiateWithdrawal(
uint152 usdnShares,
uint256 amountOutMin,
address to,
address payable validator,
uint256 deadline,
bytes calldata currentPriceData,
PreviousActionsData calldata previousActionsData
) external payable returns (bool success_);
/**
* @notice Validates a pending withdrawal action.
* @dev Consult the current oracle middleware for price data format and possible oracle fee.
* This function always sends the security deposit to the validator. So users wanting to earn the corresponding
* security deposit must use `validateActionablePendingActions`.
* In case liquidations are pending, this function might not validate the withdrawal, and `success_` would be false.
* @param validator The address associated with the pending withdrawal action. If not an EOA, it must be a contract
* that implements a `receive` function.
* @param withdrawalPriceData The price data for the pending withdrawal action.
* @param previousActionsData The data required to validate actionable pending actions.
* @return success_ Indicates whether the withdrawal was successfully validated.
*/
function validateWithdrawal(
address payable validator,
bytes calldata withdrawalPriceData,
PreviousActionsData calldata previousActionsData
) external payable returns (bool success_);
/**
* @notice Liquidates positions based on the provided asset price.
* @dev Consult the current oracle middleware for price data format and possible oracle fee.
* Each tick is liquidated in constant time. The tick version is incremented for each liquidated tick.
* @param currentPriceData The price data.
* @return liquidatedTicks_ Information about the liquidated ticks.
*/
function liquidate(bytes calldata currentPriceData)
external
payable
returns (LiqTickInfo[] memory liquidatedTicks_);
/**
* @notice Manually validates actionable pending actions.
* @dev Consult the current oracle middleware for price data format and possible oracle fee.
* The timestamp for each pending action is calculated by adding the `OracleMiddleware.validationDelay` to its
* initiation timestamp.
* @param previousActionsData The data required to validate actionable pending actions.
* @param maxValidations The maximum number of actionable pending actions to validate. At least one validation will
* be performed.
* @return validatedActions_ The number of successfully validated actions.
*/
function validateActionablePendingActions(PreviousActionsData calldata previousActionsData, uint256 maxValidations)
external
payable
returns (uint256 validatedActions_);
/**
* @notice Transfers the ownership of a position to another address.
* @dev This function reverts if the caller is not the position owner, if the position does not exist, or if the new
* owner's address is the zero address.
* If the new owner is a contract that implements the `IOwnershipCallback` interface, its `ownershipCallback`
* function will be invoked after the transfer.
* @param posId The unique identifier of the position.
* @param newOwner The address of the new position owner.
* @param delegationSignature An optional EIP712 signature to authorize the transfer on the owner's behalf.
*/
function transferPositionOwnership(PositionId calldata posId, address newOwner, bytes calldata delegationSignature)
external;
/**
* @notice Retrieves the domain separator used in EIP-712 signatures.
* @return domainSeparatorV4_ The domain separator compliant with EIP-712.
*/
function domainSeparatorV4() external view returns (bytes32 domainSeparatorV4_);
}// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
/**
* @title IUsdnProtocolCore
* @notice Interface for the core layer of the USDN protocol.
*/
interface IUsdnProtocolCore {
/**
* @notice Computes the predicted funding value since the last state update for the specified timestamp.
* @dev The funding value, when multiplied by the long trading exposure, represents the asset balance to be
* transferred to the vault side, or to the long side if the value is negative.
* Reverts with `UsdnProtocolTimestampTooOld` if the given timestamp is older than the last state update.
* @param timestamp The timestamp to use for the computation.
* @return funding_ The funding magnitude (with `FUNDING_RATE_DECIMALS` decimals) since the last update timestamp.
* @return fundingPerDay_ The funding rate per day (with `FUNDING_RATE_DECIMALS` decimals).
* @return oldLongExpo_ The long trading exposure recorded at the last state update.
*/
function funding(uint128 timestamp)
external
view
returns (int256 funding_, int256 fundingPerDay_, int256 oldLongExpo_);
/**
* @notice Initializes the protocol by making an initial deposit and creating the first long position.
* @dev This function can only be called once. No other user actions can be performed until the protocol
* is initialized.
* @param depositAmount The amount of assets to deposit.
* @param longAmount The amount of assets for the long position.
* @param desiredLiqPrice The desired liquidation price for the long position, excluding the liquidation penalty.
* @param currentPriceData The encoded current price data.
*/
function initialize(
uint128 depositAmount,
uint128 longAmount,
uint128 desiredLiqPrice,
bytes calldata currentPriceData
) external payable;
}// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
import { HugeUint } from "@smardex-solidity-libraries-1/HugeUint.sol";
import { IUsdnProtocolTypes } from "./IUsdnProtocolTypes.sol";
/**
* @title IUsdnProtocolLong
* @notice Interface for the long side layer of the USDN protocol.
*/
interface IUsdnProtocolLong is IUsdnProtocolTypes {
/**
* @notice Gets the value of the lowest usable tick, taking into account the tick spacing.
* @dev Note that the effective minimum tick of a newly open long position also depends on the minimum allowed
* leverage value and the current value of the liquidation price multiplier.
* @return tick_ The lowest usable tick.
*/
function minTick() external view returns (int24 tick_);
/**
* @notice Gets the liquidation price from a desired one by taking into account the tick rounding.
* @param desiredLiqPriceWithoutPenalty The desired liquidation price without the penalty.
* @param assetPrice The current price of the asset.
* @param longTradingExpo The trading exposition of the long side.
* @param accumulator The liquidation multiplier accumulator.
* @param tickSpacing The tick spacing.
* @param liquidationPenalty The liquidation penalty set on the tick.
* @return liqPrice_ The new liquidation price without the penalty.
*/
function getLiqPriceFromDesiredLiqPrice(
uint128 desiredLiqPriceWithoutPenalty,
uint256 assetPrice,
uint256 longTradingExpo,
HugeUint.Uint512 memory accumulator,
int24 tickSpacing,
uint24 liquidationPenalty
) external view returns (uint128 liqPrice_);
/**
* @notice Gets the value of a long position when the asset price is equal to the given price, at the given
* timestamp.
* @dev If the current price is smaller than the liquidation price of the position without the liquidation penalty,
* then the value of the position is negative.
* @param posId The unique position identifier.
* @param price The asset price.
* @param timestamp The timestamp of the price.
* @return value_ The position value in assets.
*/
function getPositionValue(PositionId calldata posId, uint128 price, uint128 timestamp)
external
view
returns (int256 value_);
/**
* @notice Gets the tick number corresponding to a given price, accounting for funding effects.
* @dev Uses the stored parameters for calculation.
* @param price The asset price.
* @return tick_ The tick number, a multiple of the tick spacing.
*/
function getEffectiveTickForPrice(uint128 price) external view returns (int24 tick_);
/**
* @notice Gets the tick number corresponding to a given price, accounting for funding effects.
* @param price The asset price.
* @param assetPrice The current price of the asset.
* @param longTradingExpo The trading exposition of the long side.
* @param accumulator The liquidation multiplier accumulator.
* @param tickSpacing The tick spacing.
* @return tick_ The tick number, a multiple of the tick spacing.
*/
function getEffectiveTickForPrice(
uint128 price,
uint256 assetPrice,
uint256 longTradingExpo,
HugeUint.Uint512 memory accumulator,
int24 tickSpacing
) external view returns (int24 tick_);
/**
* @notice Retrieves the liquidation penalty assigned to the given tick if there are positions in it, otherwise
* retrieve the current setting value from storage.
* @param tick The tick number.
* @return liquidationPenalty_ The liquidation penalty, in tick spacing units.
*/
function getTickLiquidationPenalty(int24 tick) external view returns (uint24 liquidationPenalty_);
/**
* @notice Gets a long position identified by its tick, tick version and index.
* @param posId The unique position identifier.
* @return pos_ The position data.
* @return liquidationPenalty_ The liquidation penalty for that position.
*/
function getLongPosition(PositionId calldata posId)
external
view
returns (Position memory pos_, uint24 liquidationPenalty_);
/**
* @notice Gets the predicted value of the long balance for the given asset price and timestamp.
* @dev The effects of the funding and any PnL of the long positions since the last contract state
* update is taken into account, as well as the fees. If the provided timestamp is older than the last state
* update, the function reverts with `UsdnProtocolTimestampTooOld`. The value cannot be below 0.
* @param currentPrice The given asset price.
* @param timestamp The timestamp corresponding to the given price.
* @return available_ The long balance value in assets.
*/
function longAssetAvailableWithFunding(uint128 currentPrice, uint128 timestamp)
external
view
returns (uint256 available_);
/**
* @notice Gets the predicted value of the long trading exposure for the given asset price and timestamp.
* @dev The effects of the funding and any profit or loss of the long positions since the last contract state
* update is taken into account. If the provided timestamp is older than the last state update, the function reverts
* with `UsdnProtocolTimestampTooOld`. The value cannot be below 0.
* @param currentPrice The given asset price.
* @param timestamp The timestamp corresponding to the given price.
* @return expo_ The long trading exposure value in assets.
*/
function longTradingExpoWithFunding(uint128 currentPrice, uint128 timestamp)
external
view
returns (uint256 expo_);
}// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
/**
* @title IUsdnProtocolVault
* @notice Interface for the vault layer of the USDN protocol.
*/
interface IUsdnProtocolVault {
/**
* @notice Calculates the predicted USDN token price based on the given asset price and timestamp.
* @dev The effects of the funding and the PnL of the long positions since the last contract state update are taken
* into account.
* @param currentPrice The current or predicted asset price.
* @param timestamp The timestamp corresponding to `currentPrice`.
* @return price_ The predicted USDN token price.
*/
function usdnPrice(uint128 currentPrice, uint128 timestamp) external view returns (uint256 price_);
/**
* @notice Calculates the USDN token price based on the given asset price at the current timestamp.
* @dev The effects of the funding and the PnL of the long positions since the last contract state update are taken
* into account.
* @param currentPrice The asset price at `block.timestamp`.
* @return price_ The calculated USDN token price.
*/
function usdnPrice(uint128 currentPrice) external view returns (uint256 price_);
/**
* @notice Gets the amount of assets in the vault for the given asset price and timestamp.
* @dev The effects of the funding, the PnL of the long positions and the accumulated fees since the last contract
* state update are taken into account, but not liquidations. If the provided timestamp is older than the last
* state update, the function reverts with `UsdnProtocolTimestampTooOld`.
* @param currentPrice The current or predicted asset price.
* @param timestamp The timestamp corresponding to `currentPrice` (must not be earlier than `_lastUpdateTimestamp`).
* @return available_ The available vault balance (cannot be less than 0).
*/
function vaultAssetAvailableWithFunding(uint128 currentPrice, uint128 timestamp)
external
view
returns (uint256 available_);
}// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.0;
/// @title IPythEvents contains the events that Pyth contract emits.
/// @dev This interface can be used for listening to the updates for off-chain and testing purposes.
interface IPythEvents {
/// @dev Emitted when the price feed with `id` has received a fresh update.
/// @param id The Pyth Price Feed ID.
/// @param publishTime Publish time of the given price update.
/// @param price Price of the given price update.
/// @param conf Confidence interval of the given price update.
event PriceFeedUpdate(
bytes32 indexed id,
uint64 publishTime,
int64 price,
uint64 conf
);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/introspection/IERC165.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC-165 standard, as defined in the
* https://eips.ethereum.org/EIPS/eip-165[ERC].
*
* Implementers can declare support of contract interfaces, which can then be
* queried by others ({ERC165Checker}).
*
* For an implementation, see {ERC165}.
*/
interface IERC165 {
/**
* @dev Returns true if this contract implements the interface defined by
* `interfaceId`. See the corresponding
* https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[ERC section]
* to learn more about how these ids are created.
*
* This function call must use less than 30 000 gas.
*/
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}{
"remappings": [
"@chainlink/=dependencies/@chainlink-1.2.0/",
"@openzeppelin/contracts-upgradeable/=dependencies/@openzeppelin-contracts-upgradeable-5.1.0/",
"@openzeppelin/contracts/=dependencies/@openzeppelin-contracts-5.1.0/",
"@pythnetwork/pyth-sdk-solidity/=dependencies/@pythnetwork-pyth-sdk-solidity-3.1.0/",
"@redstone-finance/evm-connector/=dependencies/@redstone-finance-evm-connector-0.6.2/",
"@smardex-solidity-libraries-1/=dependencies/@smardex-solidity-libraries-1.0.1/src/",
"@uniswap/permit2/=dependencies/@uniswap-permit2-1.0.0/",
"forge-std/=dependencies/forge-std-1.9.4/src/",
"openzeppelin-foundry-upgrades/=dependencies/openzeppelin-foundry-upgrades-0.3.6/src/",
"solady/src/=dependencies/solady-0.0.228/src/",
"@chainlink-1.2.0/=dependencies/@chainlink-1.2.0/",
"@openzeppelin-contracts-5.1.0/=dependencies/@openzeppelin-contracts-5.1.0/",
"@openzeppelin-contracts-upgradeable-5.1.0/=dependencies/@openzeppelin-contracts-upgradeable-5.1.0/",
"@pythnetwork-pyth-sdk-solidity-3.1.0/=dependencies/@pythnetwork-pyth-sdk-solidity-3.1.0/",
"@redstone-finance-evm-connector-0.6.2/=dependencies/@redstone-finance-evm-connector-0.6.2/contracts/",
"@smardex-solidity-libraries-1.0.1/=dependencies/@smardex-solidity-libraries-1.0.1/src/",
"@uniswap-permit2-1.0.0/=dependencies/@uniswap-permit2-1.0.0/src/",
"ds-test/=dependencies/openzeppelin-foundry-upgrades-0.3.6/lib/solidity-stringutils/lib/ds-test/src/",
"forge-std-1.9.4/=dependencies/forge-std-1.9.4/src/",
"forge-std-1/=dependencies/@smardex-solidity-libraries-1.0.1/dependencies/forge-std-1.9.4/src/",
"openzeppelin-foundry-upgrades-0.3.6/=dependencies/openzeppelin-foundry-upgrades-0.3.6/src/",
"solady-0.0.228/=dependencies/solady-0.0.228/src/",
"solidity-stringutils/=dependencies/openzeppelin-foundry-upgrades-0.3.6/lib/solidity-stringutils/",
"solmate/=dependencies/@uniswap-permit2-1.0.0/lib/solmate/"
],
"optimizer": {
"enabled": true,
"runs": 20000
},
"metadata": {
"useLiteralContent": false,
"bytecodeHash": "ipfs",
"appendCBOR": true
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"evmVersion": "cancun",
"viaIR": false,
"libraries": {}
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"internalType":"address","name":"pythContract","type":"address"},{"internalType":"bytes32","name":"pythPriceID","type":"bytes32"},{"internalType":"address","name":"chainlinkPriceFeed","type":"address"},{"internalType":"address","name":"wstETH","type":"address"},{"internalType":"uint256","name":"chainlinkTimeElapsedLimit","type":"uint256"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AccessControlBadConfirmation","type":"error"},{"inputs":[{"internalType":"uint48","name":"schedule","type":"uint48"}],"name":"AccessControlEnforcedDefaultAdminDelay","type":"error"},{"inputs":[],"name":"AccessControlEnforcedDefaultAdminRules","type":"error"},{"inputs":[{"internalType":"address","name":"defaultAdmin","type":"address"}],"name":"AccessControlInvalidDefaultAdmin","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"bytes32","name":"neededRole","type":"bytes32"}],"name":"AccessControlUnauthorizedAccount","type":"error"},{"inputs":[],"name":"OracleMiddlewareConfRatioTooHigh","type":"error"},{"inputs":[],"name":"OracleMiddlewareIncorrectFee","type":"error"},{"inputs":[],"name":"OracleMiddlewareInvalidLowLatencyDelay","type":"error"},{"inputs":[],"name":"OracleMiddlewareInvalidPenaltyBps","type":"error"},{"inputs":[{"internalType":"uint64","name":"newDelay","type":"uint64"}],"name":"OracleMiddlewareInvalidRecentPriceDelay","type":"error"},{"inputs":[],"name":"OracleMiddlewareInvalidRoundId","type":"error"},{"inputs":[{"internalType":"uint256","name":"timestamp","type":"uint256"}],"name":"OracleMiddlewarePriceTooOld","type":"error"},{"inputs":[{"internalType":"uint256","name":"timestamp","type":"uint256"}],"name":"OracleMiddlewarePriceTooRecent","type":"error"},{"inputs":[{"internalType":"uint256","name":"fee","type":"uint256"}],"name":"OracleMiddlewarePythFeeSafeguard","type":"error"},{"inputs":[{"internalType":"int32","name":"expo","type":"int32"}],"name":"OracleMiddlewarePythPositiveExponent","type":"error"},{"inputs":[],"name":"OracleMiddlewareRedstoneSafeguard","type":"error"},{"inputs":[{"internalType":"address","name":"to","type":"address"}],"name":"OracleMiddlewareTransferFailed","type":"error"},{"inputs":[],"name":"OracleMiddlewareTransferToZeroAddress","type":"error"},{"inputs":[{"internalType":"int256","name":"price","type":"int256"}],"name":"OracleMiddlewareWrongPrice","type":"error"},{"inputs":[{"internalType":"uint8","name":"bits","type":"uint8"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"SafeCastOverflowedUintDowncast","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"newConfRatio","type":"uint256"}],"name":"ConfRatioUpdated","type":"event"},{"anonymous":false,"inputs":[],"name":"DefaultAdminDelayChangeCanceled","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint48","name":"newDelay","type":"uint48"},{"indexed":false,"internalType":"uint48","name":"effectSchedule","type":"uint48"}],"name":"DefaultAdminDelayChangeScheduled","type":"event"},{"anonymous":false,"inputs":[],"name":"DefaultAdminTransferCanceled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"newAdmin","type":"address"},{"indexed":false,"internalType":"uint48","name":"acceptSchedule","type":"uint48"}],"name":"DefaultAdminTransferScheduled","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint16","name":"newLowLatencyDelay","type":"uint16"}],"name":"LowLatencyDelayUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint16","name":"newPenaltyBps","type":"uint16"}],"name":"PenaltyBpsUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint64","name":"newDelay","type":"uint64"}],"name":"PythRecentPriceDelayUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint48","name":"newDelay","type":"uint48"}],"name":"RedstoneRecentPriceDelayUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"previousAdminRole","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"newAdminRole","type":"bytes32"}],"name":"RoleAdminChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleGranted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleRevoked","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"newTimeElapsedLimit","type":"uint256"}],"name":"TimeElapsedLimitUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"newValidationDelay","type":"uint256"}],"name":"ValidationDelayUpdated","type":"event"},{"inputs":[],"name":"ADMIN_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"BPS_DIVISOR","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DEFAULT_ADMIN_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_CONF_RATIO","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PRICE_TOO_OLD","outputs":[{"internalType":"int256","name":"","type":"int256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"acceptDefaultAdminTransfer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newAdmin","type":"address"}],"name":"beginDefaultAdminTransfer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"cancelDefaultAdminTransfer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint48","name":"newDelay","type":"uint48"}],"name":"changeDefaultAdminDelay","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"defaultAdmin","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"defaultAdminDelay","outputs":[{"internalType":"uint48","name":"","type":"uint48"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"defaultAdminDelayIncreaseWait","outputs":[{"internalType":"uint48","name":"","type":"uint48"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getChainlinkDecimals","outputs":[{"internalType":"uint256","name":"decimals_","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getChainlinkTimeElapsedLimit","outputs":[{"internalType":"uint256","name":"limit_","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getConfRatioBps","outputs":[{"internalType":"uint16","name":"ratio_","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getDecimals","outputs":[{"internalType":"uint8","name":"decimals_","type":"uint8"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"getLowLatencyDelay","outputs":[{"internalType":"uint16","name":"delay_","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getPriceFeed","outputs":[{"internalType":"contract AggregatorV3Interface","name":"priceFeed_","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getPyth","outputs":[{"internalType":"contract IPyth","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getPythFeedId","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getPythRecentPriceDelay","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"name":"getRoleAdmin","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getValidationDelay","outputs":[{"internalType":"uint256","name":"delay_","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"grantRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"hasRole","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"actionId","type":"bytes32"},{"internalType":"uint128","name":"targetTimestamp","type":"uint128"},{"internalType":"enum IUsdnProtocolTypes.ProtocolAction","name":"action","type":"uint8"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"parseAndValidatePrice","outputs":[{"components":[{"internalType":"uint256","name":"price","type":"uint256"},{"internalType":"uint256","name":"neutralPrice","type":"uint256"},{"internalType":"uint256","name":"timestamp","type":"uint256"}],"internalType":"struct PriceInfo","name":"","type":"tuple"}],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"pendingDefaultAdmin","outputs":[{"internalType":"address","name":"newAdmin","type":"address"},{"internalType":"uint48","name":"schedule","type":"uint48"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pendingDefaultAdminDelay","outputs":[{"internalType":"uint48","name":"newDelay","type":"uint48"},{"internalType":"uint48","name":"schedule","type":"uint48"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"renounceRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"revokeRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"rollbackDefaultAdminDelay","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"newTimeElapsedLimit","type":"uint256"}],"name":"setChainlinkTimeElapsedLimit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint16","name":"newConfRatio","type":"uint16"}],"name":"setConfRatio","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint16","name":"newLowLatencyDelay","type":"uint16"},{"internalType":"contract IUsdnProtocol","name":"usdnProtocol","type":"address"}],"name":"setLowLatencyDelay","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint64","name":"newDelay","type":"uint64"}],"name":"setPythRecentPriceDelay","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"newValidationDelay","type":"uint256"}],"name":"setValidationDelay","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"data","type":"bytes"},{"internalType":"enum IUsdnProtocolTypes.ProtocolAction","name":"","type":"uint8"}],"name":"validationCost","outputs":[{"internalType":"uint256","name":"result_","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"}],"name":"withdrawEther","outputs":[],"stateMutability":"nonpayable","type":"function"}]Contract Creation Code
6101006040525f80546001600160401b031916602d1790556018600555600680546304b00fa063ffffffff1990911617905534801561003c575f80fd5b506040516140cd3803806140cd83398101604081905261005b91610250565b6001600160a01b0380861660a0526080859052831660c0526001819055848484835f33806100a257604051636116401160e11b81525f600482015260240160405180910390fd5b600380546001600160d01b0316600160d01b65ffffffffffff8516021790556100cb5f8261011b565b5050506100fe7fa49807205ce4d355092ef5a8a18f56e8913cf4a201fbe287825b095693c217753361011b60201b60201c565b5050506001600160a01b0390931660e052506102a3945050505050565b5f82610177575f6101346004546001600160a01b031690565b6001600160a01b03161461015b57604051631fe1e13d60e11b815260040160405180910390fd5b600480546001600160a01b0319166001600160a01b0384161790555b610181838361018a565b90505b92915050565b5f8281526002602090815260408083206001600160a01b038516845290915281205460ff1661022e575f8381526002602090815260408083206001600160a01b03861684529091529020805460ff191660011790556101e63390565b6001600160a01b0316826001600160a01b0316847f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a4506001610184565b505f610184565b80516001600160a01b038116811461024b575f80fd5b919050565b5f805f805f60a08688031215610264575f80fd5b61026d86610235565b94506020860151935061028260408701610235565b925061029060608701610235565b9150608086015190509295509295909350565b60805160a05160c05160e051613d9c6103315f395f6112dd01525f81816105ff01528181610f41015281816124f2015281816127fc01528181612fba015281816130b201526131ef01525f818161049f015281816115e90152818161268f01528181612b3b01528181612c840152612d8a01525f8181610704015281816126540152612a7b0152613d9c5ff3fe6080604052600436106102ab575f3560e01c8063986e189011610165578063be7756dd116100c6578063cf6eefb71161007c578063d602b9fd11610062578063d602b9fd14610818578063f0141d841461082c578063f211abcc14610847575f80fd5b8063cf6eefb7146107a1578063d547741f146107f9575f80fd5b8063c9371327116100ac578063c93713271461075b578063cc8463c814610779578063cefc14291461078d575f80fd5b8063be7756dd14610728578063c4fa8bf41461073c575f80fd5b8063ab8b2f341161011b578063af933b5711610101578063af933b57146106b0578063b1529fbf146106cf578063b222ec71146106f6575f80fd5b8063ab8b2f3414610669578063af2d32bb1461067d575f80fd5b80639e87a5cd1161014b5780639e87a5cd146105f1578063a1eda53c14610623578063a217fddf14610656575f80fd5b8063986e1890146105be57806398793ff9146105dd575f80fd5b8063634e93da1161020f57806384ef8ffc116101c557806391d14854116101ab57806391d148541461053657806393f22b9d1461058757806394218074146105a6575f80fd5b806384ef8ffc146104f85780638da5cb5b14610522575f80fd5b806375b238fc116101f557806375b238fc1461045e57806378399b0b14610491578063831bcbf3146104e4575f80fd5b8063634e93da14610420578063649a5ec71461043f575f80fd5b8063248a9ca31161026457806336568abe1161024a57806336568abe146103c35780633f5bd8a9146103e257806363174c7c14610401575f80fd5b8063248a9ca3146103685780632f2ff15d146103a4575f80fd5b80630aa6220b116102945780630aa6220b1461030b5780630b6313a614610321578063191fe1ed14610340575f80fd5b806301ffc9a7146102af578063022d63fb146102e3575b5f80fd5b3480156102ba575f80fd5b506102ce6102c9366004613267565b61087c565b60405190151581526020015b60405180910390f35b3480156102ee575f80fd5b50620697805b60405165ffffffffffff90911681526020016102da565b348015610316575f80fd5b5061031f6108d7565b005b34801561032c575f80fd5b5061031f61033b3660046132dd565b6108ec565b34801561034b575f80fd5b5061035561271081565b60405161ffff90911681526020016102da565b348015610373575f80fd5b50610396610382366004613312565b5f9081526002602052604090206001015490565b6040519081526020016102da565b3480156103af575f80fd5b5061031f6103be366004613329565b610a83565b3480156103ce575f80fd5b5061031f6103dd366004613329565b610ac8565b3480156103ed575f80fd5b506103966103fc36600461339f565b610bd2565b34801561040c575f80fd5b5061031f61041b366004613404565b610bf6565b34801561042b575f80fd5b5061031f61043a36600461341f565b610d31565b34801561044a575f80fd5b5061031f61045936600461343a565b610d44565b348015610469575f80fd5b506103967fa49807205ce4d355092ef5a8a18f56e8913cf4a201fbe287825b095693c2177581565b34801561049c575f80fd5b507f00000000000000000000000000000000000000000000000000000000000000005b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016102da565b3480156104ef575f80fd5b50600554610396565b348015610503575f80fd5b5060045473ffffffffffffffffffffffffffffffffffffffff166104bf565b34801561052d575f80fd5b506104bf610d57565b348015610541575f80fd5b506102ce610550366004613329565b5f91825260026020908152604080842073ffffffffffffffffffffffffffffffffffffffff93909316845291905290205460ff1690565b348015610592575f80fd5b5061031f6105a136600461345f565b610d7c565b3480156105b1575f80fd5b5060065461ffff16610355565b3480156105c9575f80fd5b5061031f6105d8366004613312565b610e55565b3480156105e8575f80fd5b50610355610eb4565b3480156105fc575f80fd5b507f00000000000000000000000000000000000000000000000000000000000000006104bf565b34801561062e575f80fd5b50610637610ec4565b6040805165ffffffffffff9384168152929091166020830152016102da565b348015610661575f80fd5b506103965f81565b348015610674575f80fd5b50610396610f3e565b348015610688575f80fd5b506103967f800000000000000000000000000000000000000000000000000000000000000081565b3480156106bb575f80fd5b5061031f6106ca36600461341f565b610fd4565b3480156106da575f80fd5b505f5460405167ffffffffffffffff90911681526020016102da565b348015610701575f80fd5b507f0000000000000000000000000000000000000000000000000000000000000000610396565b348015610733575f80fd5b50600154610396565b348015610747575f80fd5b5061031f610756366004613312565b6110fe565b348015610766575f80fd5b5060065462010000900461ffff16610355565b348015610784575f80fd5b506102f461115d565b348015610798575f80fd5b5061031f6111fa565b3480156107ac575f80fd5b506003546040805173ffffffffffffffffffffffffffffffffffffffff831681527401000000000000000000000000000000000000000090920465ffffffffffff166020830152016102da565b348015610804575f80fd5b5061031f610813366004613329565b611256565b348015610823575f80fd5b5061031f611297565b348015610837575f80fd5b50604051601281526020016102da565b61085a610855366004613495565b6112a9565b60408051825181526020808401519082015291810151908201526060016102da565b5f7fffffffff0000000000000000000000000000000000000000000000000000000082167f314987860000000000000000000000000000000000000000000000000000000014806108d157506108d1826113da565b92915050565b5f6108e181611470565b6108e961147a565b50565b7fa49807205ce4d355092ef5a8a18f56e8913cf4a201fbe287825b095693c2177561091681611470565b6115188361ffff161115610956576040517f14a7fd0f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8173ffffffffffffffffffffffffffffffffffffffff16634aee7f9f6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561099f573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906109c39190613501565b6fffffffffffffffffffffffffffffffff168361ffff161015610a12576040517f14a7fd0f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600680547fffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000ffff166201000061ffff8616908102919091179091556040519081527fdd0f8a3132b698ac11a3f9bcd9f8e2799f39bc4f08b761827d9e24e0ace6edd59060200160405180910390a1505050565b81610aba576040517f3fc3c27a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610ac48282611486565b5050565b81158015610af0575060045473ffffffffffffffffffffffffffffffffffffffff8281169116145b15610bc85760035473ffffffffffffffffffffffffffffffffffffffff81169074010000000000000000000000000000000000000000900465ffffffffffff1681151580610b44575065ffffffffffff8116155b80610b5757504265ffffffffffff821610155b15610b9d576040517f19ca5ebb00000000000000000000000000000000000000000000000000000000815265ffffffffffff821660048201526024015b60405180910390fd5b5050600380547fffffffffffff000000000000ffffffffffffffffffffffffffffffffffffffff1690555b610ac482826114b0565b5f610bdd8484611509565b15610bef57610bec8484611528565b90505b9392505050565b7fa49807205ce4d355092ef5a8a18f56e8913cf4a201fbe287825b095693c21775610c2081611470565b600a8267ffffffffffffffff161015610c71576040517fd44ff2b100000000000000000000000000000000000000000000000000000000815267ffffffffffffffff83166004820152602401610b94565b6102588267ffffffffffffffff161115610cc3576040517fd44ff2b100000000000000000000000000000000000000000000000000000000815267ffffffffffffffff83166004820152602401610b94565b5f80547fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000001667ffffffffffffffff84169081179091556040519081527f241c93c63d73e9b5365f977482dd8048b228ee742453ca59b21448b4a6a4b5b8906020015b60405180910390a15050565b5f610d3b81611470565b610ac482611665565b5f610d4e81611470565b610ac4826116e4565b5f610d7760045473ffffffffffffffffffffffffffffffffffffffff1690565b905090565b7fa49807205ce4d355092ef5a8a18f56e8913cf4a201fbe287825b095693c21775610da681611470565b610db36127106002613549565b61ffff168261ffff161115610df4576040517fc18fce8e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600680547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00001661ffff84169081179091556040519081527f444babe03f2cf1ecf2d92f386b4b36c9bc9a3cb1874b3abe1c5d5a55291395a890602001610d25565b7fa49807205ce4d355092ef5a8a18f56e8913cf4a201fbe287825b095693c21775610e7f81611470565b60058290556040518281527f42e08bed13b165e657b77ec405fe78a7cc5893381b77bf557b457f00ee8fa10a90602001610d25565b610ec16127106002613549565b81565b6004545f907a010000000000000000000000000000000000000000000000000000900465ffffffffffff168015158015610f0657504265ffffffffffff821610155b610f11575f80610f36565b60045474010000000000000000000000000000000000000000900465ffffffffffff16815b915091509091565b5f7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015610fa8573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610fcc9190613566565b60ff16905090565b7fa49807205ce4d355092ef5a8a18f56e8913cf4a201fbe287825b095693c21775610ffe81611470565b73ffffffffffffffffffffffffffffffffffffffff821661104b576040517f80bc7b9100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f8273ffffffffffffffffffffffffffffffffffffffff16476040515f6040518083038185875af1925050503d805f81146110a1576040519150601f19603f3d011682016040523d82523d5f602084013e6110a6565b606091505b50509050806110f9576040517fb5ed065300000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff84166004820152602401610b94565b505050565b7fa49807205ce4d355092ef5a8a18f56e8913cf4a201fbe287825b095693c2177561112881611470565b60018290556040518281527fb253a4f7edf0a3926be13935f7bfcce96181bceff9941569c8124dc09d0ecc6990602001610d25565b6004545f907a010000000000000000000000000000000000000000000000000000900465ffffffffffff16801515801561119e57504265ffffffffffff8216105b6111d0576003547a010000000000000000000000000000000000000000000000000000900465ffffffffffff166111f4565b60045474010000000000000000000000000000000000000000900465ffffffffffff165b91505090565b60035473ffffffffffffffffffffffffffffffffffffffff1633811461124e576040517fc22c8022000000000000000000000000000000000000000000000000000000008152336004820152602401610b94565b6108e961174c565b8161128d576040517f3fc3c27a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610ac4828261183d565b5f6112a181611470565b6108e9611861565b6112ca60405180606001604052805f81526020015f81526020015f81525090565b5f6112d8878787878761186b565b90505f7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663035faf826040518163ffffffff1660e01b8152600401602060405180830381865afa158015611344573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906113689190613586565b90506040518060600160405280670de0b6b3a764000083855f015161138d919061359d565b61139791906135e1565b8152602001670de0b6b3a76400008385602001516113b5919061359d565b6113bf91906135e1565b81526020018360400151815250925050505b95945050505050565b5f7fffffffff0000000000000000000000000000000000000000000000000000000082167f7965db0b0000000000000000000000000000000000000000000000000000000014806108d157507f01ffc9a7000000000000000000000000000000000000000000000000000000007fffffffff000000000000000000000000000000000000000000000000000000008316146108d1565b6108e98133611a4a565b6114845f80611ad1565b565b5f828152600260205260409020600101546114a081611470565b6114aa8383611c2a565b50505050565b73ffffffffffffffffffffffffffffffffffffffff811633146114ff576040517f6697b23200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6110f98282611ce8565b5f6020821161151957505f6108d1565b50503560e01c63504e41551490565b6040805160018082528183019092525f91829190816020015b606081526020019060019003908161154157905050905083838080601f0160208091040260200160405190810160405280939291908181526020018383808284375f9201829052508551869450909250151590506115a1576115a1613621565b60209081029190910101526040517fd47eed4500000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000169063d47eed459061161e908490600401613704565b602060405180830381865afa158015611639573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061165d9190613586565b949350505050565b5f61166e61115d565b61167742611d49565b6116819190613716565b905061168d8282611d98565b60405165ffffffffffff8216815273ffffffffffffffffffffffffffffffffffffffff8316907f3377dc44241e779dd06afab5b788a35ca5f3b778836e2990bdb26a2a4b2e5ed69060200160405180910390a25050565b5f6116ee82611e33565b6116f742611d49565b6117019190613716565b905061170d8282611ad1565b6040805165ffffffffffff8085168252831660208201527ff1038c18cf84a56e432fdbfaf746924b7ea511dfe03a6506a0ceba4888788d9b9101610d25565b60035473ffffffffffffffffffffffffffffffffffffffff81169074010000000000000000000000000000000000000000900465ffffffffffff1680158061179c57504265ffffffffffff821610155b156117dd576040517f19ca5ebb00000000000000000000000000000000000000000000000000000000815265ffffffffffff82166004820152602401610b94565b6118055f61180060045473ffffffffffffffffffffffffffffffffffffffff1690565b611ce8565b506118105f83611c2a565b5050600380547fffffffffffff000000000000000000000000000000000000000000000000000016905550565b5f8281526002602052604090206001015461185781611470565b6114aa8383611ce8565b6114845f80611d98565b61188c60405180606001604052805f81526020015f81526020015f81525090565b5f84600a81111561189f5761189f613734565b036118d4576006546118cd908490849088906002906118c89062010000900461ffff1683613761565b611e7a565b90506113d1565b600184600a8111156118e8576118e8613734565b036118f9576118cd83836002611ee9565b600384600a81111561190d5761190d613734565b0361191f576118cd83838760016120b6565b600584600a81111561193357611933613734565b03611944576118cd8383875f6120b6565b600784600a81111561195857611958613734565b03611969576118cd8383875f6120b6565b600984600a81111561197d5761197d613734565b0361198f576118cd83838760016120b6565b600a84600a8111156119a3576119a3613734565b036119b6576118cd83835f60025f611e7a565b600284600a8111156119ca576119ca613734565b036119db576118cd83836002611ee9565b600484600a8111156119ef576119ef613734565b03611a00576118cd83836002611ee9565b600684600a811115611a1457611a14613734565b03611a25576118cd83836002611ee9565b600884600a811115611a3957611a39613734565b036113d1576118cd83836002611ee9565b5f82815260026020908152604080832073ffffffffffffffffffffffffffffffffffffffff8516845290915290205460ff16610ac4576040517fe2517d3f00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8216600482015260248101839052604401610b94565b6004547a010000000000000000000000000000000000000000000000000000900465ffffffffffff168015611ba5574265ffffffffffff82161015611b7c576004546003805479ffffffffffffffffffffffffffffffffffffffffffffffffffff167401000000000000000000000000000000000000000090920465ffffffffffff167a01000000000000000000000000000000000000000000000000000002919091179055611ba5565b6040517f2b1fa2edafe6f7b9e97c1a9e0c3660e645beb2dcaa2d45bdbf9beaf5472e1ec5905f90a15b506004805473ffffffffffffffffffffffffffffffffffffffff167401000000000000000000000000000000000000000065ffffffffffff9485160279ffffffffffffffffffffffffffffffffffffffffffffffffffff16177a0100000000000000000000000000000000000000000000000000009290931691909102919091179055565b5f82611cde575f611c5060045473ffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff1614611c9d576040517f3fc3c27a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600480547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff84161790555b610bef838361219f565b5f82158015611d11575060045473ffffffffffffffffffffffffffffffffffffffff8381169116145b15611d3f57600480547fffffffffffffffffffffffff00000000000000000000000000000000000000001690555b610bef838361229c565b5f65ffffffffffff821115611d94576040517f6dfcc6500000000000000000000000000000000000000000000000000000000081526030600482015260248101839052604401610b94565b5090565b600380547401000000000000000000000000000000000000000065ffffffffffff84811682027fffffffffffff0000000000000000000000000000000000000000000000000000841673ffffffffffffffffffffffffffffffffffffffff8816171790935590041680156110f9576040517f8886ebfc4259abdbc16601dd8fb5678e54878f47b3c34836cfc51154a9605109905f90a1505050565b5f80611e3d61115d565b90508065ffffffffffff168365ffffffffffff1611611e6557611e608382613789565b610bef565b610bef65ffffffffffff841662069780612359565b611e9b60405180606001604052805f81526020015f81526020015f81525090565b6fffffffffffffffffffffffffffffffff841615611ec357600554611ec09085613761565b93505b5f611ed2878787601287612368565b9050611ede81856123ef565b979650505050505050565b611f0a60405180606001604052805f81526020015f81526020015f81525090565b8215611f2457611f1d84845f855f611e7a565b9050610bef565b3415611f5c576040517fd7d2537300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f611f6760126124dc565b90505f611f7460126125ec565b90508060400151826020015111611fea57600154611f9290426137a7565b81604001511015611fd75780604001516040517f18802ca0000000000000000000000000000000000000000000000000000000008152600401610b9491815260200190565b611fe181856123ef565b92505050610bef565b81517f80000000000000000000000000000000000000000000000000000000000000000161204c5781602001516040517f18802ca0000000000000000000000000000000000000000000000000000000008152600401610b9491815260200190565b81515f1261208c5781516040517fd3b9d7b80000000000000000000000000000000000000000000000000000000081526004810191909152602401610b94565b50604080516060810182528251815282516020808301919091529092015190820152949350505050565b6120d760405180606001604052805f81526020015f81526020015f81525090565b6006545f906120f09062010000900461ffff1685613761565b9050806fffffffffffffffffffffffffffffffff164211612120576121188686868685611e7a565b91505061165d565b3415612158576040517fd7d2537300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f612165868801886137d1565b90505f6121728383612782565b60408051606081018252825181528251602080830191909152909201519082015298975050505050505050565b5f82815260026020908152604080832073ffffffffffffffffffffffffffffffffffffffff8516845290915281205460ff16612295575f83815260026020908152604080832073ffffffffffffffffffffffffffffffffffffffff86168452909152902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790556122333390565b73ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16847f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45060016108d1565b505f6108d1565b5f82815260026020908152604080832073ffffffffffffffffffffffffffffffffffffffff8516845290915281205460ff1615612295575f83815260026020908152604080832073ffffffffffffffffffffffffffffffffffffffff8616808552925280832080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016905551339286917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a45060016108d1565b5f828218828410028218610bef565b61238960405180606001604052805f81526020015f81526020015f81525090565b5f61239687878786612a33565b90505f816040015160030b13156123e55760408082015190517f19c6ad6600000000000000000000000000000000000000000000000000000000815260039190910b6004820152602401610b94565b611ede8185612edb565b61241060405180606001604052805f81526020015f81526020015f81525090565b600182600281111561242457612424613734565b036124715760065460208401515f91612710916124459161ffff169061359d565b61244f91906135e1565b84519091508110612463576001825261246b565b835181900382525b506124c7565b5f82600281111561248457612484613734565b036124c2576006546020840151612710916124a59161ffff9091169061359d565b6124af91906135e1565b83516124bb91906137ec565b81526124c7565b825181525b60408084015190820152915160208301525090565b604080518082019091525f80825260208201525f7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015612559573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061257d9190613566565b9050612587612fa3565b80519092507f8000000000000000000000000000000000000000000000000000000000000000016125b85750919050565b6125c381600a613920565b6125ce84600a61392e565b83516125da9190613939565b6125e49190613984565b825250919050565b61260d60405180606001604052805f81526020015f81526020015f81525090565b604080516080810182525f80825260208201819052818301819052606082015290517f96834ad30000000000000000000000000000000000000000000000000000000081527f0000000000000000000000000000000000000000000000000000000000000000600482015273ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016906396834ad390602401608060405180830381865afa925050508015612710575060408051601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016820190925261270d91810190613aed565b60015b156127185790505b5f815f015160070b1361272b5750919050565b5f816040015160030b13156127785760408082015190517f19c6ad6600000000000000000000000000000000000000000000000000000000815260039190910b6004820152602401610b94565b610bef8184612edb565b604080518082019091525f80825260208201526127a060128361309c565b90505f815f0151136127e45780516040517fd3b9d7b80000000000000000000000000000000000000000000000000000000081526004810191909152602401610b94565b5f73ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016639a6fc8f561282c600186613b07565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e084901b16815269ffffffffffffffffffff909116600482015260240160a060405180830381865afa158015612889573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906128ad9190613b29565b509350505050805f036129915761ffff604084901c165f6128db69ffff000000000000000086166001613b7d565b90508469ffffffffffffffffffff168169ffffffffffffffffffff161461292e576040517f649fc61100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60015461294d906fffffffffffffffffffffffffffffffff88166137ec565b8460200151111561298a576040517f649fc61100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50506129dd565b836fffffffffffffffffffffffffffffffff168111156129dd576040517f649fc61100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b836fffffffffffffffffffffffffffffffff16826020015111612a2c576040517f649fc61100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5092915050565b604080516080810182525f80825260208201819052818301819052606082018190528251600180825281850190945291929091908160200160208202803683370190505090507f0000000000000000000000000000000000000000000000000000000000000000815f81518110612aac57612aac613621565b60209081029190910101526040805160018082528183019092525f91816020015b6060815260200190600190039081612acd57905050905086868080601f0160208091040260200160405190810160405280939291908181526020018383808284375f920182905250855186945090925015159050612b2d57612b2d613621565b60200260200101819052505f7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663d47eed45836040518263ffffffff1660e01b8152600401612b929190613704565b602060405180830381865afa158015612bad573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612bd19190613586565b9050662386f26fc10000811115612c17576040517f2ff94f9b00000000000000000000000000000000000000000000000000000000815260048101829052602401610b94565b803414612c50576040517fd7d2537300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6060866fffffffffffffffffffffffffffffffff165f03612d4d575f5473ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001690634716e9c590849086908890612cc59067ffffffffffffffff1642613b9f565b426040518663ffffffff1660e01b8152600401612ce59493929190613bbf565b5f6040518083038185885af1158015612d00573d5f803e3d5ffd5b50505050506040513d5f823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0168201604052612d469190810190613c3b565b9050612e2b565b6040517faccca7f900000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000169063accca7f9908490612dc790879089908d908d90600401613bbf565b5f6040518083038185885af1158015612de2573d5f803e3d5ffd5b50505050506040513d5f823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0168201604052612e289190810190613c3b565b90505b5f815f81518110612e3e57612e3e613621565b6020026020010151602001515f015160070b13612eaf57805f81518110612e6757612e67613621565b6020908102919091018101510151516040517fd3b9d7b800000000000000000000000000000000000000000000000000000000815260079190910b6004820152602401610b94565b805f81518110612ec157612ec1613621565b602002602001015160200151945050505050949350505050565b612efc60405180606001604052805f81526020015f81526020015f81525090565b5f8360400151612f0b90613d2a565b63ffffffff169050604051806060016040528082600a612f2b919061392e565b612f3686600a61392e565b8751612f45919060070b61359d565b612f4f91906135e1565b8152602001612f5f83600a61392e565b612f6a86600a61392e565b876020015167ffffffffffffffff16612f83919061359d565b612f8d91906135e1565b8152602001856060015181525091505092915050565b604080518082019091525f80825260208201525f807f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663feaf968c6040518163ffffffff1660e01b815260040160a060405180830381865afa158015613021573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906130459190613b29565b509350509250506001544261305a91906137a7565b811015613085577f800000000000000000000000000000000000000000000000000000000000000091505b604080518082019091529182526020820152919050565b604080518082019091525f80825260208201525f7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015613119573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061313d9190613566565b90506131488361318f565b91505f825f01511361315a57506108d1565b61316581600a613920565b61317085600a61392e565b835161317c9190613939565b6131869190613984565b82525092915050565b6040805180820182525f808252602082015290517f9a6fc8f500000000000000000000000000000000000000000000000000000000815269ffffffffffffffffffff8316600482015273ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001690639a6fc8f59060240160a060405180830381865afa158015613234573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906132589190613b29565b50602085015250825250919050565b5f60208284031215613277575f80fd5b81357fffffffff0000000000000000000000000000000000000000000000000000000081168114610bef575f80fd5b803561ffff811681146132b7575f80fd5b919050565b73ffffffffffffffffffffffffffffffffffffffff811681146108e9575f80fd5b5f80604083850312156132ee575f80fd5b6132f7836132a6565b91506020830135613307816132bc565b809150509250929050565b5f60208284031215613322575f80fd5b5035919050565b5f806040838503121561333a575f80fd5b823591506020830135613307816132bc565b5f8083601f84011261335c575f80fd5b50813567ffffffffffffffff811115613373575f80fd5b60208301915083602082850101111561338a575f80fd5b9250929050565b8035600b81106132b7575f80fd5b5f805f604084860312156133b1575f80fd5b833567ffffffffffffffff8111156133c7575f80fd5b6133d38682870161334c565b90945092506133e6905060208501613391565b90509250925092565b67ffffffffffffffff811681146108e9575f80fd5b5f60208284031215613414575f80fd5b8135610bef816133ef565b5f6020828403121561342f575f80fd5b8135610bef816132bc565b5f6020828403121561344a575f80fd5b813565ffffffffffff81168114610bef575f80fd5b5f6020828403121561346f575f80fd5b610bef826132a6565b6fffffffffffffffffffffffffffffffff811681146108e9575f80fd5b5f805f805f608086880312156134a9575f80fd5b8535945060208601356134bb81613478565b93506134c960408701613391565b9250606086013567ffffffffffffffff8111156134e4575f80fd5b6134f08882890161334c565b969995985093965092949392505050565b5f60208284031215613511575f80fd5b8151610bef81613478565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b61ffff8181168382160290811690818114612a2c57612a2c61351c565b5f60208284031215613576575f80fd5b815160ff81168114610bef575f80fd5b5f60208284031215613596575f80fd5b5051919050565b80820281158282048414176108d1576108d161351c565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b5f826135ef576135ef6135b4565b500490565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b5f82825180855260208501945060208160051b830101602085015f5b838110156136f8577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0858403018852815180518085528060208301602087015e5f6020828701015260207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8301168601019450505060208201915060208801975060018101905061366a565b50909695505050505050565b602081525f610bef602083018461364e565b65ffffffffffff81811683821601908111156108d1576108d161351c565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602160045260245ffd5b6fffffffffffffffffffffffffffffffff81811683821601908111156108d1576108d161351c565b65ffffffffffff82811682821603908111156108d1576108d161351c565b818103818111156108d1576108d161351c565b69ffffffffffffffffffff811681146108e9575f80fd5b5f602082840312156137e1575f80fd5b8135610bef816137ba565b808201808211156108d1576108d161351c565b6001815b600184111561383a5780850481111561381e5761381e61351c565b600184161561382c57908102905b60019390931c928002613803565b935093915050565b5f82613850575060016108d1565b8161385c57505f6108d1565b8160018114613872576002811461387c57613898565b60019150506108d1565b60ff84111561388d5761388d61351c565b50506001821b6108d1565b5060208310610133831016604e8410600b84101617156138bb575081810a6108d1565b6138e67fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff84846137ff565b807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff048211156139185761391861351c565b029392505050565b5f610bef60ff841683613842565b5f610bef8383613842565b8082025f82127f8000000000000000000000000000000000000000000000000000000000000000841416156139705761397061351c565b81810583148215176108d1576108d161351c565b5f82613992576139926135b4565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff83147f8000000000000000000000000000000000000000000000000000000000000000831416156139e6576139e661351c565b500590565b6040516060810167ffffffffffffffff81118282101715613a0e57613a0e6135f4565b60405290565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff81118282101715613a5b57613a5b6135f4565b604052919050565b5f60808284031215613a73575f80fd5b6040516080810167ffffffffffffffff81118282101715613a9657613a966135f4565b806040525080915082518060070b8114613aae575f80fd5b81526020830151613abe816133ef565b60208201526040830151600381900b8114613ad7575f80fd5b6040820152606092830151920191909152919050565b5f60808284031215613afd575f80fd5b610bef8383613a63565b69ffffffffffffffffffff82811682821603908111156108d1576108d161351c565b5f805f805f60a08688031215613b3d575f80fd5b8551613b48816137ba565b60208701516040880151606089015160808a015193985091965094509250613b6f816137ba565b809150509295509295909350565b69ffffffffffffffffffff81811683821601908111156108d1576108d161351c565b67ffffffffffffffff82811682821603908111156108d1576108d161351c565b608081525f613bd1608083018761364e565b82810360208401528086518083526020830191506020880192505f5b81811015613c0b578351835260209384019390920191600101613bed565b5050809250505067ffffffffffffffff8416604083015267ffffffffffffffff8316606083015295945050505050565b5f60208284031215613c4b575f80fd5b815167ffffffffffffffff811115613c61575f80fd5b8201601f81018413613c71575f80fd5b805167ffffffffffffffff811115613c8b57613c8b6135f4565b613c9a60208260051b01613a14565b8082825260208201915060206101208402850101925086831115613cbc575f80fd5b6020840193505b82841015613d20576101208488031215613cdb575f80fd5b613ce36139eb565b84518152613cf48860208701613a63565b6020820152613d068860a08701613a63565b604082015282526101209390930192602090910190613cc3565b9695505050505050565b5f8160030b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffff800000008103613d5e57613d5e61351c565b5f039291505056fea2646970667358221220cc72a7860678e689e8e13604004bfe73690f6792da0aa1c5b21e812009daa0a064736f6c634300081a00330000000000000000000000004305fb66699c3b2702d4d05cf36551390a4c69c6ff61491a931112ddf1bd8147cd1b641375f79f5825126d665480874634fd0ace0000000000000000000000005f4ec3df9cbd43714fe2740f5e3616155c5b84190000000000000000000000007f39c581f595b53c5cb19bd0b3f8da6c935e2ca00000000000000000000000000000000000000000000000000000000000000e88
Deployed Bytecode
0x6080604052600436106102ab575f3560e01c8063986e189011610165578063be7756dd116100c6578063cf6eefb71161007c578063d602b9fd11610062578063d602b9fd14610818578063f0141d841461082c578063f211abcc14610847575f80fd5b8063cf6eefb7146107a1578063d547741f146107f9575f80fd5b8063c9371327116100ac578063c93713271461075b578063cc8463c814610779578063cefc14291461078d575f80fd5b8063be7756dd14610728578063c4fa8bf41461073c575f80fd5b8063ab8b2f341161011b578063af933b5711610101578063af933b57146106b0578063b1529fbf146106cf578063b222ec71146106f6575f80fd5b8063ab8b2f3414610669578063af2d32bb1461067d575f80fd5b80639e87a5cd1161014b5780639e87a5cd146105f1578063a1eda53c14610623578063a217fddf14610656575f80fd5b8063986e1890146105be57806398793ff9146105dd575f80fd5b8063634e93da1161020f57806384ef8ffc116101c557806391d14854116101ab57806391d148541461053657806393f22b9d1461058757806394218074146105a6575f80fd5b806384ef8ffc146104f85780638da5cb5b14610522575f80fd5b806375b238fc116101f557806375b238fc1461045e57806378399b0b14610491578063831bcbf3146104e4575f80fd5b8063634e93da14610420578063649a5ec71461043f575f80fd5b8063248a9ca31161026457806336568abe1161024a57806336568abe146103c35780633f5bd8a9146103e257806363174c7c14610401575f80fd5b8063248a9ca3146103685780632f2ff15d146103a4575f80fd5b80630aa6220b116102945780630aa6220b1461030b5780630b6313a614610321578063191fe1ed14610340575f80fd5b806301ffc9a7146102af578063022d63fb146102e3575b5f80fd5b3480156102ba575f80fd5b506102ce6102c9366004613267565b61087c565b60405190151581526020015b60405180910390f35b3480156102ee575f80fd5b50620697805b60405165ffffffffffff90911681526020016102da565b348015610316575f80fd5b5061031f6108d7565b005b34801561032c575f80fd5b5061031f61033b3660046132dd565b6108ec565b34801561034b575f80fd5b5061035561271081565b60405161ffff90911681526020016102da565b348015610373575f80fd5b50610396610382366004613312565b5f9081526002602052604090206001015490565b6040519081526020016102da565b3480156103af575f80fd5b5061031f6103be366004613329565b610a83565b3480156103ce575f80fd5b5061031f6103dd366004613329565b610ac8565b3480156103ed575f80fd5b506103966103fc36600461339f565b610bd2565b34801561040c575f80fd5b5061031f61041b366004613404565b610bf6565b34801561042b575f80fd5b5061031f61043a36600461341f565b610d31565b34801561044a575f80fd5b5061031f61045936600461343a565b610d44565b348015610469575f80fd5b506103967fa49807205ce4d355092ef5a8a18f56e8913cf4a201fbe287825b095693c2177581565b34801561049c575f80fd5b507f0000000000000000000000004305fb66699c3b2702d4d05cf36551390a4c69c65b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016102da565b3480156104ef575f80fd5b50600554610396565b348015610503575f80fd5b5060045473ffffffffffffffffffffffffffffffffffffffff166104bf565b34801561052d575f80fd5b506104bf610d57565b348015610541575f80fd5b506102ce610550366004613329565b5f91825260026020908152604080842073ffffffffffffffffffffffffffffffffffffffff93909316845291905290205460ff1690565b348015610592575f80fd5b5061031f6105a136600461345f565b610d7c565b3480156105b1575f80fd5b5060065461ffff16610355565b3480156105c9575f80fd5b5061031f6105d8366004613312565b610e55565b3480156105e8575f80fd5b50610355610eb4565b3480156105fc575f80fd5b507f0000000000000000000000005f4ec3df9cbd43714fe2740f5e3616155c5b84196104bf565b34801561062e575f80fd5b50610637610ec4565b6040805165ffffffffffff9384168152929091166020830152016102da565b348015610661575f80fd5b506103965f81565b348015610674575f80fd5b50610396610f3e565b348015610688575f80fd5b506103967f800000000000000000000000000000000000000000000000000000000000000081565b3480156106bb575f80fd5b5061031f6106ca36600461341f565b610fd4565b3480156106da575f80fd5b505f5460405167ffffffffffffffff90911681526020016102da565b348015610701575f80fd5b507fff61491a931112ddf1bd8147cd1b641375f79f5825126d665480874634fd0ace610396565b348015610733575f80fd5b50600154610396565b348015610747575f80fd5b5061031f610756366004613312565b6110fe565b348015610766575f80fd5b5060065462010000900461ffff16610355565b348015610784575f80fd5b506102f461115d565b348015610798575f80fd5b5061031f6111fa565b3480156107ac575f80fd5b506003546040805173ffffffffffffffffffffffffffffffffffffffff831681527401000000000000000000000000000000000000000090920465ffffffffffff166020830152016102da565b348015610804575f80fd5b5061031f610813366004613329565b611256565b348015610823575f80fd5b5061031f611297565b348015610837575f80fd5b50604051601281526020016102da565b61085a610855366004613495565b6112a9565b60408051825181526020808401519082015291810151908201526060016102da565b5f7fffffffff0000000000000000000000000000000000000000000000000000000082167f314987860000000000000000000000000000000000000000000000000000000014806108d157506108d1826113da565b92915050565b5f6108e181611470565b6108e961147a565b50565b7fa49807205ce4d355092ef5a8a18f56e8913cf4a201fbe287825b095693c2177561091681611470565b6115188361ffff161115610956576040517f14a7fd0f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8173ffffffffffffffffffffffffffffffffffffffff16634aee7f9f6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561099f573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906109c39190613501565b6fffffffffffffffffffffffffffffffff168361ffff161015610a12576040517f14a7fd0f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600680547fffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000ffff166201000061ffff8616908102919091179091556040519081527fdd0f8a3132b698ac11a3f9bcd9f8e2799f39bc4f08b761827d9e24e0ace6edd59060200160405180910390a1505050565b81610aba576040517f3fc3c27a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610ac48282611486565b5050565b81158015610af0575060045473ffffffffffffffffffffffffffffffffffffffff8281169116145b15610bc85760035473ffffffffffffffffffffffffffffffffffffffff81169074010000000000000000000000000000000000000000900465ffffffffffff1681151580610b44575065ffffffffffff8116155b80610b5757504265ffffffffffff821610155b15610b9d576040517f19ca5ebb00000000000000000000000000000000000000000000000000000000815265ffffffffffff821660048201526024015b60405180910390fd5b5050600380547fffffffffffff000000000000ffffffffffffffffffffffffffffffffffffffff1690555b610ac482826114b0565b5f610bdd8484611509565b15610bef57610bec8484611528565b90505b9392505050565b7fa49807205ce4d355092ef5a8a18f56e8913cf4a201fbe287825b095693c21775610c2081611470565b600a8267ffffffffffffffff161015610c71576040517fd44ff2b100000000000000000000000000000000000000000000000000000000815267ffffffffffffffff83166004820152602401610b94565b6102588267ffffffffffffffff161115610cc3576040517fd44ff2b100000000000000000000000000000000000000000000000000000000815267ffffffffffffffff83166004820152602401610b94565b5f80547fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000001667ffffffffffffffff84169081179091556040519081527f241c93c63d73e9b5365f977482dd8048b228ee742453ca59b21448b4a6a4b5b8906020015b60405180910390a15050565b5f610d3b81611470565b610ac482611665565b5f610d4e81611470565b610ac4826116e4565b5f610d7760045473ffffffffffffffffffffffffffffffffffffffff1690565b905090565b7fa49807205ce4d355092ef5a8a18f56e8913cf4a201fbe287825b095693c21775610da681611470565b610db36127106002613549565b61ffff168261ffff161115610df4576040517fc18fce8e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600680547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00001661ffff84169081179091556040519081527f444babe03f2cf1ecf2d92f386b4b36c9bc9a3cb1874b3abe1c5d5a55291395a890602001610d25565b7fa49807205ce4d355092ef5a8a18f56e8913cf4a201fbe287825b095693c21775610e7f81611470565b60058290556040518281527f42e08bed13b165e657b77ec405fe78a7cc5893381b77bf557b457f00ee8fa10a90602001610d25565b610ec16127106002613549565b81565b6004545f907a010000000000000000000000000000000000000000000000000000900465ffffffffffff168015158015610f0657504265ffffffffffff821610155b610f11575f80610f36565b60045474010000000000000000000000000000000000000000900465ffffffffffff16815b915091509091565b5f7f0000000000000000000000005f4ec3df9cbd43714fe2740f5e3616155c5b841973ffffffffffffffffffffffffffffffffffffffff1663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015610fa8573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610fcc9190613566565b60ff16905090565b7fa49807205ce4d355092ef5a8a18f56e8913cf4a201fbe287825b095693c21775610ffe81611470565b73ffffffffffffffffffffffffffffffffffffffff821661104b576040517f80bc7b9100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f8273ffffffffffffffffffffffffffffffffffffffff16476040515f6040518083038185875af1925050503d805f81146110a1576040519150601f19603f3d011682016040523d82523d5f602084013e6110a6565b606091505b50509050806110f9576040517fb5ed065300000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff84166004820152602401610b94565b505050565b7fa49807205ce4d355092ef5a8a18f56e8913cf4a201fbe287825b095693c2177561112881611470565b60018290556040518281527fb253a4f7edf0a3926be13935f7bfcce96181bceff9941569c8124dc09d0ecc6990602001610d25565b6004545f907a010000000000000000000000000000000000000000000000000000900465ffffffffffff16801515801561119e57504265ffffffffffff8216105b6111d0576003547a010000000000000000000000000000000000000000000000000000900465ffffffffffff166111f4565b60045474010000000000000000000000000000000000000000900465ffffffffffff165b91505090565b60035473ffffffffffffffffffffffffffffffffffffffff1633811461124e576040517fc22c8022000000000000000000000000000000000000000000000000000000008152336004820152602401610b94565b6108e961174c565b8161128d576040517f3fc3c27a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610ac4828261183d565b5f6112a181611470565b6108e9611861565b6112ca60405180606001604052805f81526020015f81526020015f81525090565b5f6112d8878787878761186b565b90505f7f0000000000000000000000007f39c581f595b53c5cb19bd0b3f8da6c935e2ca073ffffffffffffffffffffffffffffffffffffffff1663035faf826040518163ffffffff1660e01b8152600401602060405180830381865afa158015611344573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906113689190613586565b90506040518060600160405280670de0b6b3a764000083855f015161138d919061359d565b61139791906135e1565b8152602001670de0b6b3a76400008385602001516113b5919061359d565b6113bf91906135e1565b81526020018360400151815250925050505b95945050505050565b5f7fffffffff0000000000000000000000000000000000000000000000000000000082167f7965db0b0000000000000000000000000000000000000000000000000000000014806108d157507f01ffc9a7000000000000000000000000000000000000000000000000000000007fffffffff000000000000000000000000000000000000000000000000000000008316146108d1565b6108e98133611a4a565b6114845f80611ad1565b565b5f828152600260205260409020600101546114a081611470565b6114aa8383611c2a565b50505050565b73ffffffffffffffffffffffffffffffffffffffff811633146114ff576040517f6697b23200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6110f98282611ce8565b5f6020821161151957505f6108d1565b50503560e01c63504e41551490565b6040805160018082528183019092525f91829190816020015b606081526020019060019003908161154157905050905083838080601f0160208091040260200160405190810160405280939291908181526020018383808284375f9201829052508551869450909250151590506115a1576115a1613621565b60209081029190910101526040517fd47eed4500000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000004305fb66699c3b2702d4d05cf36551390a4c69c6169063d47eed459061161e908490600401613704565b602060405180830381865afa158015611639573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061165d9190613586565b949350505050565b5f61166e61115d565b61167742611d49565b6116819190613716565b905061168d8282611d98565b60405165ffffffffffff8216815273ffffffffffffffffffffffffffffffffffffffff8316907f3377dc44241e779dd06afab5b788a35ca5f3b778836e2990bdb26a2a4b2e5ed69060200160405180910390a25050565b5f6116ee82611e33565b6116f742611d49565b6117019190613716565b905061170d8282611ad1565b6040805165ffffffffffff8085168252831660208201527ff1038c18cf84a56e432fdbfaf746924b7ea511dfe03a6506a0ceba4888788d9b9101610d25565b60035473ffffffffffffffffffffffffffffffffffffffff81169074010000000000000000000000000000000000000000900465ffffffffffff1680158061179c57504265ffffffffffff821610155b156117dd576040517f19ca5ebb00000000000000000000000000000000000000000000000000000000815265ffffffffffff82166004820152602401610b94565b6118055f61180060045473ffffffffffffffffffffffffffffffffffffffff1690565b611ce8565b506118105f83611c2a565b5050600380547fffffffffffff000000000000000000000000000000000000000000000000000016905550565b5f8281526002602052604090206001015461185781611470565b6114aa8383611ce8565b6114845f80611d98565b61188c60405180606001604052805f81526020015f81526020015f81525090565b5f84600a81111561189f5761189f613734565b036118d4576006546118cd908490849088906002906118c89062010000900461ffff1683613761565b611e7a565b90506113d1565b600184600a8111156118e8576118e8613734565b036118f9576118cd83836002611ee9565b600384600a81111561190d5761190d613734565b0361191f576118cd83838760016120b6565b600584600a81111561193357611933613734565b03611944576118cd8383875f6120b6565b600784600a81111561195857611958613734565b03611969576118cd8383875f6120b6565b600984600a81111561197d5761197d613734565b0361198f576118cd83838760016120b6565b600a84600a8111156119a3576119a3613734565b036119b6576118cd83835f60025f611e7a565b600284600a8111156119ca576119ca613734565b036119db576118cd83836002611ee9565b600484600a8111156119ef576119ef613734565b03611a00576118cd83836002611ee9565b600684600a811115611a1457611a14613734565b03611a25576118cd83836002611ee9565b600884600a811115611a3957611a39613734565b036113d1576118cd83836002611ee9565b5f82815260026020908152604080832073ffffffffffffffffffffffffffffffffffffffff8516845290915290205460ff16610ac4576040517fe2517d3f00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8216600482015260248101839052604401610b94565b6004547a010000000000000000000000000000000000000000000000000000900465ffffffffffff168015611ba5574265ffffffffffff82161015611b7c576004546003805479ffffffffffffffffffffffffffffffffffffffffffffffffffff167401000000000000000000000000000000000000000090920465ffffffffffff167a01000000000000000000000000000000000000000000000000000002919091179055611ba5565b6040517f2b1fa2edafe6f7b9e97c1a9e0c3660e645beb2dcaa2d45bdbf9beaf5472e1ec5905f90a15b506004805473ffffffffffffffffffffffffffffffffffffffff167401000000000000000000000000000000000000000065ffffffffffff9485160279ffffffffffffffffffffffffffffffffffffffffffffffffffff16177a0100000000000000000000000000000000000000000000000000009290931691909102919091179055565b5f82611cde575f611c5060045473ffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff1614611c9d576040517f3fc3c27a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600480547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff84161790555b610bef838361219f565b5f82158015611d11575060045473ffffffffffffffffffffffffffffffffffffffff8381169116145b15611d3f57600480547fffffffffffffffffffffffff00000000000000000000000000000000000000001690555b610bef838361229c565b5f65ffffffffffff821115611d94576040517f6dfcc6500000000000000000000000000000000000000000000000000000000081526030600482015260248101839052604401610b94565b5090565b600380547401000000000000000000000000000000000000000065ffffffffffff84811682027fffffffffffff0000000000000000000000000000000000000000000000000000841673ffffffffffffffffffffffffffffffffffffffff8816171790935590041680156110f9576040517f8886ebfc4259abdbc16601dd8fb5678e54878f47b3c34836cfc51154a9605109905f90a1505050565b5f80611e3d61115d565b90508065ffffffffffff168365ffffffffffff1611611e6557611e608382613789565b610bef565b610bef65ffffffffffff841662069780612359565b611e9b60405180606001604052805f81526020015f81526020015f81525090565b6fffffffffffffffffffffffffffffffff841615611ec357600554611ec09085613761565b93505b5f611ed2878787601287612368565b9050611ede81856123ef565b979650505050505050565b611f0a60405180606001604052805f81526020015f81526020015f81525090565b8215611f2457611f1d84845f855f611e7a565b9050610bef565b3415611f5c576040517fd7d2537300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f611f6760126124dc565b90505f611f7460126125ec565b90508060400151826020015111611fea57600154611f9290426137a7565b81604001511015611fd75780604001516040517f18802ca0000000000000000000000000000000000000000000000000000000008152600401610b9491815260200190565b611fe181856123ef565b92505050610bef565b81517f80000000000000000000000000000000000000000000000000000000000000000161204c5781602001516040517f18802ca0000000000000000000000000000000000000000000000000000000008152600401610b9491815260200190565b81515f1261208c5781516040517fd3b9d7b80000000000000000000000000000000000000000000000000000000081526004810191909152602401610b94565b50604080516060810182528251815282516020808301919091529092015190820152949350505050565b6120d760405180606001604052805f81526020015f81526020015f81525090565b6006545f906120f09062010000900461ffff1685613761565b9050806fffffffffffffffffffffffffffffffff164211612120576121188686868685611e7a565b91505061165d565b3415612158576040517fd7d2537300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f612165868801886137d1565b90505f6121728383612782565b60408051606081018252825181528251602080830191909152909201519082015298975050505050505050565b5f82815260026020908152604080832073ffffffffffffffffffffffffffffffffffffffff8516845290915281205460ff16612295575f83815260026020908152604080832073ffffffffffffffffffffffffffffffffffffffff86168452909152902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790556122333390565b73ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16847f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45060016108d1565b505f6108d1565b5f82815260026020908152604080832073ffffffffffffffffffffffffffffffffffffffff8516845290915281205460ff1615612295575f83815260026020908152604080832073ffffffffffffffffffffffffffffffffffffffff8616808552925280832080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016905551339286917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a45060016108d1565b5f828218828410028218610bef565b61238960405180606001604052805f81526020015f81526020015f81525090565b5f61239687878786612a33565b90505f816040015160030b13156123e55760408082015190517f19c6ad6600000000000000000000000000000000000000000000000000000000815260039190910b6004820152602401610b94565b611ede8185612edb565b61241060405180606001604052805f81526020015f81526020015f81525090565b600182600281111561242457612424613734565b036124715760065460208401515f91612710916124459161ffff169061359d565b61244f91906135e1565b84519091508110612463576001825261246b565b835181900382525b506124c7565b5f82600281111561248457612484613734565b036124c2576006546020840151612710916124a59161ffff9091169061359d565b6124af91906135e1565b83516124bb91906137ec565b81526124c7565b825181525b60408084015190820152915160208301525090565b604080518082019091525f80825260208201525f7f0000000000000000000000005f4ec3df9cbd43714fe2740f5e3616155c5b841973ffffffffffffffffffffffffffffffffffffffff1663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015612559573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061257d9190613566565b9050612587612fa3565b80519092507f8000000000000000000000000000000000000000000000000000000000000000016125b85750919050565b6125c381600a613920565b6125ce84600a61392e565b83516125da9190613939565b6125e49190613984565b825250919050565b61260d60405180606001604052805f81526020015f81526020015f81525090565b604080516080810182525f80825260208201819052818301819052606082015290517f96834ad30000000000000000000000000000000000000000000000000000000081527fff61491a931112ddf1bd8147cd1b641375f79f5825126d665480874634fd0ace600482015273ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000004305fb66699c3b2702d4d05cf36551390a4c69c616906396834ad390602401608060405180830381865afa925050508015612710575060408051601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016820190925261270d91810190613aed565b60015b156127185790505b5f815f015160070b1361272b5750919050565b5f816040015160030b13156127785760408082015190517f19c6ad6600000000000000000000000000000000000000000000000000000000815260039190910b6004820152602401610b94565b610bef8184612edb565b604080518082019091525f80825260208201526127a060128361309c565b90505f815f0151136127e45780516040517fd3b9d7b80000000000000000000000000000000000000000000000000000000081526004810191909152602401610b94565b5f73ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000005f4ec3df9cbd43714fe2740f5e3616155c5b841916639a6fc8f561282c600186613b07565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e084901b16815269ffffffffffffffffffff909116600482015260240160a060405180830381865afa158015612889573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906128ad9190613b29565b509350505050805f036129915761ffff604084901c165f6128db69ffff000000000000000086166001613b7d565b90508469ffffffffffffffffffff168169ffffffffffffffffffff161461292e576040517f649fc61100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60015461294d906fffffffffffffffffffffffffffffffff88166137ec565b8460200151111561298a576040517f649fc61100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50506129dd565b836fffffffffffffffffffffffffffffffff168111156129dd576040517f649fc61100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b836fffffffffffffffffffffffffffffffff16826020015111612a2c576040517f649fc61100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5092915050565b604080516080810182525f80825260208201819052818301819052606082018190528251600180825281850190945291929091908160200160208202803683370190505090507fff61491a931112ddf1bd8147cd1b641375f79f5825126d665480874634fd0ace815f81518110612aac57612aac613621565b60209081029190910101526040805160018082528183019092525f91816020015b6060815260200190600190039081612acd57905050905086868080601f0160208091040260200160405190810160405280939291908181526020018383808284375f920182905250855186945090925015159050612b2d57612b2d613621565b60200260200101819052505f7f0000000000000000000000004305fb66699c3b2702d4d05cf36551390a4c69c673ffffffffffffffffffffffffffffffffffffffff1663d47eed45836040518263ffffffff1660e01b8152600401612b929190613704565b602060405180830381865afa158015612bad573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612bd19190613586565b9050662386f26fc10000811115612c17576040517f2ff94f9b00000000000000000000000000000000000000000000000000000000815260048101829052602401610b94565b803414612c50576040517fd7d2537300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6060866fffffffffffffffffffffffffffffffff165f03612d4d575f5473ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000004305fb66699c3b2702d4d05cf36551390a4c69c61690634716e9c590849086908890612cc59067ffffffffffffffff1642613b9f565b426040518663ffffffff1660e01b8152600401612ce59493929190613bbf565b5f6040518083038185885af1158015612d00573d5f803e3d5ffd5b50505050506040513d5f823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0168201604052612d469190810190613c3b565b9050612e2b565b6040517faccca7f900000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000004305fb66699c3b2702d4d05cf36551390a4c69c6169063accca7f9908490612dc790879089908d908d90600401613bbf565b5f6040518083038185885af1158015612de2573d5f803e3d5ffd5b50505050506040513d5f823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0168201604052612e289190810190613c3b565b90505b5f815f81518110612e3e57612e3e613621565b6020026020010151602001515f015160070b13612eaf57805f81518110612e6757612e67613621565b6020908102919091018101510151516040517fd3b9d7b800000000000000000000000000000000000000000000000000000000815260079190910b6004820152602401610b94565b805f81518110612ec157612ec1613621565b602002602001015160200151945050505050949350505050565b612efc60405180606001604052805f81526020015f81526020015f81525090565b5f8360400151612f0b90613d2a565b63ffffffff169050604051806060016040528082600a612f2b919061392e565b612f3686600a61392e565b8751612f45919060070b61359d565b612f4f91906135e1565b8152602001612f5f83600a61392e565b612f6a86600a61392e565b876020015167ffffffffffffffff16612f83919061359d565b612f8d91906135e1565b8152602001856060015181525091505092915050565b604080518082019091525f80825260208201525f807f0000000000000000000000005f4ec3df9cbd43714fe2740f5e3616155c5b841973ffffffffffffffffffffffffffffffffffffffff1663feaf968c6040518163ffffffff1660e01b815260040160a060405180830381865afa158015613021573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906130459190613b29565b509350509250506001544261305a91906137a7565b811015613085577f800000000000000000000000000000000000000000000000000000000000000091505b604080518082019091529182526020820152919050565b604080518082019091525f80825260208201525f7f0000000000000000000000005f4ec3df9cbd43714fe2740f5e3616155c5b841973ffffffffffffffffffffffffffffffffffffffff1663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015613119573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061313d9190613566565b90506131488361318f565b91505f825f01511361315a57506108d1565b61316581600a613920565b61317085600a61392e565b835161317c9190613939565b6131869190613984565b82525092915050565b6040805180820182525f808252602082015290517f9a6fc8f500000000000000000000000000000000000000000000000000000000815269ffffffffffffffffffff8316600482015273ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000005f4ec3df9cbd43714fe2740f5e3616155c5b84191690639a6fc8f59060240160a060405180830381865afa158015613234573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906132589190613b29565b50602085015250825250919050565b5f60208284031215613277575f80fd5b81357fffffffff0000000000000000000000000000000000000000000000000000000081168114610bef575f80fd5b803561ffff811681146132b7575f80fd5b919050565b73ffffffffffffffffffffffffffffffffffffffff811681146108e9575f80fd5b5f80604083850312156132ee575f80fd5b6132f7836132a6565b91506020830135613307816132bc565b809150509250929050565b5f60208284031215613322575f80fd5b5035919050565b5f806040838503121561333a575f80fd5b823591506020830135613307816132bc565b5f8083601f84011261335c575f80fd5b50813567ffffffffffffffff811115613373575f80fd5b60208301915083602082850101111561338a575f80fd5b9250929050565b8035600b81106132b7575f80fd5b5f805f604084860312156133b1575f80fd5b833567ffffffffffffffff8111156133c7575f80fd5b6133d38682870161334c565b90945092506133e6905060208501613391565b90509250925092565b67ffffffffffffffff811681146108e9575f80fd5b5f60208284031215613414575f80fd5b8135610bef816133ef565b5f6020828403121561342f575f80fd5b8135610bef816132bc565b5f6020828403121561344a575f80fd5b813565ffffffffffff81168114610bef575f80fd5b5f6020828403121561346f575f80fd5b610bef826132a6565b6fffffffffffffffffffffffffffffffff811681146108e9575f80fd5b5f805f805f608086880312156134a9575f80fd5b8535945060208601356134bb81613478565b93506134c960408701613391565b9250606086013567ffffffffffffffff8111156134e4575f80fd5b6134f08882890161334c565b969995985093965092949392505050565b5f60208284031215613511575f80fd5b8151610bef81613478565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b61ffff8181168382160290811690818114612a2c57612a2c61351c565b5f60208284031215613576575f80fd5b815160ff81168114610bef575f80fd5b5f60208284031215613596575f80fd5b5051919050565b80820281158282048414176108d1576108d161351c565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b5f826135ef576135ef6135b4565b500490565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b5f82825180855260208501945060208160051b830101602085015f5b838110156136f8577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0858403018852815180518085528060208301602087015e5f6020828701015260207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8301168601019450505060208201915060208801975060018101905061366a565b50909695505050505050565b602081525f610bef602083018461364e565b65ffffffffffff81811683821601908111156108d1576108d161351c565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602160045260245ffd5b6fffffffffffffffffffffffffffffffff81811683821601908111156108d1576108d161351c565b65ffffffffffff82811682821603908111156108d1576108d161351c565b818103818111156108d1576108d161351c565b69ffffffffffffffffffff811681146108e9575f80fd5b5f602082840312156137e1575f80fd5b8135610bef816137ba565b808201808211156108d1576108d161351c565b6001815b600184111561383a5780850481111561381e5761381e61351c565b600184161561382c57908102905b60019390931c928002613803565b935093915050565b5f82613850575060016108d1565b8161385c57505f6108d1565b8160018114613872576002811461387c57613898565b60019150506108d1565b60ff84111561388d5761388d61351c565b50506001821b6108d1565b5060208310610133831016604e8410600b84101617156138bb575081810a6108d1565b6138e67fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff84846137ff565b807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff048211156139185761391861351c565b029392505050565b5f610bef60ff841683613842565b5f610bef8383613842565b8082025f82127f8000000000000000000000000000000000000000000000000000000000000000841416156139705761397061351c565b81810583148215176108d1576108d161351c565b5f82613992576139926135b4565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff83147f8000000000000000000000000000000000000000000000000000000000000000831416156139e6576139e661351c565b500590565b6040516060810167ffffffffffffffff81118282101715613a0e57613a0e6135f4565b60405290565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff81118282101715613a5b57613a5b6135f4565b604052919050565b5f60808284031215613a73575f80fd5b6040516080810167ffffffffffffffff81118282101715613a9657613a966135f4565b806040525080915082518060070b8114613aae575f80fd5b81526020830151613abe816133ef565b60208201526040830151600381900b8114613ad7575f80fd5b6040820152606092830151920191909152919050565b5f60808284031215613afd575f80fd5b610bef8383613a63565b69ffffffffffffffffffff82811682821603908111156108d1576108d161351c565b5f805f805f60a08688031215613b3d575f80fd5b8551613b48816137ba565b60208701516040880151606089015160808a015193985091965094509250613b6f816137ba565b809150509295509295909350565b69ffffffffffffffffffff81811683821601908111156108d1576108d161351c565b67ffffffffffffffff82811682821603908111156108d1576108d161351c565b608081525f613bd1608083018761364e565b82810360208401528086518083526020830191506020880192505f5b81811015613c0b578351835260209384019390920191600101613bed565b5050809250505067ffffffffffffffff8416604083015267ffffffffffffffff8316606083015295945050505050565b5f60208284031215613c4b575f80fd5b815167ffffffffffffffff811115613c61575f80fd5b8201601f81018413613c71575f80fd5b805167ffffffffffffffff811115613c8b57613c8b6135f4565b613c9a60208260051b01613a14565b8082825260208201915060206101208402850101925086831115613cbc575f80fd5b6020840193505b82841015613d20576101208488031215613cdb575f80fd5b613ce36139eb565b84518152613cf48860208701613a63565b6020820152613d068860a08701613a63565b604082015282526101209390930192602090910190613cc3565b9695505050505050565b5f8160030b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffff800000008103613d5e57613d5e61351c565b5f039291505056fea2646970667358221220cc72a7860678e689e8e13604004bfe73690f6792da0aa1c5b21e812009daa0a064736f6c634300081a0033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
0000000000000000000000004305fb66699c3b2702d4d05cf36551390a4c69c6ff61491a931112ddf1bd8147cd1b641375f79f5825126d665480874634fd0ace0000000000000000000000005f4ec3df9cbd43714fe2740f5e3616155c5b84190000000000000000000000007f39c581f595b53c5cb19bd0b3f8da6c935e2ca00000000000000000000000000000000000000000000000000000000000000e88
-----Decoded View---------------
Arg [0] : pythContract (address): 0x4305FB66699C3B2702D4d05CF36551390A4c69C6
Arg [1] : pythPriceID (bytes32): 0xff61491a931112ddf1bd8147cd1b641375f79f5825126d665480874634fd0ace
Arg [2] : chainlinkPriceFeed (address): 0x5f4eC3Df9cbd43714FE2740f5E3616155c5b8419
Arg [3] : wstETH (address): 0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0
Arg [4] : chainlinkTimeElapsedLimit (uint256): 3720
-----Encoded View---------------
5 Constructor Arguments found :
Arg [0] : 0000000000000000000000004305fb66699c3b2702d4d05cf36551390a4c69c6
Arg [1] : ff61491a931112ddf1bd8147cd1b641375f79f5825126d665480874634fd0ace
Arg [2] : 0000000000000000000000005f4ec3df9cbd43714fe2740f5e3616155c5b8419
Arg [3] : 0000000000000000000000007f39c581f595b53c5cb19bd0b3f8da6c935e2ca0
Arg [4] : 0000000000000000000000000000000000000000000000000000000000000e88
Loading...
Loading
Loading...
Loading
Net Worth in USD
$0.09
Net Worth in ETH
0.000029
Token Allocations
BNB
100.00%
Multichain Portfolio | 35 Chains
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
[ Download: CSV Export ]
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.