Source Code
Overview
ETH Balance
0 ETH
ETH Value
$0.00
Cross-Chain Transactions
Loading...
Loading
Contract Name:
StableModule
Compiler Version
v0.8.20+commit.a1b79de6
Optimization Enabled:
Yes with 500 runs
Other Settings:
paris EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: SEE LICENSE IN LICENSE
pragma solidity 0.8.20;
import {SafeCast} from "@openzeppelin/contracts/utils/math/SafeCast.sol";
import {ERC20LockableUpgradeable} from "./misc/ERC20LockableUpgradeable.sol";
import {PerpMath} from "./libraries/PerpMath.sol";
import {FlatcoinStructs} from "./libraries/FlatcoinStructs.sol";
import {FlatcoinErrors} from "./libraries/FlatcoinErrors.sol";
import {FlatcoinModuleKeys} from "./libraries/FlatcoinModuleKeys.sol";
import {FlatcoinEvents} from "./libraries/FlatcoinEvents.sol";
import {ModuleUpgradeable} from "./abstracts/ModuleUpgradeable.sol";
import {IFlatcoinVault} from "./interfaces/IFlatcoinVault.sol";
import {ILeverageModule} from "./interfaces/ILeverageModule.sol";
import {IStableModule} from "./interfaces/IStableModule.sol";
import {IPointsModule} from "./interfaces/IPointsModule.sol";
/// @title StableModule
/// @author dHEDGE
/// @notice Contains functions to handle stable LP deposits and withdrawals.
contract StableModule is IStableModule, ModuleUpgradeable, ERC20LockableUpgradeable {
using SafeCast for *;
using PerpMath for int256;
using PerpMath for uint256;
uint256 public constant MIN_LIQUIDITY = 10_000; // minimum totalSupply that is allowable
/// @notice Fee for stable LP redemptions.
/// @dev 1e18 = 100%
uint256 public stableWithdrawFee;
/// @dev To prevent the implementation contract from being used, we invoke the _disableInitializers
/// function in the constructor to automatically lock it when it is deployed.
/// @custom:oz-upgrades-unsafe-allow constructor
constructor() {
_disableInitializers();
}
/// @notice Function to initialize this contract.
function initialize(IFlatcoinVault _vault, uint256 _stableWithdrawFee) external initializer {
__Module_init(FlatcoinModuleKeys._STABLE_MODULE_KEY, _vault);
__ERC20_init("Flat Money", "UNIT");
setStableWithdrawFee(_stableWithdrawFee);
}
/////////////////////////////////////////////
// External Write Functions //
/////////////////////////////////////////////
/// @notice User delayed deposit into the stable LP. Mints ERC20 token receipt.
/// @dev Needs to be used in conjunction with DelayedOrder module.
/// @param _account The usser account which has a pending deposit.
/// @param _executableAtTime The time at which the order can be executed.
/// @param _announcedDeposit The pending order.
function executeDeposit(
address _account,
uint64 _executableAtTime,
FlatcoinStructs.AnnouncedStableDeposit calldata _announcedDeposit
) external onlyAuthorizedModule {
uint256 depositAmount = _announcedDeposit.depositAmount;
uint32 maxAge = _getMaxAge(_executableAtTime);
uint256 liquidityMinted = (depositAmount * (10 ** decimals())) /
stableCollateralPerShare({_maxAge: maxAge, _priceDiffCheck: true});
if (liquidityMinted < _announcedDeposit.minAmountOut)
revert FlatcoinErrors.HighSlippage(liquidityMinted, _announcedDeposit.minAmountOut);
_mint(_account, liquidityMinted);
vault.updateStableCollateralTotal(int256(depositAmount));
if (totalSupply() < MIN_LIQUIDITY)
revert FlatcoinErrors.AmountTooSmall({amount: totalSupply(), minAmount: MIN_LIQUIDITY});
// Mint points
IPointsModule pointsModule = IPointsModule(vault.moduleAddress(FlatcoinModuleKeys._POINTS_MODULE_KEY));
pointsModule.mintDeposit(_account, _announcedDeposit.depositAmount);
emit FlatcoinEvents.Deposit(_account, depositAmount, liquidityMinted);
}
/// @notice User delayed withdrawal from the stable LP. Burns ERC20 token receipt.
/// @dev Needs to be used in conjunction with DelayedOrder module.
/// @param _account The usser account which has a pending withdrawal.
/// @param _executableAtTime The time at which the order can be executed.
/// @param _announcedWithdraw The pending order.
/// @return _amountOut The amount of collateral withdrawn.
/// @return _withdrawFee The fee paid to the remaining LPs.
function executeWithdraw(
address _account,
uint64 _executableAtTime,
FlatcoinStructs.AnnouncedStableWithdraw calldata _announcedWithdraw
) external onlyAuthorizedModule returns (uint256 _amountOut, uint256 _withdrawFee) {
uint256 withdrawAmount = _announcedWithdraw.withdrawAmount;
uint32 maxAge = _getMaxAge(_executableAtTime);
uint256 stableCollateralPerShareBefore = stableCollateralPerShare({_maxAge: maxAge, _priceDiffCheck: true});
_amountOut = (withdrawAmount * stableCollateralPerShareBefore) / (10 ** decimals());
// Unlock the locked LP tokens before burning.
// This is because if the amount to be burned is locked, the burn will fail due to `_beforeTokenTransfer`.
_unlock(_account, withdrawAmount);
_burn(_account, withdrawAmount);
vault.updateStableCollateralTotal(-int256(_amountOut));
uint256 stableCollateralPerShareAfter = stableCollateralPerShare({_maxAge: maxAge, _priceDiffCheck: true});
// Check that there is no significant impact on stable token price.
// This should never happen and means that too much value or not enough value was withdrawn.
if (totalSupply() > 0) {
if (
stableCollateralPerShareAfter < stableCollateralPerShareBefore - 1e6 ||
stableCollateralPerShareAfter > stableCollateralPerShareBefore + 1e6
) revert FlatcoinErrors.PriceImpactDuringWithdraw();
// Apply the withdraw fee if it's not the final withdrawal.
_withdrawFee = (stableWithdrawFee * _amountOut) / 1e18;
// additionalSkew = 0 because withdrawal was already processed above.
vault.checkSkewMax({sizeChange: 0, stableCollateralChange: int256(_withdrawFee)});
} else {
// Need to check there are no longs open before allowing full system withdrawal.
uint256 sizeOpenedTotal = vault.getVaultSummary().globalPositions.sizeOpenedTotal;
if (sizeOpenedTotal != 0) revert FlatcoinErrors.MaxSkewReached(sizeOpenedTotal);
if (stableCollateralPerShareAfter != 1e18) revert FlatcoinErrors.PriceImpactDuringFullWithdraw();
}
emit FlatcoinEvents.Withdraw(_account, _amountOut, withdrawAmount);
}
/// @notice Function to lock a certain amount of an account's LP tokens.
/// @dev This function is used to lock LP tokens when an account announces a delayed order.
/// @param _account The account to lock the LP tokens from.
/// @param _amount The amount of LP tokens to lock.
function lock(address _account, uint256 _amount) public onlyAuthorizedModule {
_lock(_account, _amount);
}
/// @notice Function to unlock a certain amount of an account's LP tokens.
/// @dev This function is used to unlock LP tokens when an account cancels a delayed order
/// or when an order is executed.
/// @param _account The account to unlock the LP tokens from.
/// @param _amount The amount of LP tokens to unlock.
function unlock(address _account, uint256 _amount) public onlyAuthorizedModule {
_unlock(_account, _amount);
}
/////////////////////////////////////////////
// View Functions //
/////////////////////////////////////////////
/// @notice Total collateral available for withdrawal.
/// @dev Balance takes into account trader profit and loss and funding rate.
/// @return _stableCollateralBalance The total collateral available for withdrawal.
function stableCollateralTotalAfterSettlement() public view returns (uint256 _stableCollateralBalance) {
return stableCollateralTotalAfterSettlement({_maxAge: type(uint32).max, _priceDiffCheck: false});
}
/// @notice Function to calculate total stable side collateral after accounting for trader profit and loss and funding fees.
/// @param _maxAge The oldest price oracle timestamp that can be used. Set to 0 to ignore.
/// @return _stableCollateralBalance The total collateral available for withdrawal.
function stableCollateralTotalAfterSettlement(
uint32 _maxAge,
bool _priceDiffCheck
) public view returns (uint256 _stableCollateralBalance) {
// Assumption => pnlTotal = pnlLong + fundingAccruedLong
// The assumption is based on the fact that stable LPs are the counterparty to leverage traders.
// If the `pnlLong` is +ve that means the traders won and the LPs lost between the last funding rate update and now.
// Similary if the `fundingAccruedLong` is +ve that means the market was skewed short-side.
// When we combine these two terms, we get the total profit/loss of the leverage traders.
// NOTE: This function if called after settlement returns only the PnL as funding has already been adjusted
// due to calling `_settleFundingFees()`. Although this still means `netTotal` includes the funding
// adjusted long PnL, it might not be clear to the reader of the code.
int256 netTotal = ILeverageModule(vault.moduleAddress(FlatcoinModuleKeys._LEVERAGE_MODULE_KEY))
.fundingAdjustedLongPnLTotal({maxAge: _maxAge, priceDiffCheck: _priceDiffCheck});
// The flatcoin LPs are the counterparty to the leverage traders.
// So when the traders win, the flatcoin LPs lose and vice versa.
// Therefore we subtract the leverage trader profits and add the losses
int256 totalAfterSettlement = int256(vault.stableCollateralTotal()) - netTotal;
if (totalAfterSettlement < 0) {
_stableCollateralBalance = 0;
} else {
_stableCollateralBalance = uint256(totalAfterSettlement);
}
}
/// @notice Function to calculate the collateral per share.
/// @return _collateralPerShare The collateral per share.
function stableCollateralPerShare() public view returns (uint256 _collateralPerShare) {
return stableCollateralPerShare({_maxAge: type(uint32).max, _priceDiffCheck: false});
}
/// @notice Function to calculate the collateral per share.
/// @param _maxAge The oldest price oracle timestamp that can be used.
/// @return _collateralPerShare The collateral per share.
function stableCollateralPerShare(
uint32 _maxAge,
bool _priceDiffCheck
) public view returns (uint256 _collateralPerShare) {
uint256 totalSupply = totalSupply();
if (totalSupply > 0) {
uint256 stableBalance = stableCollateralTotalAfterSettlement({
_maxAge: _maxAge,
_priceDiffCheck: _priceDiffCheck
});
_collateralPerShare = (stableBalance * (10 ** decimals())) / totalSupply;
} else {
// no shares have been minted yet
_collateralPerShare = 1e18;
}
}
/// @notice Quoter function for getting the stable deposit amount out.
/// @param _depositAmount The amount of collateral to deposit.
/// @return _amountOut The amount of LP tokens minted.
function stableDepositQuote(uint256 _depositAmount) public view returns (uint256 _amountOut) {
return (_depositAmount * (10 ** decimals())) / stableCollateralPerShare();
}
/// @notice Quoter function for getting the stable withdraw amount out.
/// @param _withdrawAmount The amount of LP tokens to withdraw.
/// @return _amountOut The amount of collateral withdrawn.
function stableWithdrawQuote(uint256 _withdrawAmount) public view returns (uint256 _amountOut) {
_amountOut = (_withdrawAmount * stableCollateralPerShare()) / (10 ** decimals());
// Take out the withdrawal fee
_amountOut -= (_amountOut * stableWithdrawFee) / 1e18;
}
/// @notice Function to get the locked amount of an account.
/// @param _account The account to get the locked amount for.
/// @return _amountLocked The amount of LP tokens locked.
function getLockedAmount(address _account) public view returns (uint256 _amountLocked) {
return _lockedAmount[_account];
}
/////////////////////////////////////////////
// Internal Functions //
/////////////////////////////////////////////
/// @notice Returns the maximum age of the oracle price to be used.
/// @param _executableAtTime The time at which the order is executable.
/// @return _maxAge The maximum age of the oracle price to be used.
function _getMaxAge(uint64 _executableAtTime) internal view returns (uint32 _maxAge) {
return (block.timestamp - _executableAtTime).toUint32();
}
/////////////////////////////////////////////
// Owner Functions //
/////////////////////////////////////////////
/// @notice Setter for the stable withdraw fee.
/// @dev Fees can be set to 0 if needed.
/// @param _stableWithdrawFee The new stable withdraw fee.
function setStableWithdrawFee(uint256 _stableWithdrawFee) public onlyOwner {
// Set fee cap to max 1%.
// This is to avoid fat fingering but if any change is needed, the owner needs to
// upgrade this module.
if (_stableWithdrawFee > 0.01e18) revert FlatcoinErrors.InvalidFee(_stableWithdrawFee);
stableWithdrawFee = _stableWithdrawFee;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.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 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);
}
}// SPDX-License-Identifier: SEE LICENSE IN LICENSE
pragma solidity 0.8.20;
import {ERC20Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol";
// solhint-disable reason-string
// solhint-disable gas-custom-errors
contract ERC20LockableUpgradeable is ERC20Upgradeable {
mapping(address account => uint256 lockedAmount) internal _lockedAmount;
event Locked(address indexed account, uint256 amount);
event Unlocked(address indexed account, uint256 amount);
// solhint-disable-next-line func-name-mixedcase
function __ERC20LockableUpgradeable_init()
internal
onlyInitializing // solhint-disable-next-line no-empty-blocks
{}
// solhint-disable-next-line func-name-mixedcase
function __ERC20LockableUpgradeable_init_unchained()
internal
onlyInitializing // solhint-disable-next-line no-empty-blocks
{}
function _lock(address account, uint256 amount) internal virtual {
require(
_lockedAmount[account] + amount <= balanceOf(account),
"ERC20LockableUpgradeable: locked amount exceeds balance"
);
_lockedAmount[account] += amount;
emit Locked(account, amount);
}
function _unlock(address account, uint256 amount) internal virtual {
require(_lockedAmount[account] >= amount, "ERC20LockableUpgradeable: requested unlock exceeds locked balance");
_lockedAmount[account] -= amount;
emit Unlocked(account, amount);
}
function _update(address from, address to, uint256 amount) internal virtual override {
// Make sure the sender has enough unlocked tokens.
// Note: the below requirement is not needed when minting tokens in which case the `from` address is 0x0.
if (from != address(0)) {
require(
balanceOf(from) - _lockedAmount[from] >= amount,
"ERC20LockableUpgradeable: insufficient unlocked balance"
);
}
super._update(from, to, amount);
}
uint256[49] private __gap;
}// SPDX-License-Identifier: SEE LICENSE IN LICENSE
pragma solidity 0.8.20;
import {SignedMath} from "@openzeppelin/contracts/utils/math/SignedMath.sol";
import {DecimalMath} from "../libraries/DecimalMath.sol";
import {FlatcoinStructs} from "../libraries/FlatcoinStructs.sol";
/// @title PerpMath
/// @author dHEDGE
/// @notice Abstract contract which contains necessary math functions for perps.
/// @dev Adapted from Synthetix PerpsV2MarketBase <https://github.com/Synthetixio/synthetix/blob/cbd8666f4331ee95fcc667ec7345d13c8ba77efb/contracts/PerpsV2MarketBase.sol#L156>
/// and <https://github.com/Synthetixio/synthetix/blob/cbd8666f4331ee95fcc667ec7345d13c8ba77efb/contracts/SafeDecimalMath.sol>
library PerpMath {
using SignedMath for int256;
using DecimalMath for int256;
using DecimalMath for uint256;
/////////////////////////////////////////////
// Funding Math Functions //
/////////////////////////////////////////////
/// @dev Returns the pSkew = skew / skewScale capping the pSkew between [-1, 1].
/// @param skew The current system skew.
/// @param stableCollateralTotal The total stable collateral in the system.
/// @return pSkew The capped proportional skew.
function _proportionalSkew(int256 skew, uint256 stableCollateralTotal) internal pure returns (int256 pSkew) {
if (stableCollateralTotal > 0) {
pSkew = skew._divideDecimal(int256(stableCollateralTotal));
if (pSkew < -1e18 || pSkew > 1e18) {
pSkew = DecimalMath.UNIT.min(pSkew.max(-DecimalMath.UNIT));
}
} else {
assert(skew == 0);
pSkew = 0;
}
}
/// @dev Retrieves the change in funding rate since the last re-computation.
/// There is no variance in computation but will be affected based on outside modifications to
/// the market skew, max funding velocity, and time delta.
/// @param proportionalSkew The capped proportional skew.
/// @param prevFundingModTimestamp The last recomputed funding timestamp.
/// @param maxFundingVelocity The maximum funding velocity.
/// @param maxVelocitySkew The maximum velocity skew.
/// @return fundingChange The change in funding rate since the last re-computation.
function _fundingChangeSinceRecomputed(
int256 proportionalSkew,
uint256 prevFundingModTimestamp,
uint256 maxFundingVelocity,
uint256 maxVelocitySkew
) internal view returns (int256 fundingChange) {
return
_currentFundingVelocity(proportionalSkew, maxFundingVelocity, maxVelocitySkew)._multiplyDecimal(
int256(_proportionalElapsedTime(prevFundingModTimestamp))
);
}
/// @dev Function to calculate the funding rate based on market conditions.
/// @param lastRecomputedFundingRate The last recomputed funding rate.
/// @param lastRecomputedFundingTimestamp The last recomputed funding timestamp.
/// @param proportionalSkew The capped proportional skew.
/// @param maxFundingVelocity The maximum funding velocity.
/// @param maxVelocitySkew The maximum velocity skew.
/// @return currFundingRate The current funding rate.
function _currentFundingRate(
int256 lastRecomputedFundingRate,
uint64 lastRecomputedFundingTimestamp,
int256 proportionalSkew,
uint256 maxFundingVelocity,
uint256 maxVelocitySkew
) internal view returns (int256 currFundingRate) {
return
lastRecomputedFundingRate +
_fundingChangeSinceRecomputed(
proportionalSkew,
lastRecomputedFundingTimestamp,
maxFundingVelocity,
maxVelocitySkew
);
}
/// @dev Calculates the sum of the unrecorded funding rates since the last funding re-computation.
/// @param vaultSummary The current summary of the vault state.
/// @param maxFundingVelocity The maximum funding velocity.
/// @param maxVelocitySkew The maximum velocity skew.
/// @return unrecordedFunding The sum of the unrecorded funding rates since the last funding re-computation.
function _unrecordedFunding(
FlatcoinStructs.VaultSummary memory vaultSummary,
uint256 maxFundingVelocity,
uint256 maxVelocitySkew
) internal view returns (int256 unrecordedFunding) {
int256 nextFundingRate = _currentFundingRate({
proportionalSkew: _proportionalSkew(vaultSummary.marketSkew, vaultSummary.stableCollateralTotal),
lastRecomputedFundingRate: vaultSummary.lastRecomputedFundingRate,
lastRecomputedFundingTimestamp: vaultSummary.lastRecomputedFundingTimestamp,
maxFundingVelocity: maxFundingVelocity,
maxVelocitySkew: maxVelocitySkew
});
// NOTE: Synthetix uses the -ve sign here. We won't use it here as we believe it makes intutive sense
// to use the same sign as the skew to preserve the traditional sense of the sign of the funding rate.
// However, this also means that we have to invert the sign when calculating the difference between user's index
// and the current global index for accumulated funding rate.
int256 avgFundingRate = (vaultSummary.lastRecomputedFundingRate + nextFundingRate) / 2;
return
avgFundingRate._multiplyDecimal(
int256(_proportionalElapsedTime(vaultSummary.lastRecomputedFundingTimestamp))
);
}
/// @dev Same as the above `_unrecordedFunding but with the current funding rate passed in.
/// @param currentFundingRate The current funding rate.
/// @param prevFundingRate The previous funding rate.
/// @param prevFundingModTimestamp The last recomputed funding timestamp.
/// @return unrecordedFunding The sum of the unrecorded funding rates since the last funding re-computation.
function _unrecordedFunding(
int256 currentFundingRate,
int256 prevFundingRate,
uint256 prevFundingModTimestamp
) internal view returns (int256 unrecordedFunding) {
int256 avgFundingRate = (prevFundingRate + currentFundingRate) / 2;
return avgFundingRate._multiplyDecimal(int256(_proportionalElapsedTime(prevFundingModTimestamp)));
}
/// @dev The new entry in the funding sequence, appended when funding is recomputed.
/// It is the sum of the last entry and the unrecorded funding,
/// so the sequence accumulates running total over the market's lifetime.
/// @param vaultSummary The current summary of the vault state.
/// @param maxFundingVelocity The maximum funding velocity.
/// @param maxVelocitySkew The maximum velocity skew.
/// @return nextFundingEntry The next entry in the funding sequence.
function _nextFundingEntry(
FlatcoinStructs.VaultSummary memory vaultSummary,
uint256 maxFundingVelocity,
uint256 maxVelocitySkew
) internal view returns (int256 nextFundingEntry) {
return
vaultSummary.cumulativeFundingRate + _unrecordedFunding(vaultSummary, maxFundingVelocity, maxVelocitySkew);
}
/// @dev Same as the above `_nextFundingEntry` but with the next funding entry passed in.
/// @param unrecordedFunding The sum of the unrecorded funding rates since the last funding re-computation.
/// @param latestFundingSequenceEntry The latest funding sequence entry.
/// @return nextFundingEntry The next entry in the funding sequence.
function _nextFundingEntry(
int256 unrecordedFunding,
int256 latestFundingSequenceEntry
) internal pure returns (int256 nextFundingEntry) {
return latestFundingSequenceEntry + unrecordedFunding;
}
/// @dev Calculates the current net funding per unit for a position.
/// @param userFundingSequenceEntry The user's last funding sequence entry.
/// @param nextFundingEntry The next funding sequence entry.
/// @return netFundingPerUnit The net funding per unit for a position.
function _netFundingPerUnit(
int256 userFundingSequenceEntry,
int256 nextFundingEntry
) internal pure returns (int256 netFundingPerUnit) {
return userFundingSequenceEntry - nextFundingEntry;
}
/*******************************************
* Position Details *
*******************************************/
/// @dev Returns the PnL in terms of the market currency (ETH/LST) and not in dollars ($).
/// This function rounds down the PnL to avoid rounding errors when subtracting individual PnLs
/// from the global `marginDepositedTotal` value when closing the position.
/// @param position The position to calculate the PnL for.
/// @param price The current price of the collateral asset.
/// @return pnl The PnL in terms of the market currency (ETH/LST) and not in dollars ($).
function _profitLoss(FlatcoinStructs.Position memory position, uint256 price) internal pure returns (int256 pnl) {
int256 priceShift = int256(price) - int256(position.averagePrice);
int256 profitLossTimesTen = (int256(position.additionalSize) * (priceShift) * 10) / int256(price);
if (profitLossTimesTen % 10 != 0) {
return profitLossTimesTen / 10 - 1;
} else {
return profitLossTimesTen / 10;
}
}
/// @dev Returns the PnL in terms of the market currency (ETH/LST) and not in dollars ($).
/// This function rounds down the funding accrued to avoid rounding errors when subtracting individual funding fees accrued
/// from the global `marginDepositedTotal` value when closing the position.
/// @param globalPosition The global position to calculate the PnL for.
/// @param price The current price of the collateral asset.
/// @return pnl The PnL in terms of the market currency (ETH/LST) and not in dollars ($).
function _profitLossTotal(
FlatcoinStructs.GlobalPositions memory globalPosition,
uint256 price
) internal pure returns (int256 pnl) {
int256 priceShift = int256(price) - int256(globalPosition.averagePrice);
return (int256(globalPosition.sizeOpenedTotal) * (priceShift)) / int256(price);
}
function _accruedFunding(
FlatcoinStructs.Position memory position,
int256 nextFundingEntry
) internal pure returns (int256 accruedFunding) {
int256 net = _netFundingPerUnit(position.entryCumulativeFunding, nextFundingEntry);
return int256(position.additionalSize)._multiplyDecimal(net);
}
/// @dev Calculates the funding fees accrued by the global position (all leverage traders).
/// To avoid rounding errors when individual positions close and the global `marginDepositedTotal` is updated,
/// we add 1 wei to the total accrued funding by longs. This also means that there might be some amount left in the
/// vault belonging to the longs which is not distributed. This is insignificant and is a trade-off to avoid rounding errors.
/// @param globalPosition The global position to calculate the funding fees accrued for.
/// @param unrecordedFunding The sum of the unrecorded funding rates since the last funding re-computation.
/// @return accruedFundingLongs The funding fees accrued by the global position (all leverage traders).
function _accruedFundingTotalByLongs(
FlatcoinStructs.GlobalPositions memory globalPosition,
int256 unrecordedFunding
) internal pure returns (int256 accruedFundingLongs) {
int256 accruedFundingTotal = -int256(globalPosition.sizeOpenedTotal)._multiplyDecimal(unrecordedFunding);
return (accruedFundingTotal != 0) ? accruedFundingTotal + 1 : accruedFundingTotal;
}
/// @dev Summarises a positions' earnings/losses.
/// @param position The position to summarise.
/// @param nextFundingEntry The next (recalculated) cumulative funding rate.
/// @param price The current price of the collateral asset.
/// @return positionSummary The summary of the position.
function _getPositionSummary(
FlatcoinStructs.Position memory position,
int256 nextFundingEntry,
uint256 price
) internal pure returns (FlatcoinStructs.PositionSummary memory positionSummary) {
int256 profitLoss = _profitLoss(position, price);
int256 accruedFunding = _accruedFunding(position, nextFundingEntry);
return
FlatcoinStructs.PositionSummary({
profitLoss: profitLoss,
accruedFunding: accruedFunding,
marginAfterSettlement: int256(position.marginDeposited) + profitLoss + accruedFunding
});
}
/// @dev Summarises the market state which is used in other functions.
/// @param vaultSummary The current summary of the vault state.
/// @param maxFundingVelocity The maximum funding velocity.
/// @param maxVelocitySkew The maximum velocity skew.
/// @param price The current price of the collateral asset.
/// @return marketSummary The summary of the market.
function _getMarketSummaryLongs(
FlatcoinStructs.VaultSummary memory vaultSummary,
uint256 maxFundingVelocity,
uint256 maxVelocitySkew,
uint256 price
) internal view returns (FlatcoinStructs.MarketSummary memory marketSummary) {
int256 currentFundingRate = _currentFundingRate({
proportionalSkew: _proportionalSkew(vaultSummary.marketSkew, vaultSummary.stableCollateralTotal),
lastRecomputedFundingRate: vaultSummary.lastRecomputedFundingRate,
lastRecomputedFundingTimestamp: vaultSummary.lastRecomputedFundingTimestamp,
maxFundingVelocity: maxFundingVelocity,
maxVelocitySkew: maxVelocitySkew
});
int256 unrecordedFunding = _unrecordedFunding(
currentFundingRate,
vaultSummary.lastRecomputedFundingRate,
vaultSummary.lastRecomputedFundingTimestamp
);
return
FlatcoinStructs.MarketSummary({
profitLossTotalByLongs: _profitLossTotal(vaultSummary.globalPositions, price),
accruedFundingTotalByLongs: _accruedFundingTotalByLongs(
vaultSummary.globalPositions,
unrecordedFunding
),
currentFundingRate: currentFundingRate,
nextFundingEntry: _nextFundingEntry(unrecordedFunding, vaultSummary.cumulativeFundingRate)
});
}
/////////////////////////////////////////////
// Liquidation Math //
/////////////////////////////////////////////
/// @notice Function to calculate the approximate liquidation price.
/// @dev Only approximation can be achieved due to the fact that the funding rate influences the liquidation price.
/// @param position The position to calculate the liquidation price for.
/// @param nextFundingEntry The next (recalculated) cumulative funding rate.
/// @param liquidationFeeRatio The liquidation fee of the system.
/// @param liquidationBufferRatio The liquidation buffer ratio of the system.
/// @param liquidationFeeUpperBound The maximum liquidation fee to be paid to the keepers.
/// @param currentPrice Current price of the collateral asset.
function _approxLiquidationPrice(
FlatcoinStructs.Position memory position,
int256 nextFundingEntry,
uint128 liquidationFeeRatio,
uint128 liquidationBufferRatio,
uint256 liquidationFeeLowerBound,
uint256 liquidationFeeUpperBound,
uint256 currentPrice
) internal pure returns (uint256 approxLiquidationPrice) {
if (position.additionalSize == 0) {
return 0;
}
FlatcoinStructs.PositionSummary memory positionSummary = _getPositionSummary(
position,
nextFundingEntry,
currentPrice
);
int256 result = _calcLiquidationPrice(
position,
positionSummary,
_liquidationMargin(
position.additionalSize,
liquidationFeeRatio,
liquidationBufferRatio,
liquidationFeeLowerBound,
liquidationFeeUpperBound,
currentPrice
)
);
return (result > 0) ? uint256(result) : 0;
}
/// @dev Function to get the liquidation status of a position.
/// @param position The position to check the liquidation status for.
/// @param liquidationFeeRatio The liquidation fee of the system.
/// @param liquidationBufferRatio The liquidation buffer ratio of the system.
/// @param liquidationFeeLowerBound The minimum liquidation fee to be paid to the flagger.
/// @param liquidationFeeUpperBound The maximum liquidation fee to be paid to the keepers.
/// @param nextFundingEntry The next (recalculated) cumulative funding rate.
/// @param currentPrice Current price of the collateral asset.
/// @return isLiquidatable Whether the position is liquidatable.
function _canLiquidate(
FlatcoinStructs.Position memory position,
uint128 liquidationFeeRatio,
uint128 liquidationBufferRatio,
uint256 liquidationFeeLowerBound,
uint256 liquidationFeeUpperBound,
int256 nextFundingEntry,
uint256 currentPrice
) internal pure returns (bool isLiquidatable) {
// No liquidations of empty positions.
if (position.additionalSize == 0) {
return false;
}
FlatcoinStructs.PositionSummary memory positionSummary = _getPositionSummary(
position,
nextFundingEntry,
currentPrice
);
uint256 lMargin = _liquidationMargin(
position.additionalSize,
liquidationFeeRatio,
liquidationBufferRatio,
liquidationFeeLowerBound,
liquidationFeeUpperBound,
currentPrice
);
return positionSummary.marginAfterSettlement <= int256(lMargin);
}
/// @dev The minimal margin at which liquidation can happen.
/// Is the sum of liquidationBuffer, liquidationFee (for flagger) and keeperLiquidationFee (for liquidator)
/// The liquidation margin contains a buffer that is proportional to the position
/// size. The buffer should prevent liquidation happening at negative margin (due to next price being worse).
/// @param positionSize size of position in fixed point decimal collateral asset units.
/// @param liquidationFeeRatio ratio of the position size to be charged as fee.
/// @param liquidationBufferRatio ratio of the position size needed to be maintained as buffer.
/// @param liquidationFeeUpperBound maximum fee to be charged in collateral asset units.
/// @param currentPrice current price of the collateral asset in USD units.
/// @return lMargin liquidation margin to maintain in collateral asset units.
function _liquidationMargin(
uint256 positionSize,
uint128 liquidationFeeRatio,
uint128 liquidationBufferRatio,
uint256 liquidationFeeLowerBound,
uint256 liquidationFeeUpperBound,
uint256 currentPrice
) internal pure returns (uint256 lMargin) {
uint256 liquidationBuffer = positionSize._multiplyDecimal(liquidationBufferRatio);
// The liquidation margin consists of the liquidation buffer, liquidation fee and the keeper fee for covering execution costs.
return
liquidationBuffer +
_liquidationFee(
positionSize,
liquidationFeeRatio,
liquidationFeeLowerBound,
liquidationFeeUpperBound,
currentPrice
);
}
/// The fee charged from the margin during liquidation. Fee is proportional to position size.
/// @dev There is a cap on the fee to prevent liquidators from being overpayed.
/// @param positionSize size of position in fixed point decimal baseAsset units.
/// @param liquidationFeeRatio ratio of the position size to be charged as fee.
/// @param liquidationFeeUpperBound maximum fee to be charged in USD units.
/// @return liquidationFee liquidation fee to be paid to liquidator in collateral asset units.
function _liquidationFee(
uint256 positionSize,
uint128 liquidationFeeRatio,
uint256 liquidationFeeLowerBound,
uint256 liquidationFeeUpperBound,
uint256 currentPrice
) internal pure returns (uint256 liquidationFee) {
// size * price * fee-ratio
uint256 proportionalFee = positionSize._multiplyDecimal(liquidationFeeRatio)._multiplyDecimal(currentPrice);
uint256 cappedProportionalFee = proportionalFee > liquidationFeeUpperBound
? liquidationFeeUpperBound
: proportionalFee;
uint256 lFeeUSD = cappedProportionalFee < liquidationFeeLowerBound
? liquidationFeeLowerBound
: cappedProportionalFee;
// Return liquidation fee in collateral asset units.
return (lFeeUSD * 1e18) / currentPrice;
}
/////////////////////////////////////////////
// Private Functions //
/////////////////////////////////////////////
/// @dev The funding velocity is based on the market skew and is scaled by the maxVelocitySkew.
/// With higher skews beyond the maxVelocitySkew, the velocity remains constant.
/// @param proportionalSkew The calculated capped proportional skew.
/// @param maxFundingVelocity The maximum funding velocity.
/// @param maxVelocitySkew The maximum velocity skew.
function _currentFundingVelocity(
int256 proportionalSkew,
uint256 maxFundingVelocity,
uint256 maxVelocitySkew
) private pure returns (int256 currFundingVelocity) {
if (maxVelocitySkew > 0) {
// Scale the funding velocity by the maxVelocitySkew and cap it at the maximum +- velocity.
int256 fundingVelocity = (proportionalSkew * int256(maxFundingVelocity)) / int256(maxVelocitySkew);
return int256(maxFundingVelocity).min(fundingVelocity.max(-int256(maxFundingVelocity)));
}
return proportionalSkew._multiplyDecimal(int256(maxFundingVelocity));
}
/// @dev Returns the time delta between the last funding timestamp and the current timestamp.
/// @param prevModTimestamp The last funding timestamp.
/// @return elapsedTime The time delta between the last funding timestamp and the current timestamp.
function _proportionalElapsedTime(uint256 prevModTimestamp) private view returns (uint256 elapsedTime) {
return (block.timestamp - prevModTimestamp)._divideDecimal(1 days);
}
/// @dev Calculates the liquidation price.
/// @param position The position to calculate the liquidation price for.
/// @param positionSummary The summary of the position.
/// @param liquidationMargin The liquidation margin.
/// @return liqPrice The liquidation price.
function _calcLiquidationPrice(
FlatcoinStructs.Position memory position,
FlatcoinStructs.PositionSummary memory positionSummary,
uint256 liquidationMargin
) private pure returns (int256 liqPrice) {
// A position can be liquidated whenever:- remainingMargin <= liquidationMargin
//
// Hence, expanding the definition of remainingMargin the exact price at which a position can be liquidated is:
//
// liquidationMargin = margin + profitLoss + funding
// liquidationMargin = margin + [(price - entryPrice) * postionSize / price] + funding
// liquidationMargin - (margin + funding) = [(price - entryPrice) * postionSize / price]
// liquidationMargin - (margin + funding) = postionSize - (entryPrice * postionSize / price)
// positionSize - [liquidationMargin - (margin + funding)] = entryPrice * postionSize / price
// positionSize * entryPrice / {positionSize - [liquidationMargin - (margin + funding)]} = price
//
// In our case, positionSize = position.additionalSize.
// Note: If there are bounds on `liquidationFee` and/or `keeperFee` then this formula doesn't yield an accurate liquidation price.
// This is because, when the position size is too large such that liquidation fee for that position has to be bounded we are essentially
// solving the following equation:
// LiquidationBuffer + (LiquidationUpperBound / Price) + KeeperFee = Margin + (Price - EntryPrice)*PositionSize + AccruedFunding
// And according to Wolfram Alpha, this equation cannot be solved for Price (at least trivially):
// https://www.wolframalpha.com/input?i=A+++(B+/+X)+%3D+C+++(X+-+D)+*+E+,+X+%3E+0,+Solution+for+variable+X
return
int256((position.additionalSize)._multiplyDecimal(position.averagePrice))._divideDecimal(
int256(position.additionalSize + position.marginDeposited) +
positionSummary.accruedFunding -
int256(liquidationMargin)
);
}
}// SPDX-License-Identifier: SEE LICENSE IN LICENSE
pragma solidity 0.8.20;
import {IChainlinkAggregatorV3} from "../interfaces/IChainlinkAggregatorV3.sol";
import {IPyth} from "pyth-sdk-solidity/IPyth.sol";
library FlatcoinStructs {
enum OrderType {
None, // 0
StableDeposit, // 1
StableWithdraw, // 2
LeverageOpen, // 3
LeverageClose, // 4
LeverageAdjust, // 5
LimitClose // 6
}
enum LimitOrderExecutionType {
None, // 0
StopLoss, // 1
ProfitTake // 2
}
/// @notice Global position data
/// @dev This is the consolidated data of all leverage positions used to calculate funding fees and total profit and loss.
/// @dev One can imagine this as being the data of a single big position on leverage side against the stable side.
/// @param marginDepositedTotal Total collateral deposited for leverage trade positions.
/// @param averagePrice The last time funding fees and profit and loss were settled.
/// @param sizeOpenedTotal The total size of leverage across all trades on entry.
struct GlobalPositions {
int256 marginDepositedTotal;
uint256 averagePrice;
uint256 sizeOpenedTotal;
}
/// @notice Individual leverage position
struct Position {
uint256 averagePrice;
uint256 marginDeposited;
uint256 additionalSize;
int256 entryCumulativeFunding;
}
struct MarketSummary {
int256 profitLossTotalByLongs;
int256 accruedFundingTotalByLongs;
int256 currentFundingRate;
int256 nextFundingEntry;
}
struct PositionSummary {
int256 profitLoss;
int256 accruedFunding;
int256 marginAfterSettlement;
}
struct VaultSummary {
int256 marketSkew;
int256 cumulativeFundingRate;
int256 lastRecomputedFundingRate;
uint64 lastRecomputedFundingTimestamp;
uint256 stableCollateralTotal;
GlobalPositions globalPositions;
}
struct Order {
OrderType orderType;
uint256 keeperFee; // The deposit paid upon submitting that needs to be paid / refunded on tx confirmation
uint64 executableAtTime; // The timestamp at which this order is executable at
bytes orderData;
}
struct AnnouncedStableDeposit {
uint256 depositAmount;
uint256 minAmountOut; // The minimum amount of tokens expected to receive back
address announcedBy; // To differentiate between deposit and depositFor
}
struct AnnouncedStableWithdraw {
uint256 withdrawAmount;
uint256 minAmountOut; // The minimum amount of underlying tokens expected to receive back
}
struct AnnouncedLeverageOpen {
uint256 margin; // The margin amount to be used as leverage collateral
uint256 additionalSize; // The additional size exposure (leverage)
uint256 maxFillPrice; // The maximum price accepted by the user
uint256 tradeFee;
address announcedBy; // To differentiate between open and openFor
}
struct AnnouncedLeverageAdjust {
uint256 tokenId;
int256 marginAdjustment;
int256 additionalSizeAdjustment;
uint256 fillPrice; // should be passed depending on the type of additionalSizeAdjustment
uint256 tradeFee;
uint256 totalFee;
}
// Note: the tradeFee is determined at time of execution
struct LimitClose {
// Note: the tradeFee is determined at time of execution
uint256 tokenId;
uint256 priceLowerThreshold;
uint256 priceUpperThreshold;
}
struct AnnouncedLeverageClose {
uint256 tokenId; // The NFT of the position
uint256 minFillPrice; // The minimum price accepted by the user
uint256 tradeFee;
}
struct OnchainOracle {
IChainlinkAggregatorV3 oracleContract; // Chainlink oracle contract
uint32 maxAge; // Oldest price that is acceptable to use
}
struct OffchainOracle {
IPyth oracleContract; // Pyth network oracle contract
bytes32 priceId; // Pyth network price Id
uint32 maxAge; // Oldest price that is acceptable to use
uint32 minConfidenceRatio; // the minimum Pyth oracle price / expo ratio. The higher, the more confident the accuracy of the price.
}
struct AuthorizedModule {
bytes32 moduleKey;
address moduleAddress;
}
struct LeveragePositionData {
uint256 tokenId;
uint256 averagePrice;
uint256 marginDeposited;
uint256 additionalSize;
int256 entryCumulativeFunding;
int256 profitLoss;
int256 accruedFunding;
int256 marginAfterSettlement;
uint256 liquidationPrice;
uint256 limitOrderPriceLowerThreshold;
uint256 limitOrderPriceUpperThreshold;
}
struct MintRate {
uint256 lastAccumulatedMint;
uint256 maxAccumulatedMint;
uint64 lastMintTimestamp;
uint64 decayTime;
}
}// SPDX-License-Identifier: SEE LICENSE IN LICENSE
pragma solidity 0.8.20;
library FlatcoinErrors {
enum PriceSource {
OnChain,
OffChain
}
error ZeroAddress(string variableName);
error ZeroValue(string variableName);
error Paused(bytes32 moduleKey);
error OnlyOwner(address msgSender);
error AmountTooSmall(uint256 amount, uint256 minAmount);
error HighSlippage(uint256 supplied, uint256 accepted);
/// @dev DelayedOrder
error MaxFillPriceTooLow(uint256 maxFillPrice, uint256 currentPrice);
/// @dev DelayedOrder
error MinFillPriceTooHigh(uint256 minFillPrice, uint256 currentPrice);
/// @dev DelayedOrder
error NotEnoughMarginForFees(int256 marginAmount, uint256 feeAmount);
/// @dev DelayedOrder
error OrderHasExpired();
/// @dev DelayedOrder
error OrderHasNotExpired();
/// @dev DelayedOrder
error ExecutableTimeNotReached(uint256 executableTime);
/// @dev DelayedOrder
error NotTokenOwner(uint256 tokenId, address msgSender);
/// @dev DelayedOrder
error MaxSkewReached(uint256 skewFraction);
/// @dev DelayedOrder
error InvalidSkewFractionMax(uint256 skewFractionMax);
/// @dev DelayedOrder
error InvalidMaxVelocitySkew(uint256 maxVelocitySkew);
/// @dev DelayedOrder
error NotEnoughBalanceForWithdraw(address account, uint256 totalBalance, uint256 withdrawAmount);
/// @dev DelayedOrder
error WithdrawalTooSmall(uint256 withdrawAmount, uint256 keeperFee);
/// @dev DelayedOrder
error InvariantViolation(string variableName);
/// @dev DelayedOrder
error InvalidLeverageCriteria();
/// @dev DelayedOrder
error LeverageTooLow(uint256 leverageMin, uint256 leverage);
/// @dev DelayedOrder
error LeverageTooHigh(uint256 leverageMax, uint256 leverage);
/// @dev DelayedOrder
error MarginTooSmall(uint256 marginMin, uint256 margin);
/// @dev DelayedOrder
error DelayedOrderInvalid(address account);
/// @dev DelayedOrder
error DepositCapReached(uint256 collateralCap);
/// @dev DelayedOrder
error InsufficientGlobalMargin();
/// @dev LimitOrder
error LimitOrderInvalid(uint256 tokenId);
/// @dev LimitOrder
error LimitOrderPriceNotInRange(uint256 price, uint256 priceLowerThreshold, uint256 priceUpperThreshold);
/// @dev LimitOrder
error InvalidThresholds(uint256 priceLowerThreshold, uint256 priceUpperThreshold);
error InvalidFee(uint256 fee);
error OnlyAuthorizedModule(address msgSender);
error ValueNotPositive(string variableName);
/// @dev LeverageModule
error MarginMismatchOnClose();
/// @dev OracleModule
error RefundFailed();
error PriceStale(PriceSource priceSource);
error PriceInvalid(PriceSource priceSource);
error PriceMismatch(uint256 diffPercent);
/// @dev OracleModule
error OracleConfigInvalid();
/// @dev StableModule
error PriceImpactDuringWithdraw();
/// @dev StableModule
error PriceImpactDuringFullWithdraw();
/// @dev KeeperFee
error ETHPriceInvalid();
/// @dev KeeperFee
error ETHPriceStale();
/// @dev Error to emit when a leverage position is not liquidatable.
/// @param tokenId The token ID of the position.
error CannotLiquidate(uint256 tokenId);
error InvalidBounds(uint256 lower, uint256 upper);
error PositionCreatesBadDebt();
error ModuleKeyEmpty();
/// @dev PointsModule
error MaxVarianceExceeded(string variableName);
}// SPDX-License-Identifier: SEE LICENSE IN LICENSE
pragma solidity 0.8.20;
library FlatcoinModuleKeys {
bytes32 internal constant _STABLE_MODULE_KEY = bytes32("stableModule");
bytes32 internal constant _LEVERAGE_MODULE_KEY = bytes32("leverageModule");
bytes32 internal constant _ORACLE_MODULE_KEY = bytes32("oracleModule");
bytes32 internal constant _DELAYED_ORDER_KEY = bytes32("delayedOrder");
bytes32 internal constant _LIMIT_ORDER_KEY = bytes32("limitOrder");
bytes32 internal constant _LIQUIDATION_MODULE_KEY = bytes32("liquidationModule");
bytes32 internal constant _KEEPER_FEE_MODULE_KEY = bytes32("keeperFee");
bytes32 internal constant _POINTS_MODULE_KEY = bytes32("pointsModule");
}// SPDX-License-Identifier: SEE LICENSE IN LICENSE
pragma solidity 0.8.20;
import {FlatcoinStructs} from "./FlatcoinStructs.sol";
library FlatcoinEvents {
event FundingFeesSettled(int256 settledFundingFee);
event OrderAnnounced(address account, FlatcoinStructs.OrderType orderType, uint256 keeperFee);
event OrderExecuted(address account, FlatcoinStructs.OrderType orderType, uint256 keeperFee);
event OrderCancelled(address account, FlatcoinStructs.OrderType orderType);
event Deposit(address depositor, uint256 depositAmount, uint256 mintedAmount);
event Withdraw(address withdrawer, uint256 withdrawAmount, uint256 burnedAmount);
event LeverageOpen(address account, uint256 tokenId, uint256 entryPrice, uint256 margin, uint256 size);
event LeverageAdjust(
uint256 tokenId,
uint256 averagePrice,
uint256 adjustPrice,
int256 marginDelta,
int256 sizeDelta
);
event LeverageClose(
uint256 tokenId,
uint256 closePrice,
FlatcoinStructs.PositionSummary positionSummary,
uint256 settledMargin,
uint256 size
);
event SetAsset(address asset);
event SetOnChainOracle(FlatcoinStructs.OnchainOracle oracle);
event SetOffChainOracle(FlatcoinStructs.OffchainOracle oracle);
event PositionLiquidated(
uint256 tokenId,
address liquidator,
uint256 liquidationFee,
uint256 closePrice,
FlatcoinStructs.PositionSummary positionSummary
);
event LiquidationFeeRatioModified(uint256 oldRatio, uint256 newRatio);
event LiquidationBufferRatioModified(uint256 oldRatio, uint256 newRatio);
event LiquidationFeeBoundsModified(uint256 oldMin, uint256 oldMax, uint256 newMin, uint256 newMax);
event VaultAddressModified(address oldAddress, address newAddress);
event LiquidationFundsDeposited(address depositor, uint256 amount);
event LiquidationFeesWithdrawn(uint256 amount);
event SetMaxDiffPercent(uint256 maxDiffPercent);
event LimitOrderAnnounced(
address account,
uint256 tokenId,
uint256 priceLowerThreshold,
uint256 priceUpperThreshold
);
event LimitOrderExecuted(
address account,
uint256 tokenId,
uint256 keeperFee,
uint256 price,
FlatcoinStructs.LimitOrderExecutionType limitOrderType
);
event LimitOrderCancelled(address account, uint256 tokenId);
}// SPDX-License-Identifier: SEE LICENSE IN LICENSE
pragma solidity 0.8.20;
import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import {FlatcoinErrors} from "../libraries/FlatcoinErrors.sol";
import {IFlatcoinVault} from "../interfaces/IFlatcoinVault.sol";
/// @title ModuleUpgradeable
/// @author dHEDGE
/// @notice This is the base contract for all upgradeable modules in the Flatcoin system.
abstract contract ModuleUpgradeable is Initializable {
/// @notice The bytes32 encoded key of the module.
/// @dev Note that this shouldn't change ever for existing modules.
/// Due to this module being upgradeable, we can't use immutable here.
// solhint-disable-next-line var-name-mixedcase
bytes32 public MODULE_KEY;
/// @notice The FlatcoinVault contract referred to by all modules.
/// @dev Contains all the module addresses, the state of the system and more.
IFlatcoinVault public vault;
modifier onlyAuthorizedModule() {
if (vault.isAuthorizedModule(msg.sender) == false) revert FlatcoinErrors.OnlyAuthorizedModule(msg.sender);
_;
}
modifier whenNotPaused() {
if (vault.isModulePaused(MODULE_KEY)) revert FlatcoinErrors.Paused(MODULE_KEY);
_;
}
modifier onlyOwner() {
if (OwnableUpgradeable(address(vault)).owner() != msg.sender) revert FlatcoinErrors.OnlyOwner(msg.sender);
_;
}
/// @notice Setter for the vault contract.
/// @dev Can be used in case FlatcoinVault ever changes.
function setVault(IFlatcoinVault _vault) external onlyOwner {
if (address(_vault) == address(0)) revert FlatcoinErrors.ZeroAddress("vault");
vault = _vault;
}
/// @dev Function to initialize a module.
/// @param _moduleKey The bytes32 encoded key of the module.
/// @param _vault FlatcoinVault contract address.
// solhint-disable-next-line func-name-mixedcase
function __Module_init(bytes32 _moduleKey, IFlatcoinVault _vault) internal {
if (_moduleKey == bytes32("")) revert FlatcoinErrors.ModuleKeyEmpty();
if (address(_vault) == address(0)) revert FlatcoinErrors.ZeroAddress("vault");
MODULE_KEY = _moduleKey;
vault = _vault;
}
uint256[48] private __gap;
}// SPDX-License-Identifier: SEE LICENSE IN LICENSE
pragma solidity ^0.8.20;
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {FlatcoinStructs} from "../libraries/FlatcoinStructs.sol";
interface IFlatcoinVault {
function collateral() external view returns (IERC20 collateral);
function lastRecomputedFundingTimestamp() external view returns (uint64 lastRecomputedFundingTimestamp);
function minExecutabilityAge() external view returns (uint64 minExecutabilityAge);
function maxExecutabilityAge() external view returns (uint64 maxExecutabilityAge);
function lastRecomputedFundingRate() external view returns (int256 lastRecomputedFundingRate);
function cumulativeFundingRate() external view returns (int256 cumulativeFundingRate);
function maxFundingVelocity() external view returns (uint256 maxFundingVelocity);
function maxVelocitySkew() external view returns (uint256 maxVelocitySkew);
function stableCollateralTotal() external view returns (uint256 totalAmount);
function skewFractionMax() external view returns (uint256 skewFractionMax);
function moduleAddress(bytes32 _moduleKey) external view returns (address moduleAddress);
function isAuthorizedModule(address _address) external view returns (bool status);
function isModulePaused(bytes32 moduleKey) external view returns (bool paused);
function sendCollateral(address to, uint256 amount) external;
function getVaultSummary() external view returns (FlatcoinStructs.VaultSummary memory _vaultSummary);
function getGlobalPositions() external view returns (FlatcoinStructs.GlobalPositions memory _globalPositions);
function setPosition(FlatcoinStructs.Position memory _position, uint256 _tokenId) external;
function updateGlobalPositionData(uint256 price, int256 marginDelta, int256 additionalSizeDelta) external;
function updateStableCollateralTotal(int256 _stableCollateralAdjustment) external;
function addAuthorizedModules(FlatcoinStructs.AuthorizedModule[] calldata _modules) external;
function addAuthorizedModule(FlatcoinStructs.AuthorizedModule calldata _module) external;
function removeAuthorizedModule(bytes32 _moduleKey) external;
function deletePosition(uint256 _tokenId) external;
function settleFundingFees() external;
function getCurrentFundingRate() external view returns (int256 fundingRate);
function getPosition(uint256 _tokenId) external view returns (FlatcoinStructs.Position memory position);
function checkSkewMax(uint256 sizeChange, int256 stableCollateralChange) external view;
function checkCollateralCap(uint256 depositAmount) external view;
function checkGlobalMarginPositive() external view;
function stableCollateralCap() external view returns (uint256 collateralCap);
function getCurrentSkew() external view returns (int256 skew);
}// SPDX-License-Identifier: SEE LICENSE IN LICENSE
pragma solidity ^0.8.20;
import {IERC721Enumerable} from "@openzeppelin/contracts/token/ERC721/extensions/IERC721Enumerable.sol";
import {FlatcoinStructs} from "../libraries/FlatcoinStructs.sol";
interface ILeverageModule is IERC721Enumerable {
function executeOpen(address account, address keeper, FlatcoinStructs.Order calldata order) external;
function executeAdjust(address account, address keeper, FlatcoinStructs.Order calldata order) external;
function executeClose(address account, address keeper, FlatcoinStructs.Order calldata order) external;
function burn(uint256 tokenId, bytes32 moduleKey) external;
function lock(uint256 tokenId, bytes32 moduleKey) external;
function unlock(uint256 tokenId, bytes32 moduleKey) external;
function isLocked(uint256 tokenId) external view returns (bool lockStatus);
function isLockedByModule(uint256 _tokenId, bytes32 _moduleKey) external view returns (bool _lockedByModuleStatus);
function getPositionSummary(
uint256 tokenId
) external view returns (FlatcoinStructs.PositionSummary memory positionSummary);
function fundingAdjustedLongPnLTotal() external view returns (int256 _fundingAdjustedPnL);
function fundingAdjustedLongPnLTotal(
uint32 maxAge,
bool priceDiffCheck
) external view returns (int256 _fundingAdjustedPnL);
function tokenIdNext() external view returns (uint256 tokenId);
function leverageTradingFee() external view returns (uint256 leverageTradingFee);
function checkLeverageCriteria(uint256 margin, uint256 size) external view;
function marginMin() external view returns (uint256 marginMin);
function getTradeFee(uint256 size) external view returns (uint256 tradeFee);
}// SPDX-License-Identifier: SEE LICENSE IN LICENSE
pragma solidity ^0.8.20;
import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import {FlatcoinStructs} from "../libraries/FlatcoinStructs.sol";
interface IStableModule is IERC20Metadata {
function stableCollateralPerShare() external view returns (uint256 collateralPerShare);
function executeDeposit(
address account,
uint64 executableAtTime,
FlatcoinStructs.AnnouncedStableDeposit calldata announcedDeposit
) external;
function executeWithdraw(
address account,
uint64 executableAtTime,
FlatcoinStructs.AnnouncedStableWithdraw calldata announcedWithdraw
) external returns (uint256 amountOut, uint256 withdrawFee);
function stableWithdrawFee() external view returns (uint256 stableWithdrawFee);
function stableDepositQuote(uint256 depositAmount) external view returns (uint256 amountOut);
function stableWithdrawQuote(uint256 withdrawAmount) external view returns (uint256 amountOut);
function lock(address account, uint256 amount) external;
function unlock(address account, uint256 amount) external;
function getLockedAmount(address account) external view returns (uint256 amountLocked);
}// SPDX-License-Identifier: SEE LICENSE IN LICENSE
pragma solidity ^0.8.20;
interface IPointsModule {
struct MintPoints {
address to;
uint256 amount;
}
function getUnlockTax(address account) external view returns (uint256 unlockTax);
function lockedBalance(address account) external view returns (uint256 amount);
function mintDeposit(address to, uint256 depositAmount) external;
function mintLeverageOpen(address to, uint256 size) external;
function mintTo(MintPoints memory _mintPoints) external;
function mintToMultiple(MintPoints[] memory _mintPoints) external;
function pointsPerDeposit() external view returns (uint256 depositPoints);
function pointsPerSize() external view returns (uint256 sizePoints);
function setPointsVest(uint256 _unlockTaxVest, uint256 _pointsPerSize, uint256 _pointsPerDeposit) external;
function setTreasury(address _treasury) external;
function treasury() external view returns (address treasury);
function unlock(uint256 amount) external;
function unlockAll() external;
function unlockTaxVest() external view returns (uint256 unlockTaxVest);
function unlockTime(address account) external view returns (uint256 unlockTime);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/ERC20.sol)
pragma solidity ^0.8.20;
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import {ContextUpgradeable} from "../../utils/ContextUpgradeable.sol";
import {IERC20Errors} from "@openzeppelin/contracts/interfaces/draft-IERC6093.sol";
import {Initializable} from "../../proxy/utils/Initializable.sol";
/**
* @dev Implementation of the {IERC20} interface.
*
* This implementation is agnostic to the way tokens are created. This means
* that a supply mechanism has to be added in a derived contract using {_mint}.
*
* TIP: For a detailed writeup see our guide
* https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How
* to implement supply mechanisms].
*
* The default value of {decimals} is 18. To change this, you should override
* this function so it returns a different value.
*
* We have followed general OpenZeppelin Contracts guidelines: functions revert
* instead returning `false` on failure. This behavior is nonetheless
* conventional and does not conflict with the expectations of ERC20
* applications.
*
* Additionally, an {Approval} event is emitted on calls to {transferFrom}.
* This allows applications to reconstruct the allowance for all accounts just
* by listening to said events. Other implementations of the EIP may not emit
* these events, as it isn't required by the specification.
*/
abstract contract ERC20Upgradeable is Initializable, ContextUpgradeable, IERC20, IERC20Metadata, IERC20Errors {
/// @custom:storage-location erc7201:openzeppelin.storage.ERC20
struct ERC20Storage {
mapping(address account => uint256) _balances;
mapping(address account => mapping(address spender => uint256)) _allowances;
uint256 _totalSupply;
string _name;
string _symbol;
}
// keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.ERC20")) - 1)) & ~bytes32(uint256(0xff))
bytes32 private constant ERC20StorageLocation = 0x52c63247e1f47db19d5ce0460030c497f067ca4cebf71ba98eeadabe20bace00;
function _getERC20Storage() private pure returns (ERC20Storage storage $) {
assembly {
$.slot := ERC20StorageLocation
}
}
/**
* @dev Sets the values for {name} and {symbol}.
*
* All two of these values are immutable: they can only be set once during
* construction.
*/
function __ERC20_init(string memory name_, string memory symbol_) internal onlyInitializing {
__ERC20_init_unchained(name_, symbol_);
}
function __ERC20_init_unchained(string memory name_, string memory symbol_) internal onlyInitializing {
ERC20Storage storage $ = _getERC20Storage();
$._name = name_;
$._symbol = symbol_;
}
/**
* @dev Returns the name of the token.
*/
function name() public view virtual returns (string memory) {
ERC20Storage storage $ = _getERC20Storage();
return $._name;
}
/**
* @dev Returns the symbol of the token, usually a shorter version of the
* name.
*/
function symbol() public view virtual returns (string memory) {
ERC20Storage storage $ = _getERC20Storage();
return $._symbol;
}
/**
* @dev Returns the number of decimals used to get its user representation.
* For example, if `decimals` equals `2`, a balance of `505` tokens should
* be displayed to a user as `5.05` (`505 / 10 ** 2`).
*
* Tokens usually opt for a value of 18, imitating the relationship between
* Ether and Wei. This is the default value returned by this function, unless
* it's overridden.
*
* NOTE: This information is only used for _display_ purposes: it in
* no way affects any of the arithmetic of the contract, including
* {IERC20-balanceOf} and {IERC20-transfer}.
*/
function decimals() public view virtual returns (uint8) {
return 18;
}
/**
* @dev See {IERC20-totalSupply}.
*/
function totalSupply() public view virtual returns (uint256) {
ERC20Storage storage $ = _getERC20Storage();
return $._totalSupply;
}
/**
* @dev See {IERC20-balanceOf}.
*/
function balanceOf(address account) public view virtual returns (uint256) {
ERC20Storage storage $ = _getERC20Storage();
return $._balances[account];
}
/**
* @dev See {IERC20-transfer}.
*
* Requirements:
*
* - `to` cannot be the zero address.
* - the caller must have a balance of at least `value`.
*/
function transfer(address to, uint256 value) public virtual returns (bool) {
address owner = _msgSender();
_transfer(owner, to, value);
return true;
}
/**
* @dev See {IERC20-allowance}.
*/
function allowance(address owner, address spender) public view virtual returns (uint256) {
ERC20Storage storage $ = _getERC20Storage();
return $._allowances[owner][spender];
}
/**
* @dev See {IERC20-approve}.
*
* NOTE: If `value` is the maximum `uint256`, the allowance is not updated on
* `transferFrom`. This is semantically equivalent to an infinite approval.
*
* Requirements:
*
* - `spender` cannot be the zero address.
*/
function approve(address spender, uint256 value) public virtual returns (bool) {
address owner = _msgSender();
_approve(owner, spender, value);
return true;
}
/**
* @dev See {IERC20-transferFrom}.
*
* Emits an {Approval} event indicating the updated allowance. This is not
* required by the EIP. See the note at the beginning of {ERC20}.
*
* NOTE: Does not update the allowance if the current allowance
* is the maximum `uint256`.
*
* Requirements:
*
* - `from` and `to` cannot be the zero address.
* - `from` must have a balance of at least `value`.
* - the caller must have allowance for ``from``'s tokens of at least
* `value`.
*/
function transferFrom(address from, address to, uint256 value) public virtual returns (bool) {
address spender = _msgSender();
_spendAllowance(from, spender, value);
_transfer(from, to, value);
return true;
}
/**
* @dev Moves a `value` amount of tokens from `from` to `to`.
*
* This internal function is equivalent to {transfer}, and can be used to
* e.g. implement automatic token fees, slashing mechanisms, etc.
*
* Emits a {Transfer} event.
*
* NOTE: This function is not virtual, {_update} should be overridden instead.
*/
function _transfer(address from, address to, uint256 value) internal {
if (from == address(0)) {
revert ERC20InvalidSender(address(0));
}
if (to == address(0)) {
revert ERC20InvalidReceiver(address(0));
}
_update(from, to, value);
}
/**
* @dev Transfers a `value` amount of tokens from `from` to `to`, or alternatively mints (or burns) if `from`
* (or `to`) is the zero address. All customizations to transfers, mints, and burns should be done by overriding
* this function.
*
* Emits a {Transfer} event.
*/
function _update(address from, address to, uint256 value) internal virtual {
ERC20Storage storage $ = _getERC20Storage();
if (from == address(0)) {
// Overflow check required: The rest of the code assumes that totalSupply never overflows
$._totalSupply += value;
} else {
uint256 fromBalance = $._balances[from];
if (fromBalance < value) {
revert ERC20InsufficientBalance(from, fromBalance, value);
}
unchecked {
// Overflow not possible: value <= fromBalance <= totalSupply.
$._balances[from] = fromBalance - value;
}
}
if (to == address(0)) {
unchecked {
// Overflow not possible: value <= totalSupply or value <= fromBalance <= totalSupply.
$._totalSupply -= value;
}
} else {
unchecked {
// Overflow not possible: balance + value is at most totalSupply, which we know fits into a uint256.
$._balances[to] += value;
}
}
emit Transfer(from, to, value);
}
/**
* @dev Creates a `value` amount of tokens and assigns them to `account`, by transferring it from address(0).
* Relies on the `_update` mechanism
*
* Emits a {Transfer} event with `from` set to the zero address.
*
* NOTE: This function is not virtual, {_update} should be overridden instead.
*/
function _mint(address account, uint256 value) internal {
if (account == address(0)) {
revert ERC20InvalidReceiver(address(0));
}
_update(address(0), account, value);
}
/**
* @dev Destroys a `value` amount of tokens from `account`, lowering the total supply.
* Relies on the `_update` mechanism.
*
* Emits a {Transfer} event with `to` set to the zero address.
*
* NOTE: This function is not virtual, {_update} should be overridden instead
*/
function _burn(address account, uint256 value) internal {
if (account == address(0)) {
revert ERC20InvalidSender(address(0));
}
_update(account, address(0), value);
}
/**
* @dev Sets `value` as the allowance of `spender` over the `owner` s tokens.
*
* This internal function is equivalent to `approve`, and can be used to
* e.g. set automatic allowances for certain subsystems, etc.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `owner` cannot be the zero address.
* - `spender` cannot be the zero address.
*
* Overrides to this logic should be done to the variant with an additional `bool emitEvent` argument.
*/
function _approve(address owner, address spender, uint256 value) internal {
_approve(owner, spender, value, true);
}
/**
* @dev Variant of {_approve} with an optional flag to enable or disable the {Approval} event.
*
* By default (when calling {_approve}) the flag is set to true. On the other hand, approval changes made by
* `_spendAllowance` during the `transferFrom` operation set the flag to false. This saves gas by not emitting any
* `Approval` event during `transferFrom` operations.
*
* Anyone who wishes to continue emitting `Approval` events on the`transferFrom` operation can force the flag to
* true using the following override:
* ```
* function _approve(address owner, address spender, uint256 value, bool) internal virtual override {
* super._approve(owner, spender, value, true);
* }
* ```
*
* Requirements are the same as {_approve}.
*/
function _approve(address owner, address spender, uint256 value, bool emitEvent) internal virtual {
ERC20Storage storage $ = _getERC20Storage();
if (owner == address(0)) {
revert ERC20InvalidApprover(address(0));
}
if (spender == address(0)) {
revert ERC20InvalidSpender(address(0));
}
$._allowances[owner][spender] = value;
if (emitEvent) {
emit Approval(owner, spender, value);
}
}
/**
* @dev Updates `owner` s allowance for `spender` based on spent `value`.
*
* Does not update the allowance value in case of infinite allowance.
* Revert if not enough allowance is available.
*
* Does not emit an {Approval} event.
*/
function _spendAllowance(address owner, address spender, uint256 value) internal virtual {
uint256 currentAllowance = allowance(owner, spender);
if (currentAllowance != type(uint256).max) {
if (currentAllowance < value) {
revert ERC20InsufficientAllowance(spender, currentAllowance, value);
}
unchecked {
_approve(owner, spender, currentAllowance - value, false);
}
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/math/SignedMath.sol)
pragma solidity ^0.8.20;
/**
* @dev Standard signed math utilities missing in the Solidity language.
*/
library SignedMath {
/**
* @dev Returns the largest of two signed numbers.
*/
function max(int256 a, int256 b) internal pure returns (int256) {
return a > b ? a : b;
}
/**
* @dev Returns the smallest of two signed numbers.
*/
function min(int256 a, int256 b) internal pure returns (int256) {
return a < b ? a : b;
}
/**
* @dev Returns the average of two signed numbers without overflow.
* The result is rounded towards zero.
*/
function average(int256 a, int256 b) internal pure returns (int256) {
// Formula from the book "Hacker's Delight"
int256 x = (a & b) + ((a ^ b) >> 1);
return x + (int256(uint256(x) >> 255) & (a ^ b));
}
/**
* @dev Returns the absolute unsigned value of a signed value.
*/
function abs(int256 n) internal pure returns (uint256) {
unchecked {
// must be unchecked in order to support `n = type(int256).min`
return uint256(n >= 0 ? n : -n);
}
}
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.20;
/// @title DecimalMath
/// @author dHEDGE
/// @author Adapted from Synthetix <https://github.com/Synthetixio/synthetix/blob/cbd8666f4331ee95fcc667ec7345d13c8ba77efb/contracts/SignedSafeDecimalMath.sol>
/// and <https://github.com/Synthetixio/synthetix/blob/cbd8666f4331ee95fcc667ec7345d13c8ba77efb/contracts/SafeDecimalMath.sol>
/// @notice Library for fixed point math.
// TODO: Explore if Solmate FixedPointMathLib can be used instead. <https://github.com/transmissions11/solmate/blob/main/src/utils/FixedPointMathLib.sol>
// solhint-disable gas-named-return-values
library DecimalMath {
/* Number of decimal places in the representations. */
uint8 public constant DECIMALS = 18;
uint8 public constant HIGH_PRECISION_DECIMALS = 27;
/* The number representing 1.0. */
int256 public constant UNIT = 1e18;
/* The number representing 1.0 for higher fidelity numbers. */
int256 public constant PRECISE_UNIT = int256(10 ** uint256(HIGH_PRECISION_DECIMALS));
int256 private constant _UNIT_TO_HIGH_PRECISION_CONVERSION_FACTOR =
int256(10 ** uint256(HIGH_PRECISION_DECIMALS - DECIMALS));
/**
* @return The result of multiplying x and y, interpreting the operands as fixed-point
* decimals.
*
* @dev A unit factor is divided out after the product of x and y is evaluated,
* so that product must be less than 2**256. As this is an integer division,
* the internal division always rounds down. This helps save on gas. Rounding
* is more expensive on gas.
*/
function _multiplyDecimal(int256 x, int256 y) internal pure returns (int256) {
/* Divide by UNIT to remove the extra factor introduced by the product. */
return (x * y) / UNIT;
}
/**
* @return The result of multiplying x and y, interpreting the operands as fixed-point
* decimals.
*
* @dev A unit factor is divided out after the product of x and y is evaluated,
* so that product must be less than 2**256. As this is an integer division,
* the internal division always rounds down. This helps save on gas. Rounding
* is more expensive on gas.
*/
function _multiplyDecimal(uint256 x, uint256 y) internal pure returns (uint256) {
/* Divide by UNIT to remove the extra factor introduced by the product. */
return (x * y) / uint256(UNIT);
}
/**
* @return The result of safely multiplying x and y, interpreting the operands
* as fixed-point decimals of a precise unit.
*
* @dev The operands should be in the precise unit factor which will be
* divided out after the product of x and y is evaluated, so that product must be
* less than 2**256.
*
* Unlike multiplyDecimal, this function rounds the result to the nearest increment.
* Rounding is useful when you need to retain fidelity for small decimal numbers
* (eg. small fractions or percentages).
*/
function _multiplyDecimalRoundPrecise(int256 x, int256 y) internal pure returns (int256) {
return _multiplyDecimalRound(x, y, PRECISE_UNIT);
}
/**
* @return The result of safely multiplying x and y, interpreting the operands
* as fixed-point decimals of a precise unit.
*
* @dev The operands should be in the precise unit factor which will be
* divided out after the product of x and y is evaluated, so that product must be
* less than 2**256.
*
* Unlike multiplyDecimal, this function rounds the result to the nearest increment.
* Rounding is useful when you need to retain fidelity for small decimal numbers
* (eg. small fractions or percentages).
*/
function _multiplyDecimalRoundPrecise(uint256 x, uint256 y) internal pure returns (uint256) {
return _multiplyDecimalRound(x, y, uint256(PRECISE_UNIT));
}
/**
* @return The result of safely multiplying x and y, interpreting the operands
* as fixed-point decimals of a standard unit.
*
* @dev The operands should be in the standard unit factor which will be
* divided out after the product of x and y is evaluated, so that product must be
* less than 2**256.
*
* Unlike multiplyDecimal, this function rounds the result to the nearest increment.
* Rounding is useful when you need to retain fidelity for small decimal numbers
* (eg. small fractions or percentages).
*/
function _multiplyDecimalRound(int256 x, int256 y) internal pure returns (int256) {
return _multiplyDecimalRound(x, y, UNIT);
}
/**
* @return The result of safely multiplying x and y, interpreting the operands
* as fixed-point decimals of a standard unit.
*
* @dev The operands should be in the standard unit factor which will be
* divided out after the product of x and y is evaluated, so that product must be
* less than 2**256.
*
* Unlike multiplyDecimal, this function rounds the result to the nearest increment.
* Rounding is useful when you need to retain fidelity for small decimal numbers
* (eg. small fractions or percentages).
*/
function _multiplyDecimalRound(uint256 x, uint256 y) internal pure returns (uint256) {
return _multiplyDecimalRound(x, y, uint256(UNIT));
}
/**
* @return The result of safely dividing x and y. The return value is a high
* precision decimal.
*
* @dev y is divided after the product of x and the standard precision unit
* is evaluated, so the product of x and UNIT must be less than 2**256. As
* this is an integer division, the result is always rounded down.
* This helps save on gas. Rounding is more expensive on gas.
*/
function _divideDecimal(int256 x, int256 y) internal pure returns (int256) {
/* Reintroduce the UNIT factor that will be divided out by y. */
return (x * UNIT) / y;
}
/**
* @return The result of safely dividing x and y. The return value is a high
* precision decimal.
*
* @dev y is divided after the product of x and the standard precision unit
* is evaluated, so the product of x and UNIT must be less than 2**256. As
* this is an integer division, the result is always rounded down.
* This helps save on gas. Rounding is more expensive on gas.
*/
function _divideDecimal(uint256 x, uint256 y) internal pure returns (uint256) {
/* Reintroduce the UNIT factor that will be divided out by y. */
return (x * uint256(UNIT)) / y;
}
/**
* @return The result of safely dividing x and y. The return value is as a rounded
* standard precision decimal.
*
* @dev y is divided after the product of x and the standard precision unit
* is evaluated, so the product of x and the standard precision unit must
* be less than 2**256. The result is rounded to the nearest increment.
*/
function _divideDecimalRound(int256 x, int256 y) internal pure returns (int256) {
return _divideDecimalRound(x, y, UNIT);
}
/**
* @return The result of safely dividing x and y. The return value is as a rounded
* high precision decimal.
*
* @dev y is divided after the product of x and the high precision unit
* is evaluated, so the product of x and the high precision unit must
* be less than 2**256. The result is rounded to the nearest increment.
*/
function _divideDecimalRoundPrecise(int256 x, int256 y) internal pure returns (int256) {
return _divideDecimalRound(x, y, PRECISE_UNIT);
}
/**
* @return The result of safely dividing x and y. The return value is as a rounded
* high precision decimal.
*
* @dev y is divided after the product of x and the high precision unit
* is evaluated, so the product of x and the high precision unit must
* be less than 2**256. The result is rounded to the nearest increment.
*/
function _divideDecimalRoundPrecise(uint256 x, uint256 y) internal pure returns (uint256) {
return _divideDecimalRound(x, y, uint256(PRECISE_UNIT));
}
/**
* @dev Convert a standard decimal representation to a high precision one.
*/
function _decimalToPreciseDecimal(int256 i) internal pure returns (int256) {
return i * _UNIT_TO_HIGH_PRECISION_CONVERSION_FACTOR;
}
/**
* @dev Convert a standard decimal representation to a high precision one.
*/
function _decimalToPreciseDecimal(uint256 i) internal pure returns (uint256) {
return i * uint256(_UNIT_TO_HIGH_PRECISION_CONVERSION_FACTOR);
}
/**
* @dev Convert a high precision decimal to a standard decimal representation.
*/
function _preciseDecimalToDecimal(int256 i) internal pure returns (int256) {
int256 quotientTimesTen = i / (_UNIT_TO_HIGH_PRECISION_CONVERSION_FACTOR / 10);
return _roundDividingByTen(quotientTimesTen);
}
/**
* @dev Convert a high precision decimal to a standard decimal representation.
*/
function _preciseDecimalToDecimal(uint256 i) internal pure returns (uint256) {
uint256 quotientTimesTen = i / (uint256(_UNIT_TO_HIGH_PRECISION_CONVERSION_FACTOR) / 10);
if (quotientTimesTen % 10 >= 5) {
quotientTimesTen += 10;
}
return quotientTimesTen / 10;
}
/**
* @dev Rounds an input with an extra zero of precision, returning the result without the extra zero.
* Half increments round away from zero; positive numbers at a half increment are rounded up,
* while negative such numbers are rounded down. This behaviour is designed to be consistent with the
* unsigned version of this library (SafeDecimalMath).
*/
function _roundDividingByTen(int256 valueTimesTen) private pure returns (int256) {
int256 increment;
if (valueTimesTen % 10 >= 5) {
increment = 10;
} else if (valueTimesTen % 10 <= -5) {
increment = -10;
}
return (valueTimesTen + increment) / 10;
}
/**
* @return The result of safely multiplying x and y, interpreting the operands
* as fixed-point decimals of the specified precision unit.
*
* @dev The operands should be in the form of a the specified unit factor which will be
* divided out after the product of x and y is evaluated, so that product must be
* less than 2**256.
*
* Unlike multiplyDecimal, this function rounds the result to the nearest increment.
* Rounding is useful when you need to retain fidelity for small decimal numbers
* (eg. small fractions or percentages).
*/
function _multiplyDecimalRound(int256 x, int256 y, int256 precisionUnit) private pure returns (int256) {
/* Divide by UNIT to remove the extra factor introduced by the product. */
int256 quotientTimesTen = (x * y) / (precisionUnit / 10);
return _roundDividingByTen(quotientTimesTen);
}
/**
* @return The result of safely multiplying x and y, interpreting the operands
* as fixed-point decimals of the specified precision unit.
*
* @dev The operands should be in the form of a the specified unit factor which will be
* divided out after the product of x and y is evaluated, so that product must be
* less than 2**256.
*
* Unlike multiplyDecimal, this function rounds the result to the nearest increment.
* Rounding is useful when you need to retain fidelity for small decimal numbers
* (eg. small fractions or percentages).
*/
function _multiplyDecimalRound(uint256 x, uint256 y, uint256 precisionUnit) private pure returns (uint256) {
/* Divide by UNIT to remove the extra factor introduced by the product. */
uint256 quotientTimesTen = (x * y) / (precisionUnit / 10);
if (quotientTimesTen % 10 >= 5) {
quotientTimesTen += 10;
}
return quotientTimesTen / 10;
}
/**
* @return The result of safely dividing x and y. The return value is as a rounded
* decimal in the precision unit specified in the parameter.
*
* @dev y is divided after the product of x and the specified precision unit
* is evaluated, so the product of x and the specified precision unit must
* be less than 2**256. The result is rounded to the nearest increment.
*/
function _divideDecimalRound(int256 x, int256 y, int256 precisionUnit) private pure returns (int256) {
int256 resultTimesTen = (x * precisionUnit * 10) / y;
return _roundDividingByTen(resultTimesTen);
}
/**
* @return The result of safely dividing x and y. The return value is as a rounded
* decimal in the precision unit specified in the parameter.
*
* @dev y is divided after the product of x and the specified precision unit
* is evaluated, so the product of x and the specified precision unit must
* be less than 2**256. The result is rounded to the nearest increment.
*/
function _divideDecimalRound(uint256 x, uint256 y, uint256 precisionUnit) private pure returns (uint256) {
uint256 resultTimesTen = (x * (precisionUnit * 10)) / y;
if (resultTimesTen % 10 >= 5) {
resultTimesTen += 10;
}
return resultTimesTen / 10;
}
}// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity 0.8.20;
interface IChainlinkAggregatorV3 {
function decimals() external view returns (uint8 decimals);
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/consumers/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 does not store the price updates on-chain.
///
/// 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);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)
pragma solidity ^0.8.20;
import {ContextUpgradeable} from "../utils/ContextUpgradeable.sol";
import {Initializable} from "../proxy/utils/Initializable.sol";
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* The initial owner is set to the address provided by the deployer. This can
* later be changed with {transferOwnership}.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be applied to your functions to restrict their use to
* the owner.
*/
abstract contract OwnableUpgradeable is Initializable, ContextUpgradeable {
/// @custom:storage-location erc7201:openzeppelin.storage.Ownable
struct OwnableStorage {
address _owner;
}
// keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Ownable")) - 1)) & ~bytes32(uint256(0xff))
bytes32 private constant OwnableStorageLocation = 0x9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300;
function _getOwnableStorage() private pure returns (OwnableStorage storage $) {
assembly {
$.slot := OwnableStorageLocation
}
}
/**
* @dev The caller account is not authorized to perform an operation.
*/
error OwnableUnauthorizedAccount(address account);
/**
* @dev The owner is not a valid owner account. (eg. `address(0)`)
*/
error OwnableInvalidOwner(address owner);
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Initializes the contract setting the address provided by the deployer as the initial owner.
*/
function __Ownable_init(address initialOwner) internal onlyInitializing {
__Ownable_init_unchained(initialOwner);
}
function __Ownable_init_unchained(address initialOwner) internal onlyInitializing {
if (initialOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(initialOwner);
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
_checkOwner();
_;
}
/**
* @dev Returns the address of the current owner.
*/
function owner() public view virtual returns (address) {
OwnableStorage storage $ = _getOwnableStorage();
return $._owner;
}
/**
* @dev Throws if the sender is not the owner.
*/
function _checkOwner() internal view virtual {
if (owner() != _msgSender()) {
revert OwnableUnauthorizedAccount(_msgSender());
}
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions. Can only be called by the current owner.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby disabling any functionality that is only available to the owner.
*/
function renounceOwnership() public virtual onlyOwner {
_transferOwnership(address(0));
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public virtual onlyOwner {
if (newOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Internal function without access restriction.
*/
function _transferOwnership(address newOwner) internal virtual {
OwnableStorage storage $ = _getOwnableStorage();
address oldOwner = $._owner;
$._owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (proxy/utils/Initializable.sol)
pragma solidity ^0.8.20;
/**
* @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
* behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
* external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
* function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
*
* The initialization functions use a version number. Once a version number is used, it is consumed and cannot be
* reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in
* case an upgrade adds a module that needs to be initialized.
*
* For example:
*
* [.hljs-theme-light.nopadding]
* ```solidity
* contract MyToken is ERC20Upgradeable {
* function initialize() initializer public {
* __ERC20_init("MyToken", "MTK");
* }
* }
*
* contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {
* function initializeV2() reinitializer(2) public {
* __ERC20Permit_init("MyToken");
* }
* }
* ```
*
* TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
* possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
*
* CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
* that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
*
* [CAUTION]
* ====
* Avoid leaving a contract uninitialized.
*
* An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
* contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke
* the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:
*
* [.hljs-theme-light.nopadding]
* ```
* /// @custom:oz-upgrades-unsafe-allow constructor
* constructor() {
* _disableInitializers();
* }
* ```
* ====
*/
abstract contract Initializable {
/**
* @dev Storage of the initializable contract.
*
* It's implemented on a custom ERC-7201 namespace to reduce the risk of storage collisions
* when using with upgradeable contracts.
*
* @custom:storage-location erc7201:openzeppelin.storage.Initializable
*/
struct InitializableStorage {
/**
* @dev Indicates that the contract has been initialized.
*/
uint64 _initialized;
/**
* @dev Indicates that the contract is in the process of being initialized.
*/
bool _initializing;
}
// keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Initializable")) - 1)) & ~bytes32(uint256(0xff))
bytes32 private constant INITIALIZABLE_STORAGE = 0xf0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00;
/**
* @dev The contract is already initialized.
*/
error InvalidInitialization();
/**
* @dev The contract is not initializing.
*/
error NotInitializing();
/**
* @dev Triggered when the contract has been initialized or reinitialized.
*/
event Initialized(uint64 version);
/**
* @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
* `onlyInitializing` functions can be used to initialize parent contracts.
*
* Similar to `reinitializer(1)`, except that in the context of a constructor an `initializer` may be invoked any
* number of times. This behavior in the constructor can be useful during testing and is not expected to be used in
* production.
*
* Emits an {Initialized} event.
*/
modifier initializer() {
// solhint-disable-next-line var-name-mixedcase
InitializableStorage storage $ = _getInitializableStorage();
// Cache values to avoid duplicated sloads
bool isTopLevelCall = !$._initializing;
uint64 initialized = $._initialized;
// Allowed calls:
// - initialSetup: the contract is not in the initializing state and no previous version was
// initialized
// - construction: the contract is initialized at version 1 (no reininitialization) and the
// current contract is just being deployed
bool initialSetup = initialized == 0 && isTopLevelCall;
bool construction = initialized == 1 && address(this).code.length == 0;
if (!initialSetup && !construction) {
revert InvalidInitialization();
}
$._initialized = 1;
if (isTopLevelCall) {
$._initializing = true;
}
_;
if (isTopLevelCall) {
$._initializing = false;
emit Initialized(1);
}
}
/**
* @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
* contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
* used to initialize parent contracts.
*
* A reinitializer may be used after the original initialization step. This is essential to configure modules that
* are added through upgrades and that require initialization.
*
* When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`
* cannot be nested. If one is invoked in the context of another, execution will revert.
*
* Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
* a contract, executing them in the right order is up to the developer or operator.
*
* WARNING: Setting the version to 2**64 - 1 will prevent any future reinitialization.
*
* Emits an {Initialized} event.
*/
modifier reinitializer(uint64 version) {
// solhint-disable-next-line var-name-mixedcase
InitializableStorage storage $ = _getInitializableStorage();
if ($._initializing || $._initialized >= version) {
revert InvalidInitialization();
}
$._initialized = version;
$._initializing = true;
_;
$._initializing = false;
emit Initialized(version);
}
/**
* @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
* {initializer} and {reinitializer} modifiers, directly or indirectly.
*/
modifier onlyInitializing() {
_checkInitializing();
_;
}
/**
* @dev Reverts if the contract is not in an initializing state. See {onlyInitializing}.
*/
function _checkInitializing() internal view virtual {
if (!_isInitializing()) {
revert NotInitializing();
}
}
/**
* @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
* Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
* to any version. It is recommended to use this to lock implementation contracts that are designed to be called
* through proxies.
*
* Emits an {Initialized} event the first time it is successfully executed.
*/
function _disableInitializers() internal virtual {
// solhint-disable-next-line var-name-mixedcase
InitializableStorage storage $ = _getInitializableStorage();
if ($._initializing) {
revert InvalidInitialization();
}
if ($._initialized != type(uint64).max) {
$._initialized = type(uint64).max;
emit Initialized(type(uint64).max);
}
}
/**
* @dev Returns the highest version that has been initialized. See {reinitializer}.
*/
function _getInitializedVersion() internal view returns (uint64) {
return _getInitializableStorage()._initialized;
}
/**
* @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.
*/
function _isInitializing() internal view returns (bool) {
return _getInitializableStorage()._initializing;
}
/**
* @dev Returns a pointer to the storage namespace.
*/
// solhint-disable-next-line var-name-mixedcase
function _getInitializableStorage() private pure returns (InitializableStorage storage $) {
assembly {
$.slot := INITIALIZABLE_STORAGE
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
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
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC721/extensions/IERC721Enumerable.sol)
pragma solidity ^0.8.20;
import {IERC721} from "../IERC721.sol";
/**
* @title ERC-721 Non-Fungible Token Standard, optional enumeration extension
* @dev See https://eips.ethereum.org/EIPS/eip-721
*/
interface IERC721Enumerable is IERC721 {
/**
* @dev Returns the total amount of tokens stored by the contract.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns a token ID owned by `owner` at a given `index` of its token list.
* Use along with {balanceOf} to enumerate all of ``owner``'s tokens.
*/
function tokenOfOwnerByIndex(address owner, uint256 index) external view returns (uint256);
/**
* @dev Returns a token ID at a given `index` of all the tokens stored by the contract.
* Use along with {totalSupply} to enumerate all tokens.
*/
function tokenByIndex(uint256 index) external view returns (uint256);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.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 ERC20 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.0.1) (utils/Context.sol)
pragma solidity ^0.8.20;
import {Initializable} from "../proxy/utils/Initializable.sol";
/**
* @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 ContextUpgradeable is Initializable {
function __Context_init() internal onlyInitializing {
}
function __Context_init_unchained() internal onlyInitializing {
}
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.0.0) (interfaces/draft-IERC6093.sol)
pragma solidity ^0.8.20;
/**
* @dev Standard ERC20 Errors
* Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC20 tokens.
*/
interface IERC20Errors {
/**
* @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
* @param balance Current balance for the interacting account.
* @param needed Minimum amount required to perform a transfer.
*/
error ERC20InsufficientBalance(address sender, uint256 balance, uint256 needed);
/**
* @dev Indicates a failure with the token `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
*/
error ERC20InvalidSender(address sender);
/**
* @dev Indicates a failure with the token `receiver`. Used in transfers.
* @param receiver Address to which tokens are being transferred.
*/
error ERC20InvalidReceiver(address receiver);
/**
* @dev Indicates a failure with the `spender`’s `allowance`. Used in transfers.
* @param spender Address that may be allowed to operate on tokens without being their owner.
* @param allowance Amount of tokens a `spender` is allowed to operate with.
* @param needed Minimum amount required to perform a transfer.
*/
error ERC20InsufficientAllowance(address spender, uint256 allowance, uint256 needed);
/**
* @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
* @param approver Address initiating an approval operation.
*/
error ERC20InvalidApprover(address approver);
/**
* @dev Indicates a failure with the `spender` to be approved. Used in approvals.
* @param spender Address that may be allowed to operate on tokens without being their owner.
*/
error ERC20InvalidSpender(address spender);
}
/**
* @dev Standard ERC721 Errors
* Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC721 tokens.
*/
interface IERC721Errors {
/**
* @dev Indicates that an address can't be an owner. For example, `address(0)` is a forbidden owner in EIP-20.
* Used in balance queries.
* @param owner Address of the current owner of a token.
*/
error ERC721InvalidOwner(address owner);
/**
* @dev Indicates a `tokenId` whose `owner` is the zero address.
* @param tokenId Identifier number of a token.
*/
error ERC721NonexistentToken(uint256 tokenId);
/**
* @dev Indicates an error related to the ownership over a particular token. Used in transfers.
* @param sender Address whose tokens are being transferred.
* @param tokenId Identifier number of a token.
* @param owner Address of the current owner of a token.
*/
error ERC721IncorrectOwner(address sender, uint256 tokenId, address owner);
/**
* @dev Indicates a failure with the token `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
*/
error ERC721InvalidSender(address sender);
/**
* @dev Indicates a failure with the token `receiver`. Used in transfers.
* @param receiver Address to which tokens are being transferred.
*/
error ERC721InvalidReceiver(address receiver);
/**
* @dev Indicates a failure with the `operator`’s approval. Used in transfers.
* @param operator Address that may be allowed to operate on tokens without being their owner.
* @param tokenId Identifier number of a token.
*/
error ERC721InsufficientApproval(address operator, uint256 tokenId);
/**
* @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
* @param approver Address initiating an approval operation.
*/
error ERC721InvalidApprover(address approver);
/**
* @dev Indicates a failure with the `operator` to be approved. Used in approvals.
* @param operator Address that may be allowed to operate on tokens without being their owner.
*/
error ERC721InvalidOperator(address operator);
}
/**
* @dev Standard ERC1155 Errors
* Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC1155 tokens.
*/
interface IERC1155Errors {
/**
* @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
* @param balance Current balance for the interacting account.
* @param needed Minimum amount required to perform a transfer.
* @param tokenId Identifier number of a token.
*/
error ERC1155InsufficientBalance(address sender, uint256 balance, uint256 needed, uint256 tokenId);
/**
* @dev Indicates a failure with the token `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
*/
error ERC1155InvalidSender(address sender);
/**
* @dev Indicates a failure with the token `receiver`. Used in transfers.
* @param receiver Address to which tokens are being transferred.
*/
error ERC1155InvalidReceiver(address receiver);
/**
* @dev Indicates a failure with the `operator`’s approval. Used in transfers.
* @param operator Address that may be allowed to operate on tokens without being their owner.
* @param owner Address of the current owner of a token.
*/
error ERC1155MissingApprovalForAll(address operator, address owner);
/**
* @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
* @param approver Address initiating an approval operation.
*/
error ERC1155InvalidApprover(address approver);
/**
* @dev Indicates a failure with the `operator` to be approved. Used in approvals.
* @param operator Address that may be allowed to operate on tokens without being their owner.
*/
error ERC1155InvalidOperator(address operator);
/**
* @dev Indicates an array length mismatch between ids and values in a safeBatchTransferFrom operation.
* Used in batch transfers.
* @param idsLength Length of the array of token identifiers
* @param valuesLength Length of the array of token amounts
*/
error ERC1155InvalidArrayLength(uint256 idsLength, uint256 valuesLength);
}// 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/consumers/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: 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
);
/// @dev Emitted when a batch price update is processed successfully.
/// @param chainId ID of the source chain that the batch price update comes from.
/// @param sequenceNumber Sequence number of the batch price update.
event BatchPriceFeedUpdate(uint16 chainId, uint64 sequenceNumber);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC721/IERC721.sol)
pragma solidity ^0.8.20;
import {IERC165} from "../../utils/introspection/IERC165.sol";
/**
* @dev Required interface of an ERC721 compliant contract.
*/
interface IERC721 is IERC165 {
/**
* @dev Emitted when `tokenId` token is transferred from `from` to `to`.
*/
event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
/**
* @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.
*/
event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
/**
* @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.
*/
event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
/**
* @dev Returns the number of tokens in ``owner``'s account.
*/
function balanceOf(address owner) external view returns (uint256 balance);
/**
* @dev Returns the owner of the `tokenId` token.
*
* Requirements:
*
* - `tokenId` must exist.
*/
function ownerOf(uint256 tokenId) external view returns (address owner);
/**
* @dev Safely transfers `tokenId` token from `from` to `to`.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must exist and be owned by `from`.
* - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
* - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon
* a safe transfer.
*
* Emits a {Transfer} event.
*/
function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external;
/**
* @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
* are aware of the ERC721 protocol to prevent tokens from being forever locked.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must exist and be owned by `from`.
* - If the caller is not `from`, it must have been allowed to move this token by either {approve} or
* {setApprovalForAll}.
* - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon
* a safe transfer.
*
* Emits a {Transfer} event.
*/
function safeTransferFrom(address from, address to, uint256 tokenId) external;
/**
* @dev Transfers `tokenId` token from `from` to `to`.
*
* WARNING: Note that the caller is responsible to confirm that the recipient is capable of receiving ERC721
* or else they may be permanently lost. Usage of {safeTransferFrom} prevents loss, though the caller must
* understand this adds an external call which potentially creates a reentrancy vulnerability.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must be owned by `from`.
* - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
*
* Emits a {Transfer} event.
*/
function transferFrom(address from, address to, uint256 tokenId) external;
/**
* @dev Gives permission to `to` to transfer `tokenId` token to another account.
* The approval is cleared when the token is transferred.
*
* Only a single account can be approved at a time, so approving the zero address clears previous approvals.
*
* Requirements:
*
* - The caller must own the token or be an approved operator.
* - `tokenId` must exist.
*
* Emits an {Approval} event.
*/
function approve(address to, uint256 tokenId) external;
/**
* @dev Approve or remove `operator` as an operator for the caller.
* Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.
*
* Requirements:
*
* - The `operator` cannot be the address zero.
*
* Emits an {ApprovalForAll} event.
*/
function setApprovalForAll(address operator, bool approved) external;
/**
* @dev Returns the account approved for `tokenId` token.
*
* Requirements:
*
* - `tokenId` must exist.
*/
function getApproved(uint256 tokenId) external view returns (address operator);
/**
* @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
*
* See {setApprovalForAll}
*/
function isApprovedForAll(address owner, address operator) external view returns (bool);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/IERC165.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC165 standard, as defined in the
* https://eips.ethereum.org/EIPS/eip-165[EIP].
*
* 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[EIP 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": [
"@openzeppelin/contracts/=lib/openzeppelin-contracts-upgradeable/lib/openzeppelin-contracts/contracts/",
"@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/",
"forge-std/=lib/forge-std/src/",
"openzeppelin-foundry-upgrades/=lib/openzeppelin-foundry-upgrades/src/",
"pyth-sdk-solidity/=lib/pyth-sdk-solidity/",
"solidity-stringutils/=lib/openzeppelin-foundry-upgrades/lib/solidity-stringutils/",
"ds-test/=lib/openzeppelin-contracts-upgradeable/lib/forge-std/lib/ds-test/src/",
"erc4626-tests/=lib/openzeppelin-contracts-upgradeable/lib/erc4626-tests/",
"openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/",
"openzeppelin-contracts/=lib/openzeppelin-contracts-upgradeable/lib/openzeppelin-contracts/"
],
"optimizer": {
"enabled": true,
"runs": 500
},
"metadata": {
"useLiteralContent": false,
"bytecodeHash": "ipfs",
"appendCBOR": true
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"evmVersion": "paris",
"viaIR": false,
"libraries": {}
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"minAmount","type":"uint256"}],"name":"AmountTooSmall","type":"error"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"allowance","type":"uint256"},{"internalType":"uint256","name":"needed","type":"uint256"}],"name":"ERC20InsufficientAllowance","type":"error"},{"inputs":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"uint256","name":"balance","type":"uint256"},{"internalType":"uint256","name":"needed","type":"uint256"}],"name":"ERC20InsufficientBalance","type":"error"},{"inputs":[{"internalType":"address","name":"approver","type":"address"}],"name":"ERC20InvalidApprover","type":"error"},{"inputs":[{"internalType":"address","name":"receiver","type":"address"}],"name":"ERC20InvalidReceiver","type":"error"},{"inputs":[{"internalType":"address","name":"sender","type":"address"}],"name":"ERC20InvalidSender","type":"error"},{"inputs":[{"internalType":"address","name":"spender","type":"address"}],"name":"ERC20InvalidSpender","type":"error"},{"inputs":[{"internalType":"uint256","name":"supplied","type":"uint256"},{"internalType":"uint256","name":"accepted","type":"uint256"}],"name":"HighSlippage","type":"error"},{"inputs":[{"internalType":"uint256","name":"fee","type":"uint256"}],"name":"InvalidFee","type":"error"},{"inputs":[],"name":"InvalidInitialization","type":"error"},{"inputs":[{"internalType":"uint256","name":"skewFraction","type":"uint256"}],"name":"MaxSkewReached","type":"error"},{"inputs":[],"name":"ModuleKeyEmpty","type":"error"},{"inputs":[],"name":"NotInitializing","type":"error"},{"inputs":[{"internalType":"address","name":"msgSender","type":"address"}],"name":"OnlyAuthorizedModule","type":"error"},{"inputs":[{"internalType":"address","name":"msgSender","type":"address"}],"name":"OnlyOwner","type":"error"},{"inputs":[],"name":"PriceImpactDuringFullWithdraw","type":"error"},{"inputs":[],"name":"PriceImpactDuringWithdraw","type":"error"},{"inputs":[{"internalType":"uint8","name":"bits","type":"uint8"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"SafeCastOverflowedUintDowncast","type":"error"},{"inputs":[{"internalType":"string","name":"variableName","type":"string"}],"name":"ZeroAddress","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"depositor","type":"address"},{"indexed":false,"internalType":"uint256","name":"depositAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"mintedAmount","type":"uint256"}],"name":"Deposit","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint64","name":"version","type":"uint64"}],"name":"Initialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Locked","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Unlocked","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"withdrawer","type":"address"},{"indexed":false,"internalType":"uint256","name":"withdrawAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"burnedAmount","type":"uint256"}],"name":"Withdraw","type":"event"},{"inputs":[],"name":"MIN_LIQUIDITY","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MODULE_KEY","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"},{"internalType":"uint64","name":"_executableAtTime","type":"uint64"},{"components":[{"internalType":"uint256","name":"depositAmount","type":"uint256"},{"internalType":"uint256","name":"minAmountOut","type":"uint256"},{"internalType":"address","name":"announcedBy","type":"address"}],"internalType":"struct FlatcoinStructs.AnnouncedStableDeposit","name":"_announcedDeposit","type":"tuple"}],"name":"executeDeposit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"},{"internalType":"uint64","name":"_executableAtTime","type":"uint64"},{"components":[{"internalType":"uint256","name":"withdrawAmount","type":"uint256"},{"internalType":"uint256","name":"minAmountOut","type":"uint256"}],"internalType":"struct FlatcoinStructs.AnnouncedStableWithdraw","name":"_announcedWithdraw","type":"tuple"}],"name":"executeWithdraw","outputs":[{"internalType":"uint256","name":"_amountOut","type":"uint256"},{"internalType":"uint256","name":"_withdrawFee","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"}],"name":"getLockedAmount","outputs":[{"internalType":"uint256","name":"_amountLocked","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IFlatcoinVault","name":"_vault","type":"address"},{"internalType":"uint256","name":"_stableWithdrawFee","type":"uint256"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"lock","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_stableWithdrawFee","type":"uint256"}],"name":"setStableWithdrawFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IFlatcoinVault","name":"_vault","type":"address"}],"name":"setVault","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"stableCollateralPerShare","outputs":[{"internalType":"uint256","name":"_collateralPerShare","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32","name":"_maxAge","type":"uint32"},{"internalType":"bool","name":"_priceDiffCheck","type":"bool"}],"name":"stableCollateralPerShare","outputs":[{"internalType":"uint256","name":"_collateralPerShare","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"stableCollateralTotalAfterSettlement","outputs":[{"internalType":"uint256","name":"_stableCollateralBalance","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32","name":"_maxAge","type":"uint32"},{"internalType":"bool","name":"_priceDiffCheck","type":"bool"}],"name":"stableCollateralTotalAfterSettlement","outputs":[{"internalType":"uint256","name":"_stableCollateralBalance","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_depositAmount","type":"uint256"}],"name":"stableDepositQuote","outputs":[{"internalType":"uint256","name":"_amountOut","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"stableWithdrawFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_withdrawAmount","type":"uint256"}],"name":"stableWithdrawQuote","outputs":[{"internalType":"uint256","name":"_amountOut","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"unlock","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"vault","outputs":[{"internalType":"contract IFlatcoinVault","name":"","type":"address"}],"stateMutability":"view","type":"function"}]Contract Creation Code
608060405234801561001057600080fd5b5061001961001e565b6100d0565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00805468010000000000000000900460ff161561006e5760405163f92ee8a960e01b815260040160405180910390fd5b80546001600160401b03908116146100cd5780546001600160401b0319166001600160401b0390811782556040519081527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50565b61228c80620000e06000396000f3fe608060405234801561001057600080fd5b50600436106101b95760003560e01c80636cbf48a6116100f9578063c110587511610097578063cd6dc68711610071578063cd6dc687146103b9578063d24545a7146103cc578063dd62ed3e146103df578063fbfa77cf1461043757600080fd5b8063c11058751461036b578063c149e6ae14610393578063c45383e6146103a657600080fd5b8063929ec537116100d3578063929ec5371461031457806395d89b411461033d578063a9059cbb14610345578063ac75491b1461035857600080fd5b80636cbf48a6146102c357806370a08231146102cb5780637eee288d1461030157600080fd5b8063282d3fdf116101665780633d23b9ca116101405780633d23b9ca1461028c578063543a4fa31461029557806367f77e54146102a85780636817031b146102b057600080fd5b8063282d3fdf14610255578063313ce5671461026a578063367585501461027957600080fd5b80631d109d39116101975780631d109d391461023057806321b77d631461023957806323b872dd1461024257600080fd5b806306fdde03146101be578063095ea7b3146101dc57806318160ddd146101ff575b600080fd5b6101c6610462565b6040516101d39190611bc0565b60405180910390f35b6101ef6101ea366004611c26565b610525565b60405190151581526020016101d3565b7f52c63247e1f47db19d5ce0460030c497f067ca4cebf71ba98eeadabe20bace02545b6040519081526020016101d3565b61022260645481565b61022261271081565b6101ef610250366004611c52565b61053f565b610268610263366004611c26565b610563565b005b604051601281526020016101d3565b610222610287366004611c93565b610606565b61022260005481565b6102686102a3366004611c93565b610630565b6102226106f6565b6102686102be366004611cac565b61070c565b610222610810565b6102226102d9366004611cac565b6001600160a01b03166000908152600080516020612237833981519152602052604090205490565b61026861030f366004611c26565b610821565b610222610322366004611cac565b6001600160a01b031660009081526032602052604090205490565b6101c66108bb565b6101ef610353366004611c26565b6108fa565b610222610366366004611cde565b610908565b61037e610379366004611d36565b610985565b604080519283526020830191909152016101d3565b6102686103a1366004611d89565b610cff565b6102226103b4366004611cde565b611015565b6102686103c7366004611c26565b6111c4565b6102226103da366004611c93565b611346565b6102226103ed366004611dce565b6001600160a01b0391821660009081527f52c63247e1f47db19d5ce0460030c497f067ca4cebf71ba98eeadabe20bace016020908152604080832093909416825291909152205490565b60015461044a906001600160a01b031681565b6040516001600160a01b0390911681526020016101d3565b7f52c63247e1f47db19d5ce0460030c497f067ca4cebf71ba98eeadabe20bace038054606091600080516020612237833981519152916104a190611dfc565b80601f01602080910402602001604051908101604052809291908181526020018280546104cd90611dfc565b801561051a5780601f106104ef5761010080835404028352916020019161051a565b820191906000526020600020905b8154815290600101906020018083116104fd57829003601f168201915b505050505091505090565b60003361053381858561139d565b60019150505b92915050565b60003361054d8582856113af565b61055885858561144c565b506001949350505050565b60015460405163399dd46360e01b81523360048201526001600160a01b039091169063399dd46390602401602060405180830381865afa1580156105ab573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105cf9190611e36565b15156000036105f857604051633b9e98fd60e11b81523360048201526024015b60405180910390fd5b61060282826114ab565b5050565b60006106106106f6565b61061c6012600a611f4d565b6106269084611f5c565b6105399190611f73565b60015460408051638da5cb5b60e01b8152905133926001600160a01b031691638da5cb5b9160048083019260209291908290030181865afa158015610679573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061069d9190611f95565b6001600160a01b0316146106c657604051630543601560e11b81523360048201526024016105ef565b662386f26fc100008111156106f15760405163179c637760e11b8152600481018290526024016105ef565b606455565b600061070763ffffffff6000610908565b905090565b60015460408051638da5cb5b60e01b8152905133926001600160a01b031691638da5cb5b9160048083019260209291908290030181865afa158015610755573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107799190611f95565b6001600160a01b0316146107a257604051630543601560e11b81523360048201526024016105ef565b6001600160a01b0381166107e15760405163eac0d38960e01b81526020600482015260056024820152641d985d5b1d60da1b60448201526064016105ef565b6001805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b0392909216919091179055565b600061070763ffffffff6000611015565b60015460405163399dd46360e01b81523360048201526001600160a01b039091169063399dd46390602401602060405180830381865afa158015610869573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061088d9190611e36565b15156000036108b157604051633b9e98fd60e11b81523360048201526024016105ef565b61060282826115cc565b7f52c63247e1f47db19d5ce0460030c497f067ca4cebf71ba98eeadabe20bace048054606091600080516020612237833981519152916104a190611dfc565b60003361053381858561144c565b6000806109337f52c63247e1f47db19d5ce0460030c497f067ca4cebf71ba98eeadabe20bace025490565b905080156109725760006109478585611015565b9050816109566012600a611f4d565b6109609083611f5c565b61096a9190611f73565b92505061097e565b670de0b6b3a764000091505b5092915050565b60015460405163399dd46360e01b815233600482015260009182916001600160a01b039091169063399dd46390602401602060405180830381865afa1580156109d2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109f69190611e36565b1515600003610a1a57604051633b9e98fd60e11b81523360048201526024016105ef565b82356000610a27866116cc565b90506000610a36826001610908565b9050610a446012600a611f4d565b610a4e8285611f5c565b610a589190611f73565b9450610a6488846115cc565b610a6e88846116ea565b6001546001600160a01b03166337f3e0d2610a8887611fb2565b6040518263ffffffff1660e01b8152600401610aa691815260200190565b600060405180830381600087803b158015610ac057600080fd5b505af1158015610ad4573d6000803e3d6000fd5b505050506000610ae5836001610908565b90506000610b117f52c63247e1f47db19d5ce0460030c497f067ca4cebf71ba98eeadabe20bace025490565b1115610be457610b24620f424083611fce565b811080610b3c5750610b3982620f4240611fe1565b81115b15610b5a57604051634a03284360e01b815260040160405180910390fd5b670de0b6b3a764000086606454610b719190611f5c565b610b7b9190611f73565b600154604051630ce8fe6160e01b815260006004820152602481018390529196506001600160a01b031690630ce8fe619060440160006040518083038186803b158015610bc757600080fd5b505afa158015610bdb573d6000803e3d6000fd5b50505050610ca9565b600154604080516362af250760e11b815290516000926001600160a01b03169163c55e4a0e916004808301926101009291908290030181865afa158015610c2f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c539190612056565b60a001516040015190508015610c7f576040516348c9e5b760e11b8152600481018290526024016105ef565b81670de0b6b3a764000014610ca757604051633fda602b60e11b815260040160405180910390fd5b505b604080516001600160a01b038b168152602081018890529081018590527ff279e6a1f5e320cca91135676d9cb6e44ca8a08c0b88342bcdb1144f6511b5689060600160405180910390a150505050935093915050565b60015460405163399dd46360e01b81523360048201526001600160a01b039091169063399dd46390602401602060405180830381865afa158015610d47573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d6b9190611e36565b1515600003610d8f57604051633b9e98fd60e11b81523360048201526024016105ef565b80356000610d9c846116cc565b90506000610dab826001610908565b610db76012600a611f4d565b610dc19085611f5c565b610dcb9190611f73565b90508360200135811015610dff5760405163437879d160e01b815260048101829052602085013560248201526044016105ef565b610e098682611720565b600154604051631bf9f06960e11b8152600481018590526001600160a01b03909116906337f3e0d290602401600060405180830381600087803b158015610e4f57600080fd5b505af1158015610e63573d6000803e3d6000fd5b50505050612710610e927f52c63247e1f47db19d5ce0460030c497f067ca4cebf71ba98eeadabe20bace025490565b1015610ede577f52c63247e1f47db19d5ce0460030c497f067ca4cebf71ba98eeadabe20bace0254604051625a5b2760e31b8152600481019190915261271060248201526044016105ef565b600154604051630aa10e9f60e41b81526b706f696e74734d6f64756c6560a01b60048201526000916001600160a01b03169063aa10e9f090602401602060405180830381865afa158015610f36573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f5a9190611f95565b6040516301a57dfb60e51b81526001600160a01b03898116600483015287356024830152919250908216906334afbf6090604401600060405180830381600087803b158015610fa857600080fd5b505af1158015610fbc573d6000803e3d6000fd5b5050604080516001600160a01b038b168152602081018890529081018590527f90890809c654f11d6e72a28fa60149770a0d11ec6c92319d6ceb2bb0a4ea1a15925060600190505b60405180910390a150505050505050565b600154604051630aa10e9f60e41b81527f6c657665726167654d6f64756c65000000000000000000000000000000000000600482015260009182916001600160a01b039091169063aa10e9f090602401602060405180830381865afa158015611082573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110a69190611f95565b604051633f93cc9760e21b815263ffffffff8616600482015284151560248201526001600160a01b03919091169063fe4f325c90604401602060405180830381865afa1580156110fa573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061111e91906120ef565b9050600081600160009054906101000a90046001600160a01b03166001600160a01b03166385820ba36040518163ffffffff1660e01b8152600401602060405180830381865afa158015611176573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061119a91906120ef565b6111a49190612108565b905060008112156111b857600092506111bc565b8092505b505092915050565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00805468010000000000000000810460ff16159067ffffffffffffffff1660008115801561120f5750825b905060008267ffffffffffffffff16600114801561122c5750303b155b90508115801561123a575080155b156112585760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff19166001178555831561128c57845468ff00000000000000001916680100000000000000001785555b6112a56b737461626c654d6f64756c6560a01b88611756565b6112ed6040518060400160405280600a815260200169466c6174204d6f6e657960b01b815250604051806040016040528060048152602001631553925560e21b8152506117e7565b6112f686610630565b831561133d57845468ff000000000000000019168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d290602001611004565b50505050505050565b60006113546012600a611f4d565b61135c6106f6565b6113669084611f5c565b6113709190611f73565b9050670de0b6b3a7640000606454826113899190611f5c565b6113939190611f73565b6105399082611fce565b6113aa83838360016117f9565b505050565b6001600160a01b0383811660009081527f52c63247e1f47db19d5ce0460030c497f067ca4cebf71ba98eeadabe20bace0160209081526040808320938616835292905220546000198114611446578181101561143757604051637dc7a0d960e11b81526001600160a01b038416600482015260248101829052604481018390526064016105ef565b611446848484840360006117f9565b50505050565b6001600160a01b03831661147657604051634b637e8f60e11b8152600060048201526024016105ef565b6001600160a01b0382166114a05760405163ec442f0560e01b8152600060048201526024016105ef565b6113aa8383836118e1565b6001600160a01b038216600090815260008051602061223783398151915260209081526040808320546032909252909120546114e8908390611fe1565b111561155c5760405162461bcd60e51b815260206004820152603760248201527f45524332304c6f636b61626c655570677261646561626c653a206c6f636b656460448201527f20616d6f756e7420657863656564732062616c616e636500000000000000000060648201526084016105ef565b6001600160a01b03821660009081526032602052604081208054839290611584908490611fe1565b90915550506040518181526001600160a01b038316907f9f1ec8c880f76798e7b793325d625e9b60e4082a553c98f42b6cda368dd60008906020015b60405180910390a25050565b6001600160a01b0382166000908152603260205260409020548111156116645760405162461bcd60e51b815260206004820152604160248201527f45524332304c6f636b61626c655570677261646561626c653a2072657175657360448201527f74656420756e6c6f636b2065786365656473206c6f636b65642062616c616e636064820152606560f81b608482015260a4016105ef565b6001600160a01b0382166000908152603260205260408120805483929061168c908490611fce565b90915550506040518181526001600160a01b038316907f0f0bc5b519ddefdd8e5f9e6423433aa2b869738de2ae34d58ebc796fc749fa0d906020016115c0565b60006105396116e567ffffffffffffffff841642611fce565b6119ac565b6001600160a01b03821661171457604051634b637e8f60e11b8152600060048201526024016105ef565b610602826000836118e1565b6001600160a01b03821661174a5760405163ec442f0560e01b8152600060048201526024016105ef565b610602600083836118e1565b816117745760405163673791e360e11b815260040160405180910390fd5b6001600160a01b0381166117b35760405163eac0d38960e01b81526020600482015260056024820152641d985d5b1d60da1b60448201526064016105ef565b6000919091556001805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b03909216919091179055565b6117ef6119e1565b6106028282611a31565b6000805160206122378339815191526001600160a01b0385166118325760405163e602df0560e01b8152600060048201526024016105ef565b6001600160a01b03841661185c57604051634a1406b160e11b8152600060048201526024016105ef565b6001600160a01b038086166000908152600183016020908152604080832093881683529290522083905581156118da57836001600160a01b0316856001600160a01b03167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925856040516118d191815260200190565b60405180910390a35b5050505050565b6001600160a01b038316156119a1576001600160a01b03831660009081526032602090815260408083205460008051602061223783398151915290925290912054829161192d91611fce565b10156119a15760405162461bcd60e51b815260206004820152603760248201527f45524332304c6f636b61626c655570677261646561626c653a20696e7375666660448201527f696369656e7420756e6c6f636b65642062616c616e636500000000000000000060648201526084016105ef565b6113aa838383611a82565b600063ffffffff8211156119dd576040516306dfcc6560e41b815260206004820152602481018390526044016105ef565b5090565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a005468010000000000000000900460ff16611a2f57604051631afcd79f60e31b815260040160405180910390fd5b565b611a396119e1565b6000805160206122378339815191527f52c63247e1f47db19d5ce0460030c497f067ca4cebf71ba98eeadabe20bace03611a738482612176565b50600481016114468382612176565b6000805160206122378339815191526001600160a01b038416611abe5781816002016000828254611ab39190611fe1565b90915550611b309050565b6001600160a01b03841660009081526020829052604090205482811015611b115760405163391434e360e21b81526001600160a01b038616600482015260248101829052604481018490526064016105ef565b6001600160a01b03851660009081526020839052604090209083900390555b6001600160a01b038316611b4e576002810180548390039055611b6d565b6001600160a01b03831660009081526020829052604090208054830190555b826001600160a01b0316846001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef84604051611bb291815260200190565b60405180910390a350505050565b600060208083528351808285015260005b81811015611bed57858101830151858201604001528201611bd1565b506000604082860101526040601f19601f8301168501019250505092915050565b6001600160a01b0381168114611c2357600080fd5b50565b60008060408385031215611c3957600080fd5b8235611c4481611c0e565b946020939093013593505050565b600080600060608486031215611c6757600080fd5b8335611c7281611c0e565b92506020840135611c8281611c0e565b929592945050506040919091013590565b600060208284031215611ca557600080fd5b5035919050565b600060208284031215611cbe57600080fd5b8135611cc981611c0e565b9392505050565b8015158114611c2357600080fd5b60008060408385031215611cf157600080fd5b823563ffffffff81168114611d0557600080fd5b91506020830135611d1581611cd0565b809150509250929050565b67ffffffffffffffff81168114611c2357600080fd5b60008060008385036080811215611d4c57600080fd5b8435611d5781611c0e565b93506020850135611d6781611d20565b92506040603f1982011215611d7b57600080fd5b506040840190509250925092565b600080600083850360a0811215611d9f57600080fd5b8435611daa81611c0e565b93506020850135611dba81611d20565b92506060603f1982011215611d7b57600080fd5b60008060408385031215611de157600080fd5b8235611dec81611c0e565b91506020830135611d1581611c0e565b600181811c90821680611e1057607f821691505b602082108103611e3057634e487b7160e01b600052602260045260246000fd5b50919050565b600060208284031215611e4857600080fd5b8151611cc981611cd0565b634e487b7160e01b600052601160045260246000fd5b600181815b80851115611ea4578160001904821115611e8a57611e8a611e53565b80851615611e9757918102915b93841c9390800290611e6e565b509250929050565b600082611ebb57506001610539565b81611ec857506000610539565b8160018114611ede5760028114611ee857611f04565b6001915050610539565b60ff841115611ef957611ef9611e53565b50506001821b610539565b5060208310610133831016604e8410600b8410161715611f27575081810a610539565b611f318383611e69565b8060001904821115611f4557611f45611e53565b029392505050565b6000611cc960ff841683611eac565b808202811582820484141761053957610539611e53565b600082611f9057634e487b7160e01b600052601260045260246000fd5b500490565b600060208284031215611fa757600080fd5b8151611cc981611c0e565b6000600160ff1b8201611fc757611fc7611e53565b5060000390565b8181038181111561053957610539611e53565b8082018082111561053957610539611e53565b634e487b7160e01b600052604160045260246000fd5b60405160c0810167ffffffffffffffff8111828210171561202d5761202d611ff4565b60405290565b6040516060810167ffffffffffffffff8111828210171561202d5761202d611ff4565b600081830361010081121561206a57600080fd5b61207261200a565b835181526020840151602082015260408401516040820152606084015161209881611d20565b80606083015250608084015160808201526060609f19830112156120bb57600080fd5b6120c3612033565b60a085810151825260c0860151602083015260e090950151604082015293810193909352509092915050565b60006020828403121561210157600080fd5b5051919050565b818103600083128015838313168383128216171561097e5761097e611e53565b601f8211156113aa57600081815260208120601f850160051c8101602086101561214f5750805b601f850160051c820191505b8181101561216e5782815560010161215b565b505050505050565b815167ffffffffffffffff81111561219057612190611ff4565b6121a48161219e8454611dfc565b84612128565b602080601f8311600181146121d957600084156121c15750858301515b600019600386901b1c1916600185901b17855561216e565b600085815260208120601f198616915b82811015612208578886015182559484019460019091019084016121e9565b50858210156122265787850151600019600388901b60f8161c191681555b5050505050600190811b0190555056fe52c63247e1f47db19d5ce0460030c497f067ca4cebf71ba98eeadabe20bace00a26469706673582212208ded5b6597f9e839977406e1e3e77516d1b271e60f39e8c0c8cab4999f0f2b0164736f6c63430008140033
Deployed Bytecode
0x608060405234801561001057600080fd5b50600436106101b95760003560e01c80636cbf48a6116100f9578063c110587511610097578063cd6dc68711610071578063cd6dc687146103b9578063d24545a7146103cc578063dd62ed3e146103df578063fbfa77cf1461043757600080fd5b8063c11058751461036b578063c149e6ae14610393578063c45383e6146103a657600080fd5b8063929ec537116100d3578063929ec5371461031457806395d89b411461033d578063a9059cbb14610345578063ac75491b1461035857600080fd5b80636cbf48a6146102c357806370a08231146102cb5780637eee288d1461030157600080fd5b8063282d3fdf116101665780633d23b9ca116101405780633d23b9ca1461028c578063543a4fa31461029557806367f77e54146102a85780636817031b146102b057600080fd5b8063282d3fdf14610255578063313ce5671461026a578063367585501461027957600080fd5b80631d109d39116101975780631d109d391461023057806321b77d631461023957806323b872dd1461024257600080fd5b806306fdde03146101be578063095ea7b3146101dc57806318160ddd146101ff575b600080fd5b6101c6610462565b6040516101d39190611bc0565b60405180910390f35b6101ef6101ea366004611c26565b610525565b60405190151581526020016101d3565b7f52c63247e1f47db19d5ce0460030c497f067ca4cebf71ba98eeadabe20bace02545b6040519081526020016101d3565b61022260645481565b61022261271081565b6101ef610250366004611c52565b61053f565b610268610263366004611c26565b610563565b005b604051601281526020016101d3565b610222610287366004611c93565b610606565b61022260005481565b6102686102a3366004611c93565b610630565b6102226106f6565b6102686102be366004611cac565b61070c565b610222610810565b6102226102d9366004611cac565b6001600160a01b03166000908152600080516020612237833981519152602052604090205490565b61026861030f366004611c26565b610821565b610222610322366004611cac565b6001600160a01b031660009081526032602052604090205490565b6101c66108bb565b6101ef610353366004611c26565b6108fa565b610222610366366004611cde565b610908565b61037e610379366004611d36565b610985565b604080519283526020830191909152016101d3565b6102686103a1366004611d89565b610cff565b6102226103b4366004611cde565b611015565b6102686103c7366004611c26565b6111c4565b6102226103da366004611c93565b611346565b6102226103ed366004611dce565b6001600160a01b0391821660009081527f52c63247e1f47db19d5ce0460030c497f067ca4cebf71ba98eeadabe20bace016020908152604080832093909416825291909152205490565b60015461044a906001600160a01b031681565b6040516001600160a01b0390911681526020016101d3565b7f52c63247e1f47db19d5ce0460030c497f067ca4cebf71ba98eeadabe20bace038054606091600080516020612237833981519152916104a190611dfc565b80601f01602080910402602001604051908101604052809291908181526020018280546104cd90611dfc565b801561051a5780601f106104ef5761010080835404028352916020019161051a565b820191906000526020600020905b8154815290600101906020018083116104fd57829003601f168201915b505050505091505090565b60003361053381858561139d565b60019150505b92915050565b60003361054d8582856113af565b61055885858561144c565b506001949350505050565b60015460405163399dd46360e01b81523360048201526001600160a01b039091169063399dd46390602401602060405180830381865afa1580156105ab573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105cf9190611e36565b15156000036105f857604051633b9e98fd60e11b81523360048201526024015b60405180910390fd5b61060282826114ab565b5050565b60006106106106f6565b61061c6012600a611f4d565b6106269084611f5c565b6105399190611f73565b60015460408051638da5cb5b60e01b8152905133926001600160a01b031691638da5cb5b9160048083019260209291908290030181865afa158015610679573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061069d9190611f95565b6001600160a01b0316146106c657604051630543601560e11b81523360048201526024016105ef565b662386f26fc100008111156106f15760405163179c637760e11b8152600481018290526024016105ef565b606455565b600061070763ffffffff6000610908565b905090565b60015460408051638da5cb5b60e01b8152905133926001600160a01b031691638da5cb5b9160048083019260209291908290030181865afa158015610755573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107799190611f95565b6001600160a01b0316146107a257604051630543601560e11b81523360048201526024016105ef565b6001600160a01b0381166107e15760405163eac0d38960e01b81526020600482015260056024820152641d985d5b1d60da1b60448201526064016105ef565b6001805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b0392909216919091179055565b600061070763ffffffff6000611015565b60015460405163399dd46360e01b81523360048201526001600160a01b039091169063399dd46390602401602060405180830381865afa158015610869573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061088d9190611e36565b15156000036108b157604051633b9e98fd60e11b81523360048201526024016105ef565b61060282826115cc565b7f52c63247e1f47db19d5ce0460030c497f067ca4cebf71ba98eeadabe20bace048054606091600080516020612237833981519152916104a190611dfc565b60003361053381858561144c565b6000806109337f52c63247e1f47db19d5ce0460030c497f067ca4cebf71ba98eeadabe20bace025490565b905080156109725760006109478585611015565b9050816109566012600a611f4d565b6109609083611f5c565b61096a9190611f73565b92505061097e565b670de0b6b3a764000091505b5092915050565b60015460405163399dd46360e01b815233600482015260009182916001600160a01b039091169063399dd46390602401602060405180830381865afa1580156109d2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109f69190611e36565b1515600003610a1a57604051633b9e98fd60e11b81523360048201526024016105ef565b82356000610a27866116cc565b90506000610a36826001610908565b9050610a446012600a611f4d565b610a4e8285611f5c565b610a589190611f73565b9450610a6488846115cc565b610a6e88846116ea565b6001546001600160a01b03166337f3e0d2610a8887611fb2565b6040518263ffffffff1660e01b8152600401610aa691815260200190565b600060405180830381600087803b158015610ac057600080fd5b505af1158015610ad4573d6000803e3d6000fd5b505050506000610ae5836001610908565b90506000610b117f52c63247e1f47db19d5ce0460030c497f067ca4cebf71ba98eeadabe20bace025490565b1115610be457610b24620f424083611fce565b811080610b3c5750610b3982620f4240611fe1565b81115b15610b5a57604051634a03284360e01b815260040160405180910390fd5b670de0b6b3a764000086606454610b719190611f5c565b610b7b9190611f73565b600154604051630ce8fe6160e01b815260006004820152602481018390529196506001600160a01b031690630ce8fe619060440160006040518083038186803b158015610bc757600080fd5b505afa158015610bdb573d6000803e3d6000fd5b50505050610ca9565b600154604080516362af250760e11b815290516000926001600160a01b03169163c55e4a0e916004808301926101009291908290030181865afa158015610c2f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c539190612056565b60a001516040015190508015610c7f576040516348c9e5b760e11b8152600481018290526024016105ef565b81670de0b6b3a764000014610ca757604051633fda602b60e11b815260040160405180910390fd5b505b604080516001600160a01b038b168152602081018890529081018590527ff279e6a1f5e320cca91135676d9cb6e44ca8a08c0b88342bcdb1144f6511b5689060600160405180910390a150505050935093915050565b60015460405163399dd46360e01b81523360048201526001600160a01b039091169063399dd46390602401602060405180830381865afa158015610d47573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d6b9190611e36565b1515600003610d8f57604051633b9e98fd60e11b81523360048201526024016105ef565b80356000610d9c846116cc565b90506000610dab826001610908565b610db76012600a611f4d565b610dc19085611f5c565b610dcb9190611f73565b90508360200135811015610dff5760405163437879d160e01b815260048101829052602085013560248201526044016105ef565b610e098682611720565b600154604051631bf9f06960e11b8152600481018590526001600160a01b03909116906337f3e0d290602401600060405180830381600087803b158015610e4f57600080fd5b505af1158015610e63573d6000803e3d6000fd5b50505050612710610e927f52c63247e1f47db19d5ce0460030c497f067ca4cebf71ba98eeadabe20bace025490565b1015610ede577f52c63247e1f47db19d5ce0460030c497f067ca4cebf71ba98eeadabe20bace0254604051625a5b2760e31b8152600481019190915261271060248201526044016105ef565b600154604051630aa10e9f60e41b81526b706f696e74734d6f64756c6560a01b60048201526000916001600160a01b03169063aa10e9f090602401602060405180830381865afa158015610f36573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f5a9190611f95565b6040516301a57dfb60e51b81526001600160a01b03898116600483015287356024830152919250908216906334afbf6090604401600060405180830381600087803b158015610fa857600080fd5b505af1158015610fbc573d6000803e3d6000fd5b5050604080516001600160a01b038b168152602081018890529081018590527f90890809c654f11d6e72a28fa60149770a0d11ec6c92319d6ceb2bb0a4ea1a15925060600190505b60405180910390a150505050505050565b600154604051630aa10e9f60e41b81527f6c657665726167654d6f64756c65000000000000000000000000000000000000600482015260009182916001600160a01b039091169063aa10e9f090602401602060405180830381865afa158015611082573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110a69190611f95565b604051633f93cc9760e21b815263ffffffff8616600482015284151560248201526001600160a01b03919091169063fe4f325c90604401602060405180830381865afa1580156110fa573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061111e91906120ef565b9050600081600160009054906101000a90046001600160a01b03166001600160a01b03166385820ba36040518163ffffffff1660e01b8152600401602060405180830381865afa158015611176573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061119a91906120ef565b6111a49190612108565b905060008112156111b857600092506111bc565b8092505b505092915050565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00805468010000000000000000810460ff16159067ffffffffffffffff1660008115801561120f5750825b905060008267ffffffffffffffff16600114801561122c5750303b155b90508115801561123a575080155b156112585760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff19166001178555831561128c57845468ff00000000000000001916680100000000000000001785555b6112a56b737461626c654d6f64756c6560a01b88611756565b6112ed6040518060400160405280600a815260200169466c6174204d6f6e657960b01b815250604051806040016040528060048152602001631553925560e21b8152506117e7565b6112f686610630565b831561133d57845468ff000000000000000019168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d290602001611004565b50505050505050565b60006113546012600a611f4d565b61135c6106f6565b6113669084611f5c565b6113709190611f73565b9050670de0b6b3a7640000606454826113899190611f5c565b6113939190611f73565b6105399082611fce565b6113aa83838360016117f9565b505050565b6001600160a01b0383811660009081527f52c63247e1f47db19d5ce0460030c497f067ca4cebf71ba98eeadabe20bace0160209081526040808320938616835292905220546000198114611446578181101561143757604051637dc7a0d960e11b81526001600160a01b038416600482015260248101829052604481018390526064016105ef565b611446848484840360006117f9565b50505050565b6001600160a01b03831661147657604051634b637e8f60e11b8152600060048201526024016105ef565b6001600160a01b0382166114a05760405163ec442f0560e01b8152600060048201526024016105ef565b6113aa8383836118e1565b6001600160a01b038216600090815260008051602061223783398151915260209081526040808320546032909252909120546114e8908390611fe1565b111561155c5760405162461bcd60e51b815260206004820152603760248201527f45524332304c6f636b61626c655570677261646561626c653a206c6f636b656460448201527f20616d6f756e7420657863656564732062616c616e636500000000000000000060648201526084016105ef565b6001600160a01b03821660009081526032602052604081208054839290611584908490611fe1565b90915550506040518181526001600160a01b038316907f9f1ec8c880f76798e7b793325d625e9b60e4082a553c98f42b6cda368dd60008906020015b60405180910390a25050565b6001600160a01b0382166000908152603260205260409020548111156116645760405162461bcd60e51b815260206004820152604160248201527f45524332304c6f636b61626c655570677261646561626c653a2072657175657360448201527f74656420756e6c6f636b2065786365656473206c6f636b65642062616c616e636064820152606560f81b608482015260a4016105ef565b6001600160a01b0382166000908152603260205260408120805483929061168c908490611fce565b90915550506040518181526001600160a01b038316907f0f0bc5b519ddefdd8e5f9e6423433aa2b869738de2ae34d58ebc796fc749fa0d906020016115c0565b60006105396116e567ffffffffffffffff841642611fce565b6119ac565b6001600160a01b03821661171457604051634b637e8f60e11b8152600060048201526024016105ef565b610602826000836118e1565b6001600160a01b03821661174a5760405163ec442f0560e01b8152600060048201526024016105ef565b610602600083836118e1565b816117745760405163673791e360e11b815260040160405180910390fd5b6001600160a01b0381166117b35760405163eac0d38960e01b81526020600482015260056024820152641d985d5b1d60da1b60448201526064016105ef565b6000919091556001805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b03909216919091179055565b6117ef6119e1565b6106028282611a31565b6000805160206122378339815191526001600160a01b0385166118325760405163e602df0560e01b8152600060048201526024016105ef565b6001600160a01b03841661185c57604051634a1406b160e11b8152600060048201526024016105ef565b6001600160a01b038086166000908152600183016020908152604080832093881683529290522083905581156118da57836001600160a01b0316856001600160a01b03167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925856040516118d191815260200190565b60405180910390a35b5050505050565b6001600160a01b038316156119a1576001600160a01b03831660009081526032602090815260408083205460008051602061223783398151915290925290912054829161192d91611fce565b10156119a15760405162461bcd60e51b815260206004820152603760248201527f45524332304c6f636b61626c655570677261646561626c653a20696e7375666660448201527f696369656e7420756e6c6f636b65642062616c616e636500000000000000000060648201526084016105ef565b6113aa838383611a82565b600063ffffffff8211156119dd576040516306dfcc6560e41b815260206004820152602481018390526044016105ef565b5090565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a005468010000000000000000900460ff16611a2f57604051631afcd79f60e31b815260040160405180910390fd5b565b611a396119e1565b6000805160206122378339815191527f52c63247e1f47db19d5ce0460030c497f067ca4cebf71ba98eeadabe20bace03611a738482612176565b50600481016114468382612176565b6000805160206122378339815191526001600160a01b038416611abe5781816002016000828254611ab39190611fe1565b90915550611b309050565b6001600160a01b03841660009081526020829052604090205482811015611b115760405163391434e360e21b81526001600160a01b038616600482015260248101829052604481018490526064016105ef565b6001600160a01b03851660009081526020839052604090209083900390555b6001600160a01b038316611b4e576002810180548390039055611b6d565b6001600160a01b03831660009081526020829052604090208054830190555b826001600160a01b0316846001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef84604051611bb291815260200190565b60405180910390a350505050565b600060208083528351808285015260005b81811015611bed57858101830151858201604001528201611bd1565b506000604082860101526040601f19601f8301168501019250505092915050565b6001600160a01b0381168114611c2357600080fd5b50565b60008060408385031215611c3957600080fd5b8235611c4481611c0e565b946020939093013593505050565b600080600060608486031215611c6757600080fd5b8335611c7281611c0e565b92506020840135611c8281611c0e565b929592945050506040919091013590565b600060208284031215611ca557600080fd5b5035919050565b600060208284031215611cbe57600080fd5b8135611cc981611c0e565b9392505050565b8015158114611c2357600080fd5b60008060408385031215611cf157600080fd5b823563ffffffff81168114611d0557600080fd5b91506020830135611d1581611cd0565b809150509250929050565b67ffffffffffffffff81168114611c2357600080fd5b60008060008385036080811215611d4c57600080fd5b8435611d5781611c0e565b93506020850135611d6781611d20565b92506040603f1982011215611d7b57600080fd5b506040840190509250925092565b600080600083850360a0811215611d9f57600080fd5b8435611daa81611c0e565b93506020850135611dba81611d20565b92506060603f1982011215611d7b57600080fd5b60008060408385031215611de157600080fd5b8235611dec81611c0e565b91506020830135611d1581611c0e565b600181811c90821680611e1057607f821691505b602082108103611e3057634e487b7160e01b600052602260045260246000fd5b50919050565b600060208284031215611e4857600080fd5b8151611cc981611cd0565b634e487b7160e01b600052601160045260246000fd5b600181815b80851115611ea4578160001904821115611e8a57611e8a611e53565b80851615611e9757918102915b93841c9390800290611e6e565b509250929050565b600082611ebb57506001610539565b81611ec857506000610539565b8160018114611ede5760028114611ee857611f04565b6001915050610539565b60ff841115611ef957611ef9611e53565b50506001821b610539565b5060208310610133831016604e8410600b8410161715611f27575081810a610539565b611f318383611e69565b8060001904821115611f4557611f45611e53565b029392505050565b6000611cc960ff841683611eac565b808202811582820484141761053957610539611e53565b600082611f9057634e487b7160e01b600052601260045260246000fd5b500490565b600060208284031215611fa757600080fd5b8151611cc981611c0e565b6000600160ff1b8201611fc757611fc7611e53565b5060000390565b8181038181111561053957610539611e53565b8082018082111561053957610539611e53565b634e487b7160e01b600052604160045260246000fd5b60405160c0810167ffffffffffffffff8111828210171561202d5761202d611ff4565b60405290565b6040516060810167ffffffffffffffff8111828210171561202d5761202d611ff4565b600081830361010081121561206a57600080fd5b61207261200a565b835181526020840151602082015260408401516040820152606084015161209881611d20565b80606083015250608084015160808201526060609f19830112156120bb57600080fd5b6120c3612033565b60a085810151825260c0860151602083015260e090950151604082015293810193909352509092915050565b60006020828403121561210157600080fd5b5051919050565b818103600083128015838313168383128216171561097e5761097e611e53565b601f8211156113aa57600081815260208120601f850160051c8101602086101561214f5750805b601f850160051c820191505b8181101561216e5782815560010161215b565b505050505050565b815167ffffffffffffffff81111561219057612190611ff4565b6121a48161219e8454611dfc565b84612128565b602080601f8311600181146121d957600084156121c15750858301515b600019600386901b1c1916600185901b17855561216e565b600085815260208120601f198616915b82811015612208578886015182559484019460019091019084016121e9565b50858210156122265787850151600019600388901b60f8161c191681555b5050505050600190811b0190555056fe52c63247e1f47db19d5ce0460030c497f067ca4cebf71ba98eeadabe20bace00a26469706673582212208ded5b6597f9e839977406e1e3e77516d1b271e60f39e8c0c8cab4999f0f2b0164736f6c63430008140033
Loading...
Loading
Loading...
Loading
Loading...
Loading
Net Worth in USD
$0.00
Net Worth in ETH
0
Multichain Portfolio | 32 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
Loading...
Loading
Loading...
Loading
Loading...
Loading
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.