Source Code
Overview
ETH Balance
0 ETH
Eth Value
$0.00View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Loading...
Loading
Loading...
Loading
Cross-Chain Transactions
Loading...
Loading
This contract may be a proxy contract. Click on More Options and select Is this a proxy? to confirm and enable the "Read as Proxy" & "Write as Proxy" tabs.
Contract Name:
PositionManagerExtension
Compiler Version
v0.8.26+commit.8a97fa7a
Optimization Enabled:
Yes with 200 runs
Other Settings:
paris EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// (c) 2024 Primex.finance
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.26;
import {IERC165Upgradeable} from "@openzeppelin/contracts-upgradeable/utils/introspection/IERC165Upgradeable.sol";
import {WadRayMath} from "../libraries/utils/WadRayMath.sol";
import {PrimexPricingLibrary} from "../libraries/PrimexPricingLibrary.sol";
import {PositionLibrary} from "../libraries/PositionLibrary.sol";
import {LimitOrderLibrary} from "../libraries/LimitOrderLibrary.sol";
import "../libraries/Errors.sol";
import "../Constants.sol";
import {PositionManagerStorageV2} from "../PositionManager/PositionManagerStorage.sol";
import {IBucketV3} from "../Bucket/IBucket.sol";
import {IPositionManagerExtension} from "./IPositionManagerExtension.sol";
import {IConditionalClosingManager} from "../interfaces/IConditionalClosingManager.sol";
import {IKeeperRewardDistributorV3} from "../KeeperRewardDistributor/IKeeperRewardDistributor.sol";
import {IPrimexDNSStorageV3} from "../PrimexDNS/IPrimexDNSStorage.sol";
import {ISpotTradingRewardDistributorV2} from "../SpotTradingRewardDistributor/ISpotTradingRewardDistributor.sol";
contract PositionManagerExtension is IPositionManagerExtension, PositionManagerStorageV2 {
using WadRayMath for uint256;
using PositionLibrary for PositionLibrary.Position;
/**
* @inheritdoc IPositionManagerExtension
*/
function setMaxPositionSize(
address _token0,
address _token1,
uint256 _amountInToken0,
uint256 _amountInToken1
) external override {
_onlyRole(SMALL_TIMELOCK_ADMIN);
PositionLibrary.setMaxPositionSize(maxPositionSize, _token0, _token1, _amountInToken0, _amountInToken1);
emit SetMaxPositionSize(_token0, _token1, _amountInToken0, _amountInToken1);
}
/**
* @inheritdoc IPositionManagerExtension
*/
function setMaxPositionSizes(MaxPositionSizeParams[] calldata _params) external override {
_onlyRole(SMALL_TIMELOCK_ADMIN);
for (uint256 i; i < _params.length; i++) {
PositionLibrary.setMaxPositionSize(
maxPositionSize,
_params[i].token0,
_params[i].token1,
_params[i].amountInToken0,
_params[i].amountInToken1
);
emit SetMaxPositionSize(
_params[i].token0,
_params[i].token1,
_params[i].amountInToken0,
_params[i].amountInToken1
);
}
}
/**
* @inheritdoc IPositionManagerExtension
*/
function setDefaultOracleTolerableLimit(uint256 _percent) external override {
_onlyRole(MEDIUM_TIMELOCK_ADMIN);
_require(_percent <= WadRayMath.WAD, Errors.INVALID_PERCENT_NUMBER.selector);
defaultOracleTolerableLimit = _percent;
emit SetDefaultOracleTolerableLimit(_percent);
}
/**
* @inheritdoc IPositionManagerExtension
*/
function setSecurityBuffer(uint256 _newSecurityBuffer) external override {
_onlyRole(MEDIUM_TIMELOCK_ADMIN);
_require(_newSecurityBuffer < WadRayMath.WAD, Errors.INVALID_SECURITY_BUFFER.selector);
securityBuffer = _newSecurityBuffer;
emit SecurityBufferChanged(_newSecurityBuffer);
}
/**
* @inheritdoc IPositionManagerExtension
*/
function setMaintenanceBuffer(uint256 _newMaintenanceBuffer) external override {
_onlyRole(MEDIUM_TIMELOCK_ADMIN);
_require(
_newMaintenanceBuffer > 0 && _newMaintenanceBuffer < WadRayMath.WAD,
Errors.INVALID_MAINTENANCE_BUFFER.selector
);
maintenanceBuffer = _newMaintenanceBuffer;
emit MaintenanceBufferChanged(_newMaintenanceBuffer);
}
/**
* @inheritdoc IPositionManagerExtension
*/
function setOracleTolerableLimit(address _assetA, address _assetB, uint256 _percent) external override {
_onlyRole(SMALL_TIMELOCK_ADMIN);
PositionLibrary.setOracleTolerableLimit(oracleTolerableLimits, _assetA, _assetB, _percent);
emit SetOracleTolerableLimit(_assetA, _assetB, _percent);
}
/**
* @inheritdoc IPositionManagerExtension
*/
function setOracleTolerableLimits(OracleTolerableLimitsParams[] calldata _limitParams) external override {
_onlyRole(SMALL_TIMELOCK_ADMIN);
for (uint256 i; i < _limitParams.length; i++) {
PositionLibrary.setOracleTolerableLimit(
oracleTolerableLimits,
_limitParams[i].assetA,
_limitParams[i].assetB,
_limitParams[i].percent
);
emit SetOracleTolerableLimit(_limitParams[i].assetA, _limitParams[i].assetB, _limitParams[i].percent);
}
}
/**
* @inheritdoc IPositionManagerExtension
*/
function setOracleTolerableLimitMultiplier(uint256 newMultiplier) external override {
_onlyRole(MEDIUM_TIMELOCK_ADMIN);
_require(
newMultiplier >= WadRayMath.WAD && newMultiplier < 10 * WadRayMath.WAD,
Errors.WRONG_TRUSTED_MULTIPLIER.selector
);
oracleTolerableLimitMultiplier = newMultiplier;
emit OracleTolerableLimitMultiplierChanged(newMultiplier);
}
/**
* @inheritdoc IPositionManagerExtension
*/
function setKeeperRewardDistributor(IKeeperRewardDistributorV3 _keeperRewardDistributor) external override {
_onlyRole(BIG_TIMELOCK_ADMIN);
_require(
IERC165Upgradeable(address(_keeperRewardDistributor)).supportsInterface(
type(IKeeperRewardDistributorV3).interfaceId
),
Errors.ADDRESS_NOT_SUPPORTED.selector
);
keeperRewardDistributor = _keeperRewardDistributor;
emit KeeperRewardDistributorChanged(address(_keeperRewardDistributor));
}
/**
* @inheritdoc IPositionManagerExtension
*/
function setSpotTradingRewardDistributor(address _spotTradingRewardDistributor) external override {
_onlyRole(BIG_TIMELOCK_ADMIN);
spotTradingRewardDistributor = ISpotTradingRewardDistributorV2(_spotTradingRewardDistributor);
}
/**
* @dev Modifier that checks if the caller has a specific role.
* @param _role The role identifier to check.
*/
function _onlyRole(bytes32 _role) internal view {
_require(registry.hasRole(_role, msg.sender), Errors.FORBIDDEN.selector);
}
/**
* @inheritdoc IPositionManagerExtension
*/
function openPositionByOrder(
LimitOrderLibrary.OpenPositionByOrderParams calldata _params
) external override whenNotPaused returns (uint256, uint256, uint256, uint256, uint256) {
_onlyRole(LOM_ROLE);
uint256 initialGasleft = gasleft();
(PositionLibrary.Position memory newPosition, PositionLibrary.OpenPositionVars memory vars) = PositionLibrary
.createPositionByOrder(_params, priceOracle, primexDNS);
PositionLibrary.OpenPositionEventData memory posEventData = _openPosition(newPosition, vars, initialGasleft);
PositionLibrary.Position memory position = positions[positions.length - 1];
emit OpenPosition({
positionId: position.id,
trader: _params.order.trader,
openedBy: _params.sender,
position: position,
entryPrice: posEventData.entryPrice,
leverage: posEventData.leverage,
closeConditions: vars.closeConditions
});
emit PositionLibrary.PaidProtocolFee({
positionId: position.id,
trader: _params.order.trader,
paymentAsset: position.positionAsset,
feeRateType: posEventData.feeRateType,
feeInPaymentAsset: posEventData.feeInPositionAsset,
feeInPmx: posEventData.feeInPmx
});
return (
vars.borrowedAmount + position.depositAmountInSoldAsset,
position.positionAmount,
position.id,
posEventData.entryPrice,
posEventData.feeInPositionAsset
);
}
/**
* @inheritdoc IPositionManagerExtension
*/
function openPosition(
PositionLibrary.OpenPositionParams calldata _params
) external payable override nonReentrant whenNotPaused {
_notBlackListed();
priceOracle.updatePullOracle{value: msg.value}(_params.pullOracleData, _params.pullOracleTypes);
(PositionLibrary.Position memory newPosition, PositionLibrary.OpenPositionVars memory vars) = PositionLibrary
.createPosition(_params, primexDNS, priceOracle);
PositionLibrary.OpenPositionEventData memory posEventData = _openPosition(newPosition, vars, 0);
PositionLibrary.Position memory position = positions[positions.length - 1];
emit OpenPosition({
positionId: position.id,
trader: position.trader,
openedBy: position.trader,
position: position,
entryPrice: posEventData.entryPrice,
leverage: posEventData.leverage,
closeConditions: vars.closeConditions
});
}
/**
* @inheritdoc IPositionManagerExtension
*/
function partiallyClosePosition(
uint256 _positionId,
uint256 _amount,
address _depositReceiver,
PrimexPricingLibrary.MegaRoute[] calldata _megaRoutes,
uint256 _amountOutMin,
bytes calldata _positionSoldAssetOracleData,
bytes calldata _nativePositionAssetOracleData,
bytes calldata _nativeSoldAssetOracleData,
bytes calldata _pmxSoldAssetOracleData,
bytes[][] calldata _pullOracleData,
uint256[] calldata _pullOracleTypes
) external payable override nonReentrant {
_notBlackListed();
_onlyExist(_positionId);
PositionLibrary.Position memory position = positions[positionIndexes[_positionId]];
_require(msg.sender == position.trader, Errors.CALLER_IS_NOT_TRADER.selector);
_require(_amount < position.positionAmount, Errors.AMOUNT_IS_MORE_THAN_POSITION_AMOUNT.selector);
priceOracle.updatePullOracle{value: msg.value}(_pullOracleData, _pullOracleTypes);
PositionLibrary.ScaledParams memory scaledParams;
scaledParams.borrowedAmountIsNotZero = position.scaledDebtAmount != 0;
scaledParams.decreasePercent = _amount.wdiv(position.positionAmount);
scaledParams.scaledDebtAmount = scaledParams.borrowedAmountIsNotZero
? position.scaledDebtAmount.wmul(scaledParams.decreasePercent)
: 0;
scaledParams.depositDecrease = position.depositAmountInSoldAsset.wmul(scaledParams.decreasePercent);
LimitOrderLibrary.Condition memory condition;
PositionLibrary.ClosePositionEventData memory posEventData = position.closePosition(
PositionLibrary.ClosePositionParams({
closeAmount: _amount,
depositDecrease: scaledParams.depositDecrease,
scaledDebtAmount: scaledParams.scaledDebtAmount,
depositReceiver: _depositReceiver,
megaRoutes: _megaRoutes,
amountOutMin: _amountOutMin,
oracleTolerableLimit: scaledParams.borrowedAmountIsNotZero
? getOracleTolerableLimit(position.positionAsset, position.soldAsset)
: 0,
primexDNS: primexDNS,
priceOracle: priceOracle,
traderBalanceVault: traderBalanceVault,
closeCondition: condition,
ccmAdditionalParams: "",
borrowedAmountIsNotZero: scaledParams.borrowedAmountIsNotZero,
pairPriceDrop: priceOracle.getPairPriceDrop(position.positionAsset, position.soldAsset),
securityBuffer: securityBuffer,
needOracleTolerableLimitCheck: scaledParams.borrowedAmountIsNotZero,
initialGasLeft: 0,
keeperRewardDistributor: address(0),
positionSoldAssetOracleData: _positionSoldAssetOracleData,
pmxSoldAssetOracleData: _pmxSoldAssetOracleData,
nativeSoldAssetOracleData: _nativeSoldAssetOracleData
}),
PositionLibrary.CloseReason.CLOSE_BY_TRADER
);
position.positionAmount -= _amount;
position.scaledDebtAmount -= scaledParams.scaledDebtAmount;
position.depositAmountInSoldAsset -= scaledParams.depositDecrease;
// isSpot = address(position.bucket) == address(0)
IPrimexDNSStorageV3.TradingOrderType tradingOrderType = address(position.bucket) == address(0)
? IPrimexDNSStorageV3.TradingOrderType.SpotMarketOrder
: IPrimexDNSStorageV3.TradingOrderType.MarginMarketOrder;
PrimexPricingLibrary.validateMinPositionSize(
position.positionAmount,
position.positionAsset,
address(priceOracle),
keeperRewardDistributor,
primexDNS,
tradingOrderType,
_nativePositionAssetOracleData
);
positions[positionIndexes[_positionId]] = position;
emit PartialClosePosition({
positionId: _positionId,
trader: msg.sender,
bucketAddress: address(position.bucket),
soldAsset: position.soldAsset,
positionAsset: position.positionAsset,
decreasePositionAmount: _amount,
depositedAmount: position.depositAmountInSoldAsset,
scaledDebtAmount: position.scaledDebtAmount,
profit: posEventData.profit,
positionDebt: posEventData.debtAmount,
amountOut: posEventData.amountOutAfterFee
});
emit PositionLibrary.PaidProtocolFee({
positionId: _positionId,
trader: msg.sender,
paymentAsset: posEventData.paymentAsset,
feeRateType: posEventData.feeRateType,
feeInPaymentAsset: posEventData.feeInPaymentAsset,
feeInPmx: posEventData.feeInPmx
});
}
/**
* @inheritdoc IPositionManagerExtension
*/
function decreaseDeposit(
uint256 _positionId,
uint256 _amount,
bytes calldata _positionSoldAssetOracleData,
bytes calldata _nativeSoldAssetOracleData,
bytes[][] calldata _pullOracleData,
uint256[] calldata _pullOracleTypes
) external payable override nonReentrant whenNotPaused {
_notBlackListed();
PositionLibrary.Position storage position = positions[positionIndexes[_positionId]];
priceOracle.updatePullOracle{value: msg.value}(_pullOracleData, _pullOracleTypes);
position.decreaseDeposit(
PositionLibrary.DecreaseDepositParams({
amount: _amount,
primexDNS: primexDNS,
priceOracle: priceOracle,
traderBalanceVault: traderBalanceVault,
pairPriceDrop: priceOracle.getPairPriceDrop(position.positionAsset, position.soldAsset),
securityBuffer: securityBuffer,
oracleTolerableLimit: getOracleTolerableLimit(position.positionAsset, position.soldAsset),
maintenanceBuffer: maintenanceBuffer,
keeperRewardDistributor: address(keeperRewardDistributor),
positionSoldAssetOracleData: _positionSoldAssetOracleData,
nativeSoldAssetOracleData: _nativeSoldAssetOracleData
})
);
emit DecreaseDeposit({
positionId: position.id,
trader: position.trader,
depositDelta: _amount,
scaledDebtAmount: position.scaledDebtAmount
});
}
/**
* @inheritdoc IPositionManagerExtension
*/
function updatePositionConditions(
uint256 _positionId,
LimitOrderLibrary.Condition[] calldata _closeConditions
) external override nonReentrant {
_notBlackListed();
PositionLibrary.Position storage position = positions[positionIndexes[_positionId]];
_require(msg.sender == position.trader, Errors.CALLER_IS_NOT_TRADER.selector);
if (keccak256(abi.encode(_closeConditions)) != keccak256(abi.encode(closeConditions[_positionId]))) {
position.setCloseConditions(closeConditions, _closeConditions, primexDNS);
position.updatedConditionsAt = block.timestamp;
emit UpdatePositionConditions({
positionId: _positionId,
trader: position.trader,
closeConditions: _closeConditions
});
}
}
/**
* @notice Interface checker
* @param _interfaceId The interface id to check
*/
function supportsInterface(bytes4 _interfaceId) public view virtual override returns (bool) {
return _interfaceId == type(IPositionManagerExtension).interfaceId || super.supportsInterface(_interfaceId);
}
/**
* @inheritdoc IPositionManagerExtension
*/
function getOracleTolerableLimit(address assetA, address assetB) public view override returns (uint256) {
uint256 oracleTolerableLimit = oracleTolerableLimits[assetA][assetB];
return oracleTolerableLimit > 0 ? oracleTolerableLimit : defaultOracleTolerableLimit;
}
/**
* @notice Opens a new position.
* @param _position The position data.
* @param _vars The variables for opening the position.
* @return posEventData The event data for the opened position.
*/
function _openPosition(
PositionLibrary.Position memory _position,
PositionLibrary.OpenPositionVars memory _vars,
uint256 _initialGasLeft
) internal returns (PositionLibrary.OpenPositionEventData memory) {
(
PositionLibrary.Position memory position,
PositionLibrary.OpenPositionEventData memory posEventData
) = PositionLibrary.openPosition(
_position,
_vars,
PositionLibrary.PositionManagerParams({
primexDNS: primexDNS,
priceOracle: priceOracle,
traderBalanceVault: traderBalanceVault,
oracleTolerableLimitForThirdAsset: _vars.isThirdAsset
? getOracleTolerableLimit(_vars.depositData.depositAsset, _position.positionAsset)
: 0,
oracleTolerableLimit: _vars.needOracleTolerableLimitCheck
? getOracleTolerableLimit(_position.soldAsset, _position.positionAsset)
: 0,
maxPositionSize: maxPositionSize[_position.soldAsset][_position.positionAsset],
initialGasLeft: _initialGasLeft,
keeperRewardDistributor: address(keeperRewardDistributor)
})
);
// create position and update indexes (by trader, by bucket)
position.id = positionsId;
positionsId++;
positions.push(position);
positionIndexes[position.id] = positions.length - 1;
traderPositionIds[position.trader].push(position.id);
traderPositionIndexes[position.id] = traderPositionIds[position.trader].length - 1;
bucketPositionIds[address(position.bucket)].push(position.id);
bucketPositionIndexes[position.id] = bucketPositionIds[address(position.bucket)].length - 1;
position.setCloseConditions(closeConditions, _vars.closeConditions, primexDNS);
// tracks spot trading activity.
if (
position.bucket == IBucketV3(address(0)) &&
spotTradingRewardDistributor != ISpotTradingRewardDistributorV2(address(0))
) {
spotTradingRewardDistributor.updateTraderActivity(
_position.trader,
position.positionAsset,
position.positionAmount,
_vars.positionUsdOracleData
);
}
return posEventData;
}
/**
* @dev Modifier to check if the sender is not blacklisted.
*/
function _notBlackListed() internal view {
_require(!whiteBlackList.isBlackListed(msg.sender), Errors.SENDER_IS_BLACKLISTED.selector);
}
/**
* @dev Modifier to check if a position exists.
* @param _id The ID of the position to check.
*/
function _onlyExist(uint256 _id) internal view {
_require(
positions.length > 0 && _id == positions[positionIndexes[_id]].id,
Errors.POSITION_DOES_NOT_EXIST.selector
);
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface AggregatorV3Interface {
function decimals() external view returns (uint8);
function description() external view returns (string memory);
function version() external view returns (uint256);
function getRoundData(uint80 _roundId)
external
view
returns (
uint80 roundId,
int256 answer,
uint256 startedAt,
uint256 updatedAt,
uint80 answeredInRound
);
function latestRoundData()
external
view
returns (
uint80 roundId,
int256 answer,
uint256 startedAt,
uint256 updatedAt,
uint80 answeredInRound
);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (proxy/utils/Initializable.sol)
pragma solidity ^0.8.2;
import "../../utils/AddressUpgradeable.sol";
/**
* @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 Indicates that the contract has been initialized.
* @custom:oz-retyped-from bool
*/
uint8 private _initialized;
/**
* @dev Indicates that the contract is in the process of being initialized.
*/
bool private _initializing;
/**
* @dev Triggered when the contract has been initialized or reinitialized.
*/
event Initialized(uint8 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 functions marked with `initializer` can be nested in the context of a
* constructor.
*
* Emits an {Initialized} event.
*/
modifier initializer() {
bool isTopLevelCall = !_initializing;
require(
(isTopLevelCall && _initialized < 1) || (!AddressUpgradeable.isContract(address(this)) && _initialized == 1),
"Initializable: contract is already initialized"
);
_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 255 will prevent any future reinitialization.
*
* Emits an {Initialized} event.
*/
modifier reinitializer(uint8 version) {
require(!_initializing && _initialized < version, "Initializable: contract is already initialized");
_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() {
require(_initializing, "Initializable: contract is not initializing");
_;
}
/**
* @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 {
require(!_initializing, "Initializable: contract is initializing");
if (_initialized != type(uint8).max) {
_initialized = type(uint8).max;
emit Initialized(type(uint8).max);
}
}
/**
* @dev Returns the highest version that has been initialized. See {reinitializer}.
*/
function _getInitializedVersion() internal view returns (uint8) {
return _initialized;
}
/**
* @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.
*/
function _isInitializing() internal view returns (bool) {
return _initializing;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (security/Pausable.sol)
pragma solidity ^0.8.0;
import "../utils/ContextUpgradeable.sol";
import "../proxy/utils/Initializable.sol";
/**
* @dev Contract module which allows children to implement an emergency stop
* mechanism that can be triggered by an authorized account.
*
* This module is used through inheritance. It will make available the
* modifiers `whenNotPaused` and `whenPaused`, which can be applied to
* the functions of your contract. Note that they will not be pausable by
* simply including this module, only once the modifiers are put in place.
*/
abstract contract PausableUpgradeable is Initializable, ContextUpgradeable {
/**
* @dev Emitted when the pause is triggered by `account`.
*/
event Paused(address account);
/**
* @dev Emitted when the pause is lifted by `account`.
*/
event Unpaused(address account);
bool private _paused;
/**
* @dev Initializes the contract in unpaused state.
*/
function __Pausable_init() internal onlyInitializing {
__Pausable_init_unchained();
}
function __Pausable_init_unchained() internal onlyInitializing {
_paused = false;
}
/**
* @dev Modifier to make a function callable only when the contract is not paused.
*
* Requirements:
*
* - The contract must not be paused.
*/
modifier whenNotPaused() {
_requireNotPaused();
_;
}
/**
* @dev Modifier to make a function callable only when the contract is paused.
*
* Requirements:
*
* - The contract must be paused.
*/
modifier whenPaused() {
_requirePaused();
_;
}
/**
* @dev Returns true if the contract is paused, and false otherwise.
*/
function paused() public view virtual returns (bool) {
return _paused;
}
/**
* @dev Throws if the contract is paused.
*/
function _requireNotPaused() internal view virtual {
require(!paused(), "Pausable: paused");
}
/**
* @dev Throws if the contract is not paused.
*/
function _requirePaused() internal view virtual {
require(paused(), "Pausable: not paused");
}
/**
* @dev Triggers stopped state.
*
* Requirements:
*
* - The contract must not be paused.
*/
function _pause() internal virtual whenNotPaused {
_paused = true;
emit Paused(_msgSender());
}
/**
* @dev Returns to normal state.
*
* Requirements:
*
* - The contract must be paused.
*/
function _unpause() internal virtual whenPaused {
_paused = false;
emit Unpaused(_msgSender());
}
/**
* @dev This empty reserved space is put in place to allow future versions to add new
* variables without shifting down storage in the inheritance chain.
* See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
*/
uint256[49] private __gap;
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (security/ReentrancyGuard.sol)
pragma solidity ^0.8.0;
import "../proxy/utils/Initializable.sol";
/**
* @dev Contract module that helps prevent reentrant calls to a function.
*
* Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
* available, which can be applied to functions to make sure there are no nested
* (reentrant) calls to them.
*
* Note that because there is a single `nonReentrant` guard, functions marked as
* `nonReentrant` may not call one another. This can be worked around by making
* those functions `private`, and then adding `external` `nonReentrant` entry
* points to them.
*
* TIP: If you would like to learn more about reentrancy and alternative ways
* to protect against it, check out our blog post
* https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
*/
abstract contract ReentrancyGuardUpgradeable is Initializable {
// Booleans are more expensive than uint256 or any type that takes up a full
// word because each write operation emits an extra SLOAD to first read the
// slot's contents, replace the bits taken up by the boolean, and then write
// back. This is the compiler's defense against contract upgrades and
// pointer aliasing, and it cannot be disabled.
// The values being non-zero value makes deployment a bit more expensive,
// but in exchange the refund on every call to nonReentrant will be lower in
// amount. Since refunds are capped to a percentage of the total
// transaction's gas, it is best to keep them low in cases like this one, to
// increase the likelihood of the full refund coming into effect.
uint256 private constant _NOT_ENTERED = 1;
uint256 private constant _ENTERED = 2;
uint256 private _status;
function __ReentrancyGuard_init() internal onlyInitializing {
__ReentrancyGuard_init_unchained();
}
function __ReentrancyGuard_init_unchained() internal onlyInitializing {
_status = _NOT_ENTERED;
}
/**
* @dev Prevents a contract from calling itself, directly or indirectly.
* Calling a `nonReentrant` function from another `nonReentrant`
* function is not supported. It is possible to prevent this from happening
* by making the `nonReentrant` function external, and making it call a
* `private` function that does the actual work.
*/
modifier nonReentrant() {
_nonReentrantBefore();
_;
_nonReentrantAfter();
}
function _nonReentrantBefore() private {
// On the first call to nonReentrant, _status will be _NOT_ENTERED
require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
// Any calls to nonReentrant after this point will fail
_status = _ENTERED;
}
function _nonReentrantAfter() private {
// By storing the original value once again, a refund is triggered (see
// https://eips.ethereum.org/EIPS/eip-2200)
_status = _NOT_ENTERED;
}
/**
* @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
* `nonReentrant` function in the call stack.
*/
function _reentrancyGuardEntered() internal view returns (bool) {
return _status == _ENTERED;
}
/**
* @dev This empty reserved space is put in place to allow future versions to add new
* variables without shifting down storage in the inheritance chain.
* See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
*/
uint256[49] private __gap;
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol)
pragma solidity ^0.8.0;
import "../IERC20Upgradeable.sol";
/**
* @dev Interface for the optional metadata functions from the ERC20 standard.
*
* _Available since v4.1._
*/
interface IERC20MetadataUpgradeable is IERC20Upgradeable {
/**
* @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 v4.9.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20Upgradeable {
/**
* @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 amount of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the amount of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves `amount` 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 amount) 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 `amount` 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 amount) external returns (bool);
/**
* @dev Moves `amount` tokens from `from` to `to` using the
* allowance mechanism. `amount` 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 amount) external returns (bool);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol)
pragma solidity ^0.8.1;
/**
* @dev Collection of functions related to the address type
*/
library AddressUpgradeable {
/**
* @dev Returns true if `account` is a contract.
*
* [IMPORTANT]
* ====
* It is unsafe to assume that an address for which this function returns
* false is an externally-owned account (EOA) and not a contract.
*
* Among others, `isContract` will return false for the following
* types of addresses:
*
* - an externally-owned account
* - a contract in construction
* - an address where a contract will be created
* - an address where a contract lived, but was destroyed
*
* Furthermore, `isContract` will also return true if the target contract within
* the same transaction is already scheduled for destruction by `SELFDESTRUCT`,
* which only has an effect at the end of a transaction.
* ====
*
* [IMPORTANT]
* ====
* You shouldn't rely on `isContract` to protect against flash loan attacks!
*
* Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
* like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
* constructor.
* ====
*/
function isContract(address account) internal view returns (bool) {
// This method relies on extcodesize/address.code.length, which returns 0
// for contracts in construction, since the code is only stored at the end
// of the constructor execution.
return account.code.length > 0;
}
/**
* @dev Replacement for Solidity's `transfer`: sends `amount` wei to
* `recipient`, forwarding all available gas and reverting on errors.
*
* https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
* of certain opcodes, possibly making contracts go over the 2300 gas limit
* imposed by `transfer`, making them unable to receive funds via
* `transfer`. {sendValue} removes this limitation.
*
* https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
*
* IMPORTANT: because control is transferred to `recipient`, care must be
* taken to not create reentrancy vulnerabilities. Consider using
* {ReentrancyGuard} or the
* https://solidity.readthedocs.io/en/v0.8.0/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/
function sendValue(address payable recipient, uint256 amount) internal {
require(address(this).balance >= amount, "Address: insufficient balance");
(bool success, ) = recipient.call{value: amount}("");
require(success, "Address: unable to send value, recipient may have reverted");
}
/**
* @dev Performs a Solidity function call using a low level `call`. A
* plain `call` is an unsafe replacement for a function call: use this
* function instead.
*
* If `target` reverts with a revert reason, it is bubbled up by this
* function (like regular Solidity function calls).
*
* Returns the raw returned data. To convert to the expected return value,
* use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
*
* Requirements:
*
* - `target` must be a contract.
* - calling `target` with `data` must not revert.
*
* _Available since v3.1._
*/
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, "Address: low-level call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
* `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but also transferring `value` wei to `target`.
*
* Requirements:
*
* - the calling contract must have an ETH balance of at least `value`.
* - the called Solidity function must be `payable`.
*
* _Available since v3.1._
*/
function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
}
/**
* @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
* with `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCallWithValue(
address target,
bytes memory data,
uint256 value,
string memory errorMessage
) internal returns (bytes memory) {
require(address(this).balance >= value, "Address: insufficient balance for call");
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
return functionStaticCall(target, data, "Address: low-level static call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(
address target,
bytes memory data,
string memory errorMessage
) internal view returns (bytes memory) {
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
return functionDelegateCall(target, data, "Address: low-level delegate call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
* the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
*
* _Available since v4.8._
*/
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata,
string memory errorMessage
) internal view returns (bytes memory) {
if (success) {
if (returndata.length == 0) {
// only check isContract if the call was successful and the return data is empty
// otherwise we already know that it was a contract
require(isContract(target), "Address: call to non-contract");
}
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
/**
* @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
* revert reason or using the provided one.
*
* _Available since v4.3._
*/
function verifyCallResult(
bool success,
bytes memory returndata,
string memory errorMessage
) internal pure returns (bytes memory) {
if (success) {
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
function _revert(bytes memory returndata, string memory errorMessage) private pure {
// Look for revert reason and bubble it up if present
if (returndata.length > 0) {
// The easiest way to bubble the revert reason is using memory via assembly
/// @solidity memory-safe-assembly
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)
pragma solidity ^0.8.0;
import "../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;
}
/**
* @dev This empty reserved space is put in place to allow future versions to add new
* variables without shifting down storage in the inheritance chain.
* See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
*/
uint256[50] private __gap;
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)
pragma solidity ^0.8.0;
import "./IERC165Upgradeable.sol";
import "../../proxy/utils/Initializable.sol";
/**
* @dev Implementation of the {IERC165} interface.
*
* Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
* for the additional interface id that will be supported. For example:
*
* ```solidity
* function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
* return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
* }
* ```
*
* Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
*/
abstract contract ERC165Upgradeable is Initializable, IERC165Upgradeable {
function __ERC165_init() internal onlyInitializing {
}
function __ERC165_init_unchained() internal onlyInitializing {
}
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
return interfaceId == type(IERC165Upgradeable).interfaceId;
}
/**
* @dev This empty reserved space is put in place to allow future versions to add new
* variables without shifting down storage in the inheritance chain.
* See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
*/
uint256[50] private __gap;
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)
pragma solidity ^0.8.0;
/**
* @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 IERC165Upgradeable {
/**
* @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);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (access/IAccessControl.sol)
pragma solidity ^0.8.0;
/**
* @dev External interface of AccessControl declared to support ERC165 detection.
*/
interface IAccessControl {
/**
* @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`
*
* `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite
* {RoleAdminChanged} not being emitted signaling this.
*
* _Available since v3.1._
*/
event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);
/**
* @dev Emitted when `account` is granted `role`.
*
* `sender` is the account that originated the contract call, an admin role
* bearer except when using {AccessControl-_setupRole}.
*/
event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);
/**
* @dev Emitted when `account` is revoked `role`.
*
* `sender` is the account that originated the contract call:
* - if using `revokeRole`, it is the admin role bearer
* - if using `renounceRole`, it is the role bearer (i.e. `account`)
*/
event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);
/**
* @dev Returns `true` if `account` has been granted `role`.
*/
function hasRole(bytes32 role, address account) external view returns (bool);
/**
* @dev Returns the admin role that controls `role`. See {grantRole} and
* {revokeRole}.
*
* To change a role's admin, use {AccessControl-_setRoleAdmin}.
*/
function getRoleAdmin(bytes32 role) external view returns (bytes32);
/**
* @dev Grants `role` to `account`.
*
* If `account` had not been already granted `role`, emits a {RoleGranted}
* event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*/
function grantRole(bytes32 role, address account) external;
/**
* @dev Revokes `role` from `account`.
*
* If `account` had been granted `role`, emits a {RoleRevoked} event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*/
function revokeRole(bytes32 role, address account) external;
/**
* @dev Revokes `role` from the calling account.
*
* Roles are often managed via {grantRole} and {revokeRole}: this function's
* purpose is to provide a mechanism for accounts to lose their privileges
* if they are compromised (such as when a trusted device is misplaced).
*
* If the calling account had been granted `role`, emits a {RoleRevoked}
* event.
*
* Requirements:
*
* - the caller must be `account`.
*/
function renounceRole(bytes32 role, address account) external;
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol)
pragma solidity ^0.8.0;
import "../IERC20.sol";
/**
* @dev Interface for the optional metadata functions from the ERC20 standard.
*
* _Available since v4.1._
*/
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 v4.9.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.0;
/**
* @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 amount of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the amount of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves `amount` 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 amount) 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 `amount` 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 amount) external returns (bool);
/**
* @dev Moves `amount` tokens from `from` to `to` using the
* allowance mechanism. `amount` 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 amount) external returns (bool);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)
pragma solidity ^0.8.0;
/**
* @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);
}// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.0;
import "./PythStructs.sol";
import "./IPythEvents.sol";
/// @title Consume prices from the Pyth Network (https://pyth.network/).
/// @dev Please refer to the guidance at https://docs.pyth.network/documentation/pythnet-price-feeds/best-practices for how to consume prices safely.
/// @author Pyth Data Association
interface IPyth is IPythEvents {
/// @notice Returns the period (in seconds) that a price feed is considered valid since its publish time
function getValidTimePeriod() external view returns (uint validTimePeriod);
/// @notice Returns the price and confidence interval.
/// @dev Reverts if the price has not been updated within the last `getValidTimePeriod()` seconds.
/// @param id The Pyth Price Feed ID of which to fetch the price and confidence interval.
/// @return price - please read the documentation of PythStructs.Price to understand how to use this safely.
function getPrice(
bytes32 id
) external view returns (PythStructs.Price memory price);
/// @notice Returns the exponentially-weighted moving average price and confidence interval.
/// @dev Reverts if the EMA price is not available.
/// @param id The Pyth Price Feed ID of which to fetch the EMA price and confidence interval.
/// @return price - please read the documentation of PythStructs.Price to understand how to use this safely.
function getEmaPrice(
bytes32 id
) external view returns (PythStructs.Price memory price);
/// @notice Returns the price of a price feed without any sanity checks.
/// @dev This function returns the most recent price update in this contract without any recency checks.
/// This function is unsafe as the returned price update may be arbitrarily far in the past.
///
/// Users of this function should check the `publishTime` in the price to ensure that the returned price is
/// sufficiently recent for their application. If you are considering using this function, it may be
/// safer / easier to use either `getPrice` or `getPriceNoOlderThan`.
/// @return price - please read the documentation of PythStructs.Price to understand how to use this safely.
function getPriceUnsafe(
bytes32 id
) external view returns (PythStructs.Price memory price);
/// @notice Returns the price that is no older than `age` seconds of the current time.
/// @dev This function is a sanity-checked version of `getPriceUnsafe` which is useful in
/// applications that require a sufficiently-recent price. Reverts if the price wasn't updated sufficiently
/// recently.
/// @return price - please read the documentation of PythStructs.Price to understand how to use this safely.
function getPriceNoOlderThan(
bytes32 id,
uint age
) external view returns (PythStructs.Price memory price);
/// @notice Returns the exponentially-weighted moving average price of a price feed without any sanity checks.
/// @dev This function returns the same price as `getEmaPrice` in the case where the price is available.
/// However, if the price is not recent this function returns the latest available price.
///
/// The returned price can be from arbitrarily far in the past; this function makes no guarantees that
/// the returned price is recent or useful for any particular application.
///
/// Users of this function should check the `publishTime` in the price to ensure that the returned price is
/// sufficiently recent for their application. If you are considering using this function, it may be
/// safer / easier to use either `getEmaPrice` or `getEmaPriceNoOlderThan`.
/// @return price - please read the documentation of PythStructs.Price to understand how to use this safely.
function getEmaPriceUnsafe(
bytes32 id
) external view returns (PythStructs.Price memory price);
/// @notice Returns the exponentially-weighted moving average price that is no older than `age` seconds
/// of the current time.
/// @dev This function is a sanity-checked version of `getEmaPriceUnsafe` which is useful in
/// applications that require a sufficiently-recent price. Reverts if the price wasn't updated sufficiently
/// recently.
/// @return price - please read the documentation of PythStructs.Price to understand how to use this safely.
function getEmaPriceNoOlderThan(
bytes32 id,
uint age
) external view returns (PythStructs.Price memory price);
/// @notice Update price feeds with given update messages.
/// This method requires the caller to pay a fee in wei; the required fee can be computed by calling
/// `getUpdateFee` with the length of the `updateData` array.
/// Prices will be updated if they are more recent than the current stored prices.
/// The call will succeed even if the update is not the most recent.
/// @dev Reverts if the transferred fee is not sufficient or the updateData is invalid.
/// @param updateData Array of price update data.
function updatePriceFeeds(bytes[] calldata updateData) external payable;
/// @notice Wrapper around updatePriceFeeds that rejects fast if a price update is not necessary. A price update is
/// necessary if the current on-chain publishTime is older than the given publishTime. It relies solely on the
/// given `publishTimes` for the price feeds and does not read the actual price update publish time within `updateData`.
///
/// This method requires the caller to pay a fee in wei; the required fee can be computed by calling
/// `getUpdateFee` with the length of the `updateData` array.
///
/// `priceIds` and `publishTimes` are two arrays with the same size that correspond to senders known publishTime
/// of each priceId when calling this method. If all of price feeds within `priceIds` have updated and have
/// a newer or equal publish time than the given publish time, it will reject the transaction to save gas.
/// Otherwise, it calls updatePriceFeeds method to update the prices.
///
/// @dev Reverts if update is not needed or the transferred fee is not sufficient or the updateData is invalid.
/// @param updateData Array of price update data.
/// @param priceIds Array of price ids.
/// @param publishTimes Array of publishTimes. `publishTimes[i]` corresponds to known `publishTime` of `priceIds[i]`
function updatePriceFeedsIfNecessary(
bytes[] calldata updateData,
bytes32[] calldata priceIds,
uint64[] calldata publishTimes
) external payable;
/// @notice Returns the required fee to update an array of price updates.
/// @param updateData Array of price update data.
/// @return feeAmount The required fee in Wei.
function getUpdateFee(
bytes[] calldata updateData
) external view returns (uint feeAmount);
/// @notice Parse `updateData` and return price feeds of the given `priceIds` if they are all published
/// within `minPublishTime` and `maxPublishTime`.
///
/// You can use this method if you want to use a Pyth price at a fixed time and not the most recent price;
/// otherwise, please consider using `updatePriceFeeds`. This method may store the price updates on-chain, if they
/// are more recent than the current stored prices.
///
/// This method requires the caller to pay a fee in wei; the required fee can be computed by calling
/// `getUpdateFee` with the length of the `updateData` array.
///
///
/// @dev Reverts if the transferred fee is not sufficient or the updateData is invalid or there is
/// no update for any of the given `priceIds` within the given time range.
/// @param updateData Array of price update data.
/// @param priceIds Array of price ids.
/// @param minPublishTime minimum acceptable publishTime for the given `priceIds`.
/// @param maxPublishTime maximum acceptable publishTime for the given `priceIds`.
/// @return priceFeeds Array of the price feeds corresponding to the given `priceIds` (with the same order).
function parsePriceFeedUpdates(
bytes[] calldata updateData,
bytes32[] calldata priceIds,
uint64 minPublishTime,
uint64 maxPublishTime
) external payable returns (PythStructs.PriceFeed[] memory priceFeeds);
/// @notice Similar to `parsePriceFeedUpdates` but ensures the updates returned are
/// the first updates published in minPublishTime. That is, if there are multiple updates for a given timestamp,
/// this method will return the first update. This method may store the price updates on-chain, if they
/// are more recent than the current stored prices.
///
///
/// @dev Reverts if the transferred fee is not sufficient or the updateData is invalid or there is
/// no update for any of the given `priceIds` within the given time range and uniqueness condition.
/// @param updateData Array of price update data.
/// @param priceIds Array of price ids.
/// @param minPublishTime minimum acceptable publishTime for the given `priceIds`.
/// @param maxPublishTime maximum acceptable publishTime for the given `priceIds`.
/// @return priceFeeds Array of the price feeds corresponding to the given `priceIds` (with the same order).
function parsePriceFeedUpdatesUnique(
bytes[] calldata updateData,
bytes32[] calldata priceIds,
uint64 minPublishTime,
uint64 maxPublishTime
) external payable returns (PythStructs.PriceFeed[] memory priceFeeds);
}// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.0;
/// @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: Apache-2.0
pragma solidity ^0.8.0;
contract PythStructs {
// A price with a degree of uncertainty, represented as a price +- a confidence interval.
//
// The confidence interval roughly corresponds to the standard error of a normal distribution.
// Both the price and confidence are stored in a fixed-point numeric representation,
// `x * (10^expo)`, where `expo` is the exponent.
//
// Please refer to the documentation at https://docs.pyth.network/documentation/pythnet-price-feeds/best-practices for how
// to how this price safely.
struct Price {
// Price
int64 price;
// Confidence interval around the price
uint64 conf;
// Price exponent
int32 expo;
// Unix timestamp describing when the price was published
uint publishTime;
}
// PriceFeed represents a current aggregate price from pyth publisher feeds.
struct PriceFeed {
// The price ID.
bytes32 id;
// Latest available price
Price price;
// Latest available exponentially-weighted moving average price
Price emaPrice;
}
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.7.5;
pragma abicoder v2;
/// @title Quoter Interface
/// @notice Supports quoting the calculated amounts from exact input or exact output swaps
/// @dev These functions are not marked view because they rely on calling non-view functions and reverting
/// to compute the result. They are also not gas efficient and should not be called on-chain.
interface IQuoter {
/// @notice Returns the amount out received for a given exact input swap without executing the swap
/// @param path The path of the swap, i.e. each token pair and the pool fee
/// @param amountIn The amount of the first token to swap
/// @return amountOut The amount of the last token that would be received
function quoteExactInput(bytes memory path, uint256 amountIn) external returns (uint256 amountOut);
/// @notice Returns the amount out received for a given exact input but for a swap of a single pool
/// @param tokenIn The token being swapped in
/// @param tokenOut The token being swapped out
/// @param fee The fee of the token pool to consider for the pair
/// @param amountIn The desired input amount
/// @param sqrtPriceLimitX96 The price limit of the pool that cannot be exceeded by the swap
/// @return amountOut The amount of `tokenOut` that would be received
function quoteExactInputSingle(
address tokenIn,
address tokenOut,
uint24 fee,
uint256 amountIn,
uint160 sqrtPriceLimitX96
) external returns (uint256 amountOut);
/// @notice Returns the amount in required for a given exact output swap without executing the swap
/// @param path The path of the swap, i.e. each token pair and the pool fee. Path must be provided in reverse order
/// @param amountOut The amount of the last token to receive
/// @return amountIn The amount of first token required to be paid
function quoteExactOutput(bytes memory path, uint256 amountOut) external returns (uint256 amountIn);
/// @notice Returns the amount in required to receive the given exact output amount but for a swap of a single pool
/// @param tokenIn The token being swapped in
/// @param tokenOut The token being swapped out
/// @param fee The fee of the token pool to consider for the pair
/// @param amountOut The desired output amount
/// @param sqrtPriceLimitX96 The price limit of the pool that cannot be exceeded by the swap
/// @return amountIn The amount required as the input for the swap in order to receive `amountOut`
function quoteExactOutputSingle(
address tokenIn,
address tokenOut,
uint24 fee,
uint256 amountOut,
uint160 sqrtPriceLimitX96
) external returns (uint256 amountIn);
}// (c) 2024 Primex.finance
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.18;
import {IActivityRewardDistributorStorage, IERC20, IPrimexDNSV3, ITraderBalanceVault} from "./IActivityRewardDistributorStorage.sol";
import {IWhiteBlackList} from "../WhiteBlackList/WhiteBlackList/IWhiteBlackList.sol";
import {IBucketV3} from "../Bucket/IBucket.sol";
import {IPausable} from "../interfaces/IPausable.sol";
interface IActivityRewardDistributor is IActivityRewardDistributorStorage, IPausable {
enum Role {
LENDER,
TRADER
}
struct BucketWithRole {
address bucketAddress;
Role role;
}
/**
* @notice Emitted on claimReward()
* @param user The address of the user who claimed reward
* @param bucket The address of the bucket this reward is related to
* @param role User role - TRADER or LENDER
* @param amount Claimed amount
*/
event ClaimReward(address indexed user, address indexed bucket, Role indexed role, uint256 amount);
/**
* @notice Initializes the ActivityRewardDistributor contract.
* @dev This function should only be called once during the initial setup of the contract.
* @param _pmx The address of the PMXToken contract.
* @param _dns The address of the PrimexDNS contract.
* @param _registry The address of the PrimexRegistry contract.
* @param _treasury The address of the treasury where fees will be collected.
* @param _traderBalanceVault The address of the TraderBalanceVault contract.
* @param _whiteBlackList The address of the WhiteBlackList contract.
*/
function initialize(
IERC20 _pmx,
IPrimexDNSV3 _dns,
address _registry,
address _treasury,
ITraderBalanceVault _traderBalanceVault,
IWhiteBlackList _whiteBlackList
) external;
/**
* @notice Saves user activity in the protocol for reward calculation
* @param bucket The address of the bucket
* @param user User address
* @param newBalance User balance after action
* @param role User role - TRADER or LENDER
*/
function updateUserActivity(IBucketV3 bucket, address user, uint256 newBalance, Role role) external;
/**
* @notice Saves activity of multiple users in the protocol for reward calculation
* @param bucket The address of the bucket
* @param users Array of user addresses
* @param newBalances Array of users balances after action
* @param length The length of the users and oldBalances arrays
* @param role User role - TRADER or LENDER
*/
function updateUsersActivities(
IBucketV3 bucket,
address[] calldata users,
uint256[] calldata newBalances,
uint256 length,
Role role
) external;
/**
* @notice Allows the caller to claim their accumulated reward from the specified buckets.
* @param bucketsArray The array of BucketWithRole objects containing the buckets from which to claim the rewards.
*/
function claimReward(BucketWithRole[] calldata bucketsArray) external;
/**
* @notice Sets up activity rewards distribution in bucket with the specified role and reward parameters.
* @dev Only callable by the MEDIUM_TIMELOCK_ADMIN role.
* @param bucket The address of the bucket to set up.
* @param role The role associated with the bucket.
* @param increaseAmount The amount by which to increase the total reward for the bucket (in PMX).
* Adds specified amount to totalReward of the bucket. Initial value of totalReward is 0.
* @param rewardPerDay The reward amount per day for the bucket.
*/
function setupBucket(address bucket, Role role, uint256 increaseAmount, uint256 rewardPerDay) external;
/**
* @notice Allows the caller to withdraw PMX tokens from a specific bucket.
* @dev Only callable by the BIG_TIMELOCK_ADMIN role.
* @param bucket The address of the bucket from which to withdraw PMX tokens.
* @param role The role associated with the bucket.
* @param amount The amount of PMX tokens to withdraw.
*/
function withdrawPmx(address bucket, Role role, uint256 amount) external;
/**
* @notice Decreases the reward per day for a bucket and role.
* @dev Only callable by the EMERGENCY_ADMIN role.
* @param bucket The address of the bucket for which to decrease the reward per day.
* @param role The role associated with the bucket.
* @param rewardPerDay The amount by which to decrease the reward per day.
*/
function decreaseRewardPerDay(address bucket, Role role, uint256 rewardPerDay) external;
/**
* @notice Returns the accumulated reward for a specific bucket and role.
* @param bucket The address of the bucket for which to retrieve the accumulated reward.
* @param role The role associated with the bucket.
* @return The accumulated reward for the specified bucket and role.
*/
function getBucketAccumulatedReward(address bucket, Role role) external view returns (uint256);
/**
* @notice Returns the claimable reward for a user across multiple buckets.
* @param bucketsArray The array of BucketWithRole objects containing the buckets to check for claimable rewards.
* @param user The address of the user for whom to calculate the claimable reward.
* @return The total claimable reward for the specified user across all provided buckets.
*/
function getClaimableReward(BucketWithRole[] calldata bucketsArray, address user) external view returns (uint256);
/**
* @notice Retrieves the user information from a specific bucket and role.
* @param bucket The address of the bucket from which to retrieve the user information.
* @param role The role associated with the bucket.
* @param user The address of the user for whom to retrieve the information.
* @return A UserInfo struct containing the user information.
*/
function getUserInfoFromBucket(address bucket, Role role, address user) external view returns (UserInfo memory);
}// (c) 2024 Primex.finance
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.18;
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {IPrimexDNSV3} from "../PrimexDNS/IPrimexDNS.sol";
import {ITraderBalanceVault} from "../TraderBalanceVault/ITraderBalanceVault.sol";
interface IActivityRewardDistributorStorage {
/*
* @param oldBalance last updated balance for user
* @param fixedReward the accumulated value of the reward at the time lastUpdatedRewardIndex
* @param lastUpdatedRewardIndex last index with which the user's reward was accumulated
*/
struct UserInfo {
uint256 fixedReward;
uint256 lastUpdatedRewardIndex;
uint256 oldBalance;
}
/*
* @param users data to calculate users rewards in this bucket
* @param rewardIndex an index that accumulates user rewards
* @param lastUpdatedTimestamp timestamp of the last update of user activity
* @param rewardPerToken current reward for one token(PToken or DebtToken of bucket)
* @param isFinished Shows that the bucket has distributed all the rewards
* @param fixedReward reward distributed by a bucket over the past period
* with a certain reward per day or with the entire reward fully distributed
* @param lastUpdatedRewardTimestamp timestamp of last fixed reward update
* @param rewardPerDay current reward distributed for 1 day
* @param totalReward Full distributable reward
* @param endTimestamp end time of the distribution of rewards, which is calculated relative to the rewardPerDay and totalReward
*/
struct BucketInfo {
mapping(address => UserInfo) users;
//accumulated reward per token
uint256 rewardIndex;
uint256 lastUpdatedTimestamp;
uint256 rewardPerToken;
uint256 scaledTotalSupply;
bool isFinished;
// setted by admin's actions
uint256 fixedReward;
uint256 lastUpdatedRewardTimestamp;
uint256 rewardPerDay;
uint256 totalReward;
uint256 endTimestamp;
}
function pmx() external returns (IERC20);
function dns() external returns (IPrimexDNSV3);
function registry() external returns (address);
function traderBalanceVault() external returns (ITraderBalanceVault);
function treasury() external view returns (address);
}// (c) 2024 Primex.finance
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.18;
import {IFeeExecutorStorage} from "./IFeeExecutorStorage.sol";
interface IFeeExecutor is IFeeExecutorStorage {
/**
* @dev Sets tier bonuses for a specific bucket.
* @param _bucket The address of the bucket.
* @param _tiers The array of tier values.
* @param _bonuses The array of NFT bonus parameters.
*/
function setTierBonus(address _bucket, uint256[] calldata _tiers, NFTBonusParams[] calldata _bonuses) external;
/**
* @dev Updates the accumulatedAmount and the lastUpdatedIndex of the existing ActivatedBonus. Called by the Debt-Token
* @param _user User for which the bonus will be updated. If user doesn't have the bonus for paused
* @param _oldScaledBalance Balance of the user before the operation at which the updateBonus function was called (e.g mint/burn)
* @param _bucket The Bucket to which the ActivatedBonus relates
**/
function updateBonus(address _user, uint256 _oldScaledBalance, address _bucket, uint256 _currentIndex) external;
/**
* @dev Updates the accumulatedAmount and the lastUpdatedIndex of the existing ActivatedBonus. Called directly by the user
* @param _nftId Id of activated token
**/
function updateBonus(uint256 _nftId) external;
/**
* @dev Updates the accumulatedAmount and the lastUpdatedIndex of the existing ActivatedBonus. Called by the P-Token or Debt-Token
* @param _users Array of the users for whom the bonus will be updated.
* @param _oldBalances Array of the balances before the operation at which the updateBonus function was called (e.g mint/transfer)
* @param _bucket The Bucket to which the ActivatedBonus relates
**/
function updateBonuses(
address[] memory _users,
uint256[] memory _oldBalances,
address _bucket,
uint256 _currentIndex
) external;
/**
* @dev Returns accumulated amount of p-tokens at the moment
* @param _user The user for which the accumatedAmount will return. If the bonus does not exist will return 0.
* If the NFT does not exist will throw an error
* @param _nftId Id of activated token
* @return The accumulated amount.
*/
function getAccumulatedAmount(address _user, uint256 _nftId) external returns (uint256);
/**
* @dev Returns the available amount (accumulated - claimedAmount) of p-tokens at the moment.
* @param _user The user for which the available amount will return. If the bonus does not exist will return 0.
* If the NFT does not exist will throw an error
* @param _nftId Id of activated token
**/
function getAvailableAmount(address _user, uint256 _nftId) external returns (uint256);
/**
* @dev Retrieves the bonus information for a user and NFT.
* @param _user The address of the user.
* @param _nftId The ID of the NFT.
* @return bonus The activated bonus information.
*/
function getBonus(address _user, uint256 _nftId) external view returns (ActivatedBonus memory);
}// (c) 2024 Primex.finance
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.18;
import {IBucketV3} from "../Bucket/IBucket.sol";
interface IFeeExecutorStorage {
struct ActivatedBonus {
uint256 nftId;
IBucketV3 bucket;
uint256 percent;
uint256 maxAmount;
uint256 accumulatedAmount;
uint256 lastUpdatedIndex;
uint256 deadline;
//if we allow to claim funds before the end of the bonus
uint256 claimedAmount;
}
struct NFTBonusParams {
uint256 percent;
uint256 maxAmount;
uint256 duration;
}
}// (c) 2024 Primex.finance
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.18;
import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import {PrimexPricingLibrary} from "../libraries/PrimexPricingLibrary.sol";
import {IPToken} from "../PToken/IPToken.sol";
import {IDebtToken} from "../DebtToken/IDebtToken.sol";
import {IPositionManager, IPositionManagerV2} from "../PositionManager/IPositionManager.sol";
import {IPriceOracle} from "../PriceOracle/IPriceOracle.sol";
import {IPrimexDNS, IPrimexDNSV3} from "../PrimexDNS/IPrimexDNS.sol";
import {IWhiteBlackList} from "../WhiteBlackList/WhiteBlackList/IWhiteBlackList.sol";
import {IReserve} from "../Reserve/IReserve.sol";
import {ILiquidityMiningRewardDistributor} from "../LiquidityMiningRewardDistributor/ILiquidityMiningRewardDistributor.sol";
import {IInterestRateStrategy} from "../interfaces/IInterestRateStrategy.sol";
import {ISwapManager} from "../SwapManager/ISwapManager.sol";
import {IBucketStorage} from "./IBucketStorage.sol";
import {IBucketEvents} from "./IBucketEvents.sol";
interface IBucket is IBucketStorage, IBucketEvents {
struct ConstructorParams {
string name;
IPToken pToken;
IDebtToken debtToken;
IPositionManager positionManager;
IPriceOracle priceOracle;
IPrimexDNS dns;
IReserve reserve;
IWhiteBlackList whiteBlackList;
address[] assets;
IERC20Metadata borrowedAsset;
uint256 feeBuffer;
uint256 withdrawalFeeRate;
uint256 reserveRate;
// liquidityMining params
ILiquidityMiningRewardDistributor liquidityMiningRewardDistributor;
uint256 liquidityMiningAmount;
uint256 liquidityMiningDeadline;
uint256 stabilizationDuration;
IInterestRateStrategy interestRateStrategy;
uint128 estimatedBar;
uint128 estimatedLar;
uint256 maxAmountPerUser;
bool isReinvestToAaveEnabled;
bytes barCalcParams;
uint256 maxTotalDeposit;
}
event Deposit(address indexed depositer, address indexed pTokenReceiver, uint256 amount);
event DepositToAave(address indexed pool, uint256 amount);
event FeeBufferChanged(uint256 feeBuffer);
event ReserveRateChanged(uint256 reserveRate);
event RatesIndexesUpdated(
uint128 bar,
uint128 lar,
uint128 variableBorrowIndex,
uint128 liquidityIndex,
uint256 timestamp
);
event WithdrawalFeeChanged(uint256 withdrawalFeeRate);
event InterestRateStrategyChanged(address interestRateStrategy);
event AddAsset(address addedAsset);
event RemoveAsset(address deletedAsset);
event MaxTotalDepositChanged(uint256 maxTotalDeposit);
event BarCalculationParamsChanged(bytes params);
event BucketLaunched();
/**
* @dev Initializes the contract with the given parameters.
* @param _params The ConstructorParams struct containing initialization parameters.
* @param _registry The address of the registry contract.
*/
function initialize(ConstructorParams memory _params, address _registry) external;
/**
* @dev Function to add new trading asset for this bucket
* @dev Only callable by the SMALL_TIMELOCK_ADMIN role.
* @param _newAsset The address of trading asset
*/
function addAsset(address _newAsset) external;
/**
* @notice Removes a trading asset from this bucket.
* @dev Only callable by the SMALL_TIMELOCK_ADMIN role.
* @param _assetToDelete The address of the asset to be removed.
*/
function removeAsset(address _assetToDelete) external;
/**
* @dev Sets barCalculationParams (urOptimal, k0, k1, b0, b1)
* @dev Only callable by the MEDIUM_TIMELOCK_ADMIN role.
*/
function setBarCalculationParams(bytes memory _params) external;
/**
* @dev Sets the reserve rate.
* @dev Only callable by the BIG_TIMELOCK_ADMIN role.
* @param _reserveRate The new reserve rate value.
*/
function setReserveRate(uint256 _reserveRate) external;
/**
* @dev Sets the new fee buffer.
* @dev Only callable by the MEDIUM_TIMELOCK_ADMIN role.
* @param _feeBuffer The new fee buffer value.
*/
function setFeeBuffer(uint256 _feeBuffer) external;
/**
* @dev Sets the withdrawal fee.
* @dev Only callable by the BIG_TIMELOCK_ADMIN role.
* @param _withdrawalFee The new withdrawal fee value.
*/
function setWithdrawalFee(uint256 _withdrawalFee) external;
/**
* @dev Sets the interest rate strategy contract address.
* @dev Only callable by the MEDIUM_TIMELOCK_ADMIN role.
* @param _interestRateStrategy The address of the interest rate strategy contract.
*/
function setInterestRateStrategy(address _interestRateStrategy) external;
/**
* @notice The function sets the max total deposit for the particular bucket
* @dev Only callable by the MEDIUM_TIMELOCK_ADMIN role.
* @param _maxTotalDeposit The amount of max total deposit for the bucket
*/
function setMaxTotalDeposit(uint256 _maxTotalDeposit) external;
/**
* @dev Deposits the 'amount' of underlying asset into the bucket. The 'PTokenReceiver' receives overlying pTokens.
* @param _pTokenReceiver The address to receive the deposited pTokens.
* @param _amount The amount of underlying tokens to be deposited
*/
function deposit(address _pTokenReceiver, uint256 _amount) external;
/**
* @dev Withdraws the 'amount' of underlying asset from the bucket. The 'amount' of overlying pTokens will be burned.
* @param _borrowAssetReceiver The address of receiver of the borrowed asset.
* @param amount The amount of underlying tokens to be withdrawn.
*/
function withdraw(address _borrowAssetReceiver, uint256 amount) external;
/**
* @notice Allows the BIG_TIMELOCK_ADMIN role to withdraw a specified amount of tokens after delisting.
* @param _amount The amount of tokens to withdraw.
*/
function withdrawAfterDelisting(uint256 _amount) external;
/**
* @dev Receives a deposit and distributes it to the specified pToken receiver.
* @dev Can be called only by another bucket.
* @param _pTokenReceiver The address of the recipient of the pToken.
* @param _amount The amount of tokens being deposited.
* @param _duration The blocking time for a fixed-term deposit (if it's 0, then it will be a usual deposit)
* @param _bucketFrom The name of the bucket from which the deposit is being made.
*/
function receiveDeposit(
address _pTokenReceiver,
uint256 _amount,
uint256 _duration,
string memory _bucketFrom
) external;
/**
* @notice Deposits (reinvests) funds from a bucket to another bucket.
* Used only in the case of failed liquidity mining in the bucket from where the transfer happens.
* @param _bucketTo The name of the destination bucket.
* @param _swapManager The address of the swap manager.
* @param _megaRoutes The array of routes for swapping tokens.
* @param _amountOutMin The minimum amount of tokens to receive from the swap.
*/
function depositFromBucket(
string calldata _bucketTo,
ISwapManager _swapManager,
PrimexPricingLibrary.MegaRoute[] calldata _megaRoutes,
uint256 _amountOutMin
) external;
/**
* @dev Allows the SMALL_TIMELOCK_ADMIN to withdraw all liquidity from Aave to Bucket.
*/
function returnLiquidityFromAaveToBucket() external;
/**
* @dev Function to update rates and indexes when a trader opens a trading position.
* Mints debt tokens to trader. Calls only by positionManager contract.
* @param _trader The address of the trader, who opens position.
* @param _amount The 'amount' for which the deal is open, and 'amount' of debtTokens will be minted to the trader.
* @param _to The address to transfer the borrowed asset to.
*/
function increaseDebt(address _trader, uint256 _amount, address _to) external;
/**
* @dev Function to update rates and indexes.
* Burns debt tokens of trader. Called only by positionManager contract.
* @param _trader The address of the trader, who opened position.
* @param _debtToBurn The 'amount' of trader's debtTokens will be burned by the trader.
* @param _receiverOfAmountToReturn Treasury in case of liquidation. TraderBalanceVault in other cases
* @param _amountToReturn Amount to transfer from bucket
* @param _permanentLossAmount The amount of the protocol's debt to creditors accrued for this position
*/
function decreaseTraderDebt(
address _trader,
uint256 _debtToBurn,
address _receiverOfAmountToReturn,
uint256 _amountToReturn,
uint256 _permanentLossAmount
) external;
/**
* @notice Batch decreases the debt of multiple traders.
* @dev This function can only be called by the BATCH_MANAGER_ROLE.
* @param _traders An array of addresses representing the traders.
* @param _debtsToBurn An array of uint256 values representing the debts to burn for each trader.
* @param _receiverOfAmountToReturn The address that will receive the amount to be returned.
* @param _amountToReturn The amount to be returned.
* @param _permanentLossAmount The amount of permanent loss.
* @param _length The length of the traders array.
*/
function batchDecreaseTradersDebt(
address[] memory _traders,
uint256[] memory _debtsToBurn,
address _receiverOfAmountToReturn,
uint256 _amountToReturn,
uint256 _permanentLossAmount,
uint256 _length
) external;
/**
* @notice This function allows a user to pay back a permanent loss by burning his pTokens.
* @param amount The amount of pTokens to be burned to pay back the permanent loss.
*/
function paybackPermanentLoss(uint256 amount) external;
/**
* @dev Calculates the permanent loss based on the scaled permanent loss and the normalized income.
* @return The amount of permanent loss.
*/
function permanentLoss() external view returns (uint256);
/**
* @dev Checks if the bucket is deprecated in the protocol.
* @return Whether the bucket is deprecated or not.
*/
function isDeprecated() external view returns (bool);
/**
* @dev Returns a boolean value indicating whether the bucket is delisted.
* @return True if the bucket is delisted, otherwise false.
*/
function isDelisted() external view returns (bool);
/**
* @dev Checks if an admin can withdraw from the bucket after delisting.
* @return A boolean indicating whether withdrawal is available.
*/
function isWithdrawAfterDelistingAvailable() external view returns (bool);
/**
* @dev Checks if this bucket is active in the protocol.
* @return bool True if the bucket is active, false otherwise.
*/
function isActive() external view returns (bool);
/**
* @dev Returns the parameters for liquidity mining.
* @return LMparams The liquidity mining parameters.
*/
function getLiquidityMiningParams() external view returns (LiquidityMiningParams memory);
/**
* @dev Returns a boolean value indicating whether the bucket is stable in the liquidity mining event.
* @return A boolean value representing the stability of the bucket.
*/
function isBucketStable() external view returns (bool);
/**
* @dev Calculates the max leverage according to the following formula:
* ((1 + maintenanceBuffer) * feeBuffer) / ((1 + maintenanceBuffer) * feeBuffer - (1 - securityBuffer) *
* (1 - pairPriceDropBA) * (1 - oracleTolerableLimitAB) * (1 - oracleTolerableLimitBA))
* @param _asset The address of trading asset
* @return The maximum leverage as a uint256 value.
*/
function maxAssetLeverage(address _asset) external view returns (uint256);
/**
* @dev Returns the normalized income per unit of underlying asset, expressed in ray
* @return The normalized income per unit of underlying asset, expressed in ray
*/
function getNormalizedIncome() external view returns (uint256);
/**
* @dev Returns the normalized variable debt per unit of underlying asset, expressed in ray
*/
function getNormalizedVariableDebt() external view returns (uint256);
/**
* @dev Returns allowed trading assets for current bucket
* @return List of addresses of allowed assets
*/
function getAllowedAssets() external view returns (address[] memory);
/**
* @dev Returns current avalable liquidity of borrowedAsset for trading.
* @return The amount of available borrowedAsset
*/
function availableLiquidity() external view returns (uint256);
}
interface IBucketV2 is IBucket {
/**
* @dev Deposits the 'amount' of underlying asset into the bucket. The 'PTokenReceiver' receives overlying pTokens.
* @param _pTokenReceiver The address to receive the deposited pTokens.
* @param _amount The amount of underlying tokens to be deposited
* @param _takeDepositFromWallet A flag indicating whether to make the deposit from user wallet
*/
function deposit(address _pTokenReceiver, uint256 _amount, bool _takeDepositFromWallet) external;
}
interface IBucketV3 is IBucketV2 {
event ChangedBucketExtension(address newBucketExtension);
/**
* @dev Calculates the max leverage according to the following formula:
* ((1 + maintenanceBuffer) * feeBuffer) / ((1 + maintenanceBuffer) * feeBuffer - (1 - securityBuffer) *
* (1 - pairPriceDropBA) * (1 - oracleTolerableLimitAB) * (1 - oracleTolerableLimitBA) + protocolFeeInPositiontAsset / positionSize)
* @param _asset The address of trading asset
* @param _feeRate The ratio of protocolFeeInPositionAsset to positionSize
* @return The maximum leverage as a uint256 value.
*/
function maxAssetLeverage(address _asset, uint256 _feeRate) external view returns (uint256);
/**
* @notice Sets the bucketExtension.
* @dev Only callable by the BIG_TIMELOCK_ADMIN role.
* @param _newBucketExtension The address of BucketExtension contract.
*/
function setBucketExtension(address _newBucketExtension) external;
}
interface IBucketV4 is IBucketV3 {
/**
* @notice Performs a flash loan transfer of a specified amount to a receiver address.
* @dev Only callable by the FLASH_LOAN_MANAGER_ROLE role.
* @param _to The address to which the flash loan amount will be transferred.
* @param _amount The amount of tokens to transfer in the flash loan.
*/
function performFlashLoanTransfer(address _to, uint256 _amount) external;
/**
* @notice Accumulates a predefined amount of asset to the bucket as a fixed, instantaneous income. Used
* to accumulate the flashloan fee to the bucket, and spread it between all the suppliers.
* @dev Only callable by the FLASH_LOAN_MANAGER_ROLE role.
* @param amount The amount to accumulate
* @param availableLiquidity The availableLiquidity before flashLoan
*/
function cumulateToLiquidityIndex(uint256 amount, uint256 availableLiquidity) external;
/**
* @notice Updates bucket's BAR and LAR.
* @dev Only callable by the FLASH_LOAN_MANAGER_ROLE role.
*/
function updateRates() external;
}// (c) 2024 Primex.finance
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.18;
interface IBucketEvents {
event WithdrawFromAave(address indexed pool, uint256 amount);
event Withdraw(address indexed withdrawer, address indexed borrowAssetReceiver, uint256 amount);
event TopUpTreasury(address indexed sender, uint256 amount);
}// (c) 2024 Primex.finance
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.18;
import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import {IPToken} from "../PToken/IPToken.sol";
import {IDebtToken} from "../DebtToken/IDebtToken.sol";
import {IPositionManagerV2} from "../PositionManager/IPositionManager.sol";
import {IPriceOracleV2} from "../PriceOracle/IPriceOracle.sol";
import {IPrimexDNSV3} from "../PrimexDNS/IPrimexDNS.sol";
import {IReserve} from "../Reserve/IReserve.sol";
import {ILiquidityMiningRewardDistributor} from "../LiquidityMiningRewardDistributor/ILiquidityMiningRewardDistributor.sol";
import {IWhiteBlackList} from "../WhiteBlackList/WhiteBlackList/IWhiteBlackList.sol";
import {IInterestRateStrategy} from "../interfaces/IInterestRateStrategy.sol";
interface IBucketStorage {
/**
* @dev Parameters of liquidity mining
*/
struct LiquidityMiningParams {
ILiquidityMiningRewardDistributor liquidityMiningRewardDistributor;
bool isBucketLaunched;
uint256 accumulatingAmount;
uint256 deadlineTimestamp;
uint256 stabilizationDuration;
uint256 stabilizationEndTimestamp;
uint256 maxAmountPerUser; // if maxAmountPerUser is >= accumulatingAmount then check on maxAmountPerUser is off
// Constant max variables are used for calculating users' points.
// These intervals are used for fair distribution of points among Lenders.
// Lenders who brought liquidity earlier receive more than the ones who deposited later.
// To get maximum points per token, a Lender should deposit immediately after the Bucket deployment.
uint256 maxDuration;
uint256 maxStabilizationEndTimestamp;
}
// 1. Corner case of bucket launch
//
// maxDuration
// ------------------------------------------------------------------------------------------------
// | |
// | stabilizationDuration |
// | -------------------------|
// | | bucket launch |
// +--+---------------------------------------------------------------------+-------------------------+------> time
// bucket deploy deadlineTimestamp maxStabilizationEndTimestamp
// (=stabilizationEndTimestamp here)
// (corner case of bucket launch)
// 2. One of cases of bucket launch
//
// | stabilizationDuration
// | -------------------------
// | | |
// +--+------------------+-------------------------+------------------------+-------------------------+------> time
// bucket deploy bucket launch stabilizationEndTimestamp deadlineTimestamp maxStabilizationEndTimestamp
// (after deadline bucket can't be launched)
struct Asset {
uint256 index;
bool isSupported;
}
function liquidityIndex() external returns (uint128);
function variableBorrowIndex() external returns (uint128);
function name() external view returns (string memory);
function registry() external view returns (address);
function positionManager() external view returns (IPositionManagerV2);
function reserve() external view returns (IReserve);
function permanentLossScaled() external view returns (uint256);
function pToken() external view returns (IPToken);
function debtToken() external view returns (IDebtToken);
function borrowedAsset() external view returns (IERC20Metadata);
function feeBuffer() external view returns (uint256);
function withdrawalFeeRate() external view returns (uint256);
/**
* @notice bar = borrowing annual rate (originally APR)
*/
function bar() external view returns (uint128);
/**
* @notice lar = lending annual rate (originally APY)
*/
function lar() external view returns (uint128);
function interestRateStrategy() external view returns (IInterestRateStrategy);
function estimatedBar() external view returns (uint128);
function estimatedLar() external view returns (uint128);
function allowedAssets(address _asset) external view returns (uint256, bool);
function whiteBlackList() external view returns (IWhiteBlackList);
function maxTotalDeposit() external view returns (uint256);
}
interface IBucketStorageV2 {
function bucketExtension() external view returns (address);
}// (c) 2024 Primex.finance
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.26;
import {IArbGasInfo} from "./interfaces/IArbGasInfo.sol";
import {IOVM_GasPriceOracle} from "./interfaces/IOVM_GasPriceOracle.sol";
// admin roles
bytes32 constant BIG_TIMELOCK_ADMIN = 0x00; // It's primary admin.
bytes32 constant MEDIUM_TIMELOCK_ADMIN = keccak256("MEDIUM_TIMELOCK_ADMIN");
bytes32 constant SMALL_TIMELOCK_ADMIN = keccak256("SMALL_TIMELOCK_ADMIN");
bytes32 constant EMERGENCY_ADMIN = keccak256("EMERGENCY_ADMIN");
bytes32 constant GUARDIAN_ADMIN = keccak256("GUARDIAN_ADMIN");
bytes32 constant NFT_MINTER = keccak256("NFT_MINTER");
bytes32 constant TRUSTED_TOLERABLE_LIMIT_ROLE = keccak256("TRUSTED_TOLERABLE_LIMIT_ROLE");
// inter-contract interactions roles
bytes32 constant NO_FEE_ROLE = keccak256("NO_FEE_ROLE");
bytes32 constant VAULT_ACCESS_ROLE = keccak256("VAULT_ACCESS_ROLE");
bytes32 constant PM_ROLE = keccak256("PM_ROLE");
bytes32 constant LOM_ROLE = keccak256("LOM_ROLE");
bytes32 constant BATCH_MANAGER_ROLE = keccak256("BATCH_MANAGER_ROLE");
bytes32 constant FLASH_LOAN_MANAGER_ROLE = keccak256("FLASH_LOAN_MANAGER_ROLE");
bytes32 constant FLASH_LOAN_FREE_BORROWER_ROLE = keccak256("FLASH_LOAN_FREE_BORROWER_ROLE");
// token constants
address constant NATIVE_CURRENCY = address(uint160(bytes20(keccak256("NATIVE_CURRENCY"))));
address constant USD = 0x0000000000000000000000000000000000000348;
address constant NATIVE_CURRENCY_CURVE = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
uint256 constant USD_MULTIPLIER = 10 ** (18 - 8); // usd decimals in chainlink is 8
uint8 constant MAX_ASSET_DECIMALS = 18;
// time constants
uint256 constant SECONDS_PER_YEAR = 365 days;
uint256 constant SECONDS_PER_DAY = 1 days;
uint256 constant HOUR = 1 hours;
uint256 constant TEN_WAD = 10 ether;
// constants for Arbitrum payment model
IArbGasInfo constant ARB_NITRO_ORACLE = IArbGasInfo(0x000000000000000000000000000000000000006C);
uint256 constant TRANSACTION_METADATA_BYTES = 140;
IOVM_GasPriceOracle constant OVM_GASPRICEORACLE = IOVM_GasPriceOracle(0x420000000000000000000000000000000000000F);
uint256 constant GAS_FOR_BYTE = 16;// (c) 2024 Primex.finance
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.18;
import {IAccessControl} from "@openzeppelin/contracts/access/IAccessControl.sol";
import {IERC165Upgradeable} from "@openzeppelin/contracts-upgradeable/utils/introspection/IERC165Upgradeable.sol";
import {IDebtTokenStorage, IBucket, IFeeExecutor, IERC20Upgradeable, IActivityRewardDistributor} from "./IDebtTokenStorage.sol";
interface IDebtToken is IDebtTokenStorage {
/**
* @dev Emitted after the mint action
* @param from The address performing the mint
* @param value The amount being
**/
event Mint(address indexed from, uint256 value);
/**
* @dev Emitted after DebtTokens are burned
* @param from The owner of the aTokens, getting them burned
* @param value The amount being burned
**/
event Burn(address indexed from, uint256 value);
/**
* @dev contract initializer
* @param _name The name of the ERC20 token.
* @param _symbol The symbol of the ERC20 token.
* @param _decimals The number of decimals for the ERC20 token.
* @param _bucketsFactory Address of the buckets factory that will call the setBucket fucntion
*/
function initialize(string memory _name, string memory _symbol, uint8 _decimals, address _bucketsFactory) external;
/**
* @dev Sets the bucket for the contract.
* @param _bucket The address of the bucket to set.
*/
function setBucket(IBucket _bucket) external;
/**
* @dev Sets the FeeDecreaser for current DebtToken.
* @param _feeDecreaser The interest increaser address.
*/
function setFeeDecreaser(IFeeExecutor _feeDecreaser) external;
/**
* @dev Sets the trader reward distributor contract address.
* @param _traderRewardDistributor The address of the trader reward distributor contract.
* Only the BIG_TIMELOCK_ADMIN role can call this function.
*/
function setTraderRewardDistributor(IActivityRewardDistributor _traderRewardDistributor) external;
/**
* @dev Mints `amount` DebtTokens to `user`
* @param _user The address receiving the minted tokens
* @param _amount The amount of tokens getting minted
* @param _index The current variableBorrowIndex
*/
function mint(address _user, uint256 _amount, uint256 _index) external;
/**
* @dev Burns DebtTokens from `user` and sends the equivalent amount of underlying to `receiverOfUnderlying`
* @param _user The owner of the DebtTokens, getting them burned
* @param _amount The amount being burned
* @param _index The current variableBorrowIndex
**/
function burn(address _user, uint256 _amount, uint256 _index) external;
/**
* @dev Burns a batch of tokens from multiple users.
* @param _users An array of user addresses whose tokens will be burned.
* @param _amounts An array of token amounts to be burned for each user.
* @param _index The index used to calculate the scaled amounts.
* @param _length The length of the user and amounts arrays.
*/
function batchBurn(address[] memory _users, uint256[] memory _amounts, uint256 _index, uint256 _length) external;
/**
* @dev Returns the principal debt balance of the user
* @param _user The address of the user.
* @return The scaled balance of the user.
*/
function scaledBalanceOf(address _user) external view returns (uint256);
/**
* @dev Returns the scaled total supply of debtToken.
* @return The scaled total supply of the debtToken.
*/
function scaledTotalSupply() external view returns (uint256);
}// (c) 2024 Primex.finance
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.18;
import {IERC20Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol";
import {IBucket, IBucketV3} from "../Bucket/IBucket.sol";
import {IFeeExecutor} from "../BonusExecutor/IFeeExecutor.sol";
import {IActivityRewardDistributor} from "../ActivityRewardDistributor/IActivityRewardDistributor.sol";
interface IDebtTokenStorage is IERC20Upgradeable {
function bucket() external view returns (IBucketV3);
function feeDecreaser() external view returns (IFeeExecutor);
function traderRewardDistributor() external view returns (IActivityRewardDistributor);
}// Copyright 2020 Compound Labs, Inc.
// SPDX-License-Identifier: BSD-3-Clause
pragma solidity ^0.8.10;
/**
* @title EIP20NonStandardInterface
* @dev Version of ERC20 with no return values for `transfer` and `transferFrom`
* See https://medium.com/coinmonks/missing-return-value-bug-at-least-130-tokens-affected-d67bf08521ca
*/
interface EIP20NonStandardInterface {
/**
* @notice Get the total number of tokens in circulation
* @return The supply of tokens
*/
function totalSupply() external view returns (uint256);
/**
* @notice Gets the balance of the specified address
* @param owner The address from which the balance will be retrieved
* @return balance The balance
*/
function balanceOf(address owner) external view returns (uint256 balance);
///
/// !!!!!!!!!!!!!!
/// !!! NOTICE !!! `transfer` does not return a value, in violation of the ERC-20 specification
/// !!!!!!!!!!!!!!
///
/**
* @notice Transfer `amount` tokens from `msg.sender` to `dst`
* @param dst The address of the destination account
* @param amount The number of tokens to transfer
*/
function transfer(address dst, uint256 amount) external;
///
/// !!!!!!!!!!!!!!
/// !!! NOTICE !!! `transferFrom` does not return a value, in violation of the ERC-20 specification
/// !!!!!!!!!!!!!!
///
/**
* @notice Transfer `amount` tokens from `src` to `dst`
* @param src The address of the source account
* @param dst The address of the destination account
* @param amount The number of tokens to transfer
*/
function transferFrom(address src, address dst, uint256 amount) external;
///
/// !!!!!!!!!!!!!!
/// !!! NOTICE !!! `approve` does not return a value, in violation of the ERC-20 specification
/// !!!!!!!!!!!!!!
///
/**
* @notice Approve `spender` to transfer up to `amount` from `src`
* @dev This will overwrite the approval amount for `spender`
* and is subject to issues noted [here](https://eips.ethereum.org/EIPS/eip-20#approve)
* @param spender The address of the account which may transfer tokens
* @param amount The number of tokens that are approved
*/
function approve(address spender, uint256 amount) external;
/**
* @notice Get the current allowance from `owner` for `spender`
* @param owner The address of the account which owns the tokens to be spent
* @param spender The address of the account which may transfer tokens
* @return remaining The number of tokens allowed to be spent
*/
function allowance(address owner, address spender) external view returns (uint256 remaining);
event Transfer(address indexed from, address indexed to, uint256 amount);
event Approval(address indexed owner, address indexed spender, uint256 amount);
}pragma solidity ^0.8.18;
/// Precompiled contract that exists in every Arbitrum Nitro chain at 0x000000000000000000000000000000000000006c.
interface IArbGasInfo {
// get ArbOS's estimate of the L1 gas price in wei
function getL1BaseFeeEstimate() external view returns (uint256);
/// @notice Get gas prices. Uses the caller's preferred aggregator, or the default if the caller doesn't have a preferred one.
/// @return return gas prices in wei
/// (
/// per L2 tx,
/// per L1 calldata byte
/// per storage allocation,
/// per ArbGas base,
/// per ArbGas congestion,
/// per ArbGas total
/// )
function getPricesInWei() external view returns (uint256, uint256, uint256, uint256, uint256, uint256);
}// (c) 2024 Primex.finance
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.18;
import {PositionLibrary} from "../libraries/PositionLibrary.sol";
import {LimitOrderLibrary} from "../libraries/LimitOrderLibrary.sol";
import {PrimexPricingLibrary} from "../libraries/PrimexPricingLibrary.sol";
interface IConditionalClosingManager {
/**
* @notice Checks if a position can be closed.
* @param _position The position details.
* @param _params The encoded parameters for closing the position.
* @param _additionalParams Additional encoded parameters (not used).
* @param _closeAmount The amount of the position to be closed, measured in the same decimal format as the position's asset.
* @param _borowedAssetAmount The amount of borrowed asset.
* @return A boolean indicating whether the position can be closed.
*/
function canBeClosedAfterSwap(
PositionLibrary.Position calldata _position,
bytes calldata _params,
bytes calldata _additionalParams,
uint256 _closeAmount,
uint256 _borowedAssetAmount,
bytes calldata _positionSoldAssetOracleData
) external payable returns (bool);
}// (c) 2024 Primex.finance
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.18;
import {LimitOrderLibrary} from "../libraries/LimitOrderLibrary.sol";
import {PrimexPricingLibrary} from "../libraries/PrimexPricingLibrary.sol";
interface IConditionalOpeningManager {
/**
* @notice Checks if a limit order can be filled based on the exchange rate.
* @dev This function compares the exchange rate with the limit price.
* @param _order The limit order details.
* @param _params Open condition parameters for the order.
* @param _additionalParams Additional parameters for the order.
* @param _exchangeRate The exchange rate in WAD format to compare with the limit price.
* @return A boolean value indicating if the limit order can be filled based on the exchange rate.
*/
function canBeFilledAfterSwap(
LimitOrderLibrary.LimitOrder calldata _order,
bytes calldata _params,
bytes calldata _additionalParams,
uint256 _exchangeRate
) external pure returns (bool);
}// (c) 2024 Primex.finance
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.18;
import {IQuoter} from "@uniswap/v3-periphery/contracts/interfaces/IQuoter.sol";
import {IPrimexDNSV3} from "../PrimexDNS/IPrimexDNS.sol";
import {ICurveCalc} from "./routers/ICurveCalc.sol";
import {ICurveRegistry} from "./routers/ICurveRegistry.sol";
import {PrimexPricingLibrary} from "../libraries/PrimexPricingLibrary.sol";
interface IDexAdapter {
/**
* @notice Possible dex types
*/
enum DexType {
none, // 0
UniswapV2, // 1 "uniswap", "sushiswap", "quickswap" (v2)
UniswapV3, // 2
Curve, // 3
Balancer, // 4
AlgebraV3, // 5
Meshswap, // 6
Paraswap, //7
Enso //8
}
/*
* @param encodedPath Swap path encoded in bytes
* Encoded differently for different dexes:
* Uniswap v2 - just encoded array of asset addresses
* Uniswap v3 - swap path is a sequence of bytes. In Solidity, a path can be built like that:
* bytes.concat(bytes20(address(weth)), bytes3(uint24(pool1Fee)), bytes20(address(usdc)), bytes3(uint24(pool2Fee)) ...)
* Quickswap - swap path is a sequence of bytes. In Solidity, a path can be built like that:
* bytes.concat(bytes20(address(weth)), bytes20(address(usdc)), bytes20(address(usdt) ...)
* Curve - encoded array of asset addresses and pool addresses
* Balancer - encoded array of asset addresses, pool ids and asset limits
* @param _amountIn TokenA amount in
* @param _amountOutMin Min tokenB amount out
* @param _to Destination address for swap
* @param _deadline Timestamp deadline for swap
* @param _dexRouter Dex router address
*/
struct SwapParams {
bytes encodedPath;
address tokenIn;
address tokenOut;
uint256 amountIn;
uint256 amountOutMin;
address to;
uint256 deadline;
address dexRouter;
}
/*
* @param encodedPath Swap path encoded in bytes
* @param _amountIn TokenA amount in
* @param _dexRouter Dex router address
*/
struct GetAmountsParams {
bytes encodedPath;
uint256 amount; // amountIn or amountOut
address dexRouter;
}
struct AmountParams {
address tokenA;
address tokenB;
uint256 amount;
PrimexPricingLibrary.MegaRoute[] megaRoutes;
}
struct MegaSwapVars {
uint256 sumOfShares;
uint256 amountOnMegaRoute;
uint256 totalAmount;
uint256 remainder;
}
event QuoterChanged(address indexed dexRouter, address indexed quoter);
event DexTypeChanged(address indexed dexRouter, uint256 indexed dexType);
/**
* @param _dexRouter The router address for which the quoter is set
* @param _quoter The quoter address to set
*/
function setQuoter(address _dexRouter, address _quoter) external;
/**
* @notice Set a dex type for a dex router
* @param _dexRouter The dex router address
* @param _dexType The dex type from enum DexType
*/
function setDexType(address _dexRouter, uint256 _dexType) external;
/**
* @notice Swap ERC20 tokens
* @param _params SwapParams struct
*/
function swapExactTokensForTokens(SwapParams memory _params) external payable returns (uint256[3] memory);
/**
* @notice Performs chained getAmountOut calculations
* @notice given an input amount of an asset, returns the maximum output amount of the other asset
* @param _params GetAmountsParams struct
*/
function getAmountsOut(GetAmountsParams memory _params) external returns (uint256[3] memory);
/**
* @notice Performs chained getAmountIn calculations
* @notice given an output amount of an asset, returns the maximum input amount of the other asset
* @param _params GetAmountsParams struct
*/
function getAmountsIn(GetAmountsParams memory _params) external returns (uint256[3] memory);
/**
* @notice Dex type mapping dexRouter => dex type
*/
function dexType(address) external view returns (DexType);
/**
* @notice Mapping from the dexRouter to its quoter
*/
function quoters(address) external view returns (address);
/**
* @return The address of the Registry contract
*/
function registry() external view returns (address);
/**
* @notice Gets the average amount of gas that is required for the swap on some dex
* @param dexRouter The address of a router
*/
function getGas(address dexRouter) external view returns (uint256);
/**
* @notice perform swap of ERC20 tokens by Path structs
* @param tokenIn source token
* @param tokenOut destination token
* @param amountIn amount in the source token
* @param receiver destination address for swap
* @param paths Array of Path structs
*/
function performPathsSwap(
address tokenIn,
address tokenOut,
uint256 amountIn,
address receiver,
PrimexPricingLibrary.Path[] calldata paths
) external payable returns (uint256);
/**
@notice Performs chained getAmountOut calculations by Path structs
@dev The function may not support some types of dex, e.g. the Paraswap
* @param amountIn amount in the source token
* @param paths Array of Path structs
*/
function getAmountsOutByPaths(
uint256 amountIn,
PrimexPricingLibrary.Path[] calldata paths
) external returns (uint256);
/**
@notice Performs chained getAmountsIn calculations by Path structs
@dev The function may not support some types of dex, e.g. the Paraswap
* @param amountOut amount in the destination token
* @param paths Array of Path structs
*/
function getAmountsInByPaths(
uint256 amountOut,
PrimexPricingLibrary.Path[] calldata paths
) external returns (uint256);
/**
@notice perform swap of ERC20 tokens by MegaRoute structs
* @param _params MegaSwapParams struct
*/
function performMegaRoutesSwap(
PrimexPricingLibrary.MegaSwapParams calldata _params
) external payable returns (uint256);
/**
* @notice perform swap of ERC20 tokens by Route structs
* @param tokenIn source token
* @param amountIn amount in the source token
* @param receiver destination address for swap
* @param routes Array of Route structs
*/
function performRoutesSwap(
address tokenIn,
uint256 amountIn,
address receiver,
PrimexPricingLibrary.Route[] calldata routes
) external payable returns (uint256);
/**
@notice Performs chained getAmountsOut calculations by Route structs
@dev The function may not support some types of dex, e.g. the Paraswap
* @param amountIn amount in the source token
* @param routes Array of Route structs
*/
function getAmountsOutByRoutes(
uint256 amountIn,
PrimexPricingLibrary.Route[] calldata routes
) external returns (uint256);
/**
@notice Performs chained getAmountsOut calculations by MegaRoute structs
@dev The function may not support some types of dex, e.g. the Paraswap
* @param _params AmountParams struct
*/
function getAmountOutByMegaRoutes(AmountParams calldata _params) external returns (uint256);
/**
@notice Performs chained getAmountsIn calculations by Route structs
@dev The function may not support some types of dex, e.g. the Paraswap
* @param amountOut amountin the destination token
* @param routes Array of Route structs
*/
function getAmountsInByRoutes(
uint256 amountOut,
PrimexPricingLibrary.Route[] calldata routes
) external returns (uint256);
/**
@notice Performs chained getAmountsIn calculations by MegaRoute structs
@dev The function may not support some types of dex, e.g. the Paraswap
* @param _params AmountParams struct
*/
function getAmountInByMegaRoutes(AmountParams calldata _params) external returns (uint256);
receive() external payable;
/**
* @notice Initializes the DexAdapter contract.
* @dev This function should only be called once during the initial setup of the contract.
* @param _primexDNS The address of the PrimexDNS contract.
*/
function initialize(address _primexDNS) external;
}// (c) 2024 Primex.finance
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.18;
interface IInterestRateStrategy {
/**
* @dev parameters for BAR calculation - they differ depending on bucket's underlying asset
*/
struct BarCalculationParams {
uint256 urOptimal;
uint256 k0;
uint256 k1;
uint256 b0;
int256 b1;
}
event BarCalculationParamsChanged(
address indexed bucket,
uint256 urOptimal,
uint256 k0,
uint256 k1,
uint256 b0,
int256 b1
);
/**
* @dev Updates bucket's BAR and LAR.
* Calculates using utilization ratio (UR):
* BAR = UR <= URoptimal ? (k0 * UR + b0) : (k1 * UR + b1), where 'b1' may be < 0,
* LAR = BAR * UR,
* if reserveRate != 0, then LAR = LAR * (1 - reserveRate)
* @param ur Utilization ratio
* @param reserveRate The reserve portion of the interest that goes to the Primex reserve
* @return tuple containing BAR and LAR
*/
function calculateInterestRates(uint256 ur, uint256 reserveRate) external returns (uint128, uint128);
/**
* @dev Set parameters for BAR calculation.
* @param _params parameters are represented in byte string
*/
function setBarCalculationParams(bytes memory _params) external;
/**
* @dev Retrieves the calculation parameters for the Bar calculation.
* @param _address an address of the bucket
* @return BarCalculationParams struct containing the parameters.
*/
function getBarCalculationParams(address _address) external view returns (BarCalculationParams memory);
}pragma solidity ^0.8.18;
/// Precompiled contract that exist on opBNB chain at 0x420000000000000000000000000000000000000F.
interface IOVM_GasPriceOracle {
/// @notice Retrieves the latest known L1 base fee.
/// @return Latest known L1 base fee.
function l1BaseFee() external view returns (uint256);
/// @notice Retrieves the current fee overhead.
/// @return Current fee overhead.
function overhead() external view returns (uint256);
/// @notice Retrieves the current fee scalar.
/// @return Current fee scalar.
function scalar() external view returns (uint256);
}// Copyright (c) 2016-2024 zOS Global Limited and contributors
// SPDX-License-Identifier: MIT
// Interface for OpenZeppelin's Pausable contract from https://github.com/OpenZeppelin/openzeppelin-contracts/
pragma solidity ^0.8.18;
interface IPausable {
/**
* @dev Triggers stopped state.
* This function can only be called by an address with the EMERGENCY_ADMIN role.
* Requirements:
*
* - The contract must not be paused.
*/
function pause() external;
/**
* @dev Returns to normal state.
* This function can only be called by an address with the SMALL_TIMELOCK_ADMIN or MEDIUM_TIMELOCK_ADMIN role depending on the contract.
* Requirements:
*
* - The contract must be paused.
*/
function unpause() external;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;
interface ISupraOraclePull {
//Verified price data
struct PriceData {
// List of pairs
uint256[] pairs;
// List of prices
// prices[i] is the price of pairs[i]
uint256[] prices;
// List of decimals
// decimals[i] is the decimals of pairs[i]
uint256[] decimals;
}
function verifyOracleProof(bytes calldata _bytesproof) external returns (PriceData memory);
}// (c) 2024 Primex.finance
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;
/* solhint-disable var-name-mixedcase */
interface ISupraSValueFeed {
struct derivedData {
int256 roundDifference;
uint256 derivedPrice;
uint256 decimals;
}
struct priceFeed {
uint256 round;
uint256 decimals;
uint256 time;
uint256 price;
}
function getDerivedSvalue(
uint256 pair_id_1,
uint256 pair_id_2,
uint256 operation
) external view returns (derivedData memory);
function getSvalue(uint256 _pairIndex) external view returns (priceFeed memory);
}// (c) 2024 Primex.finance
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.18;
import {PositionLibrary} from "../libraries/PositionLibrary.sol";
import {PrimexPricingLibrary} from "../libraries/PrimexPricingLibrary.sol";
interface ITakeProfitStopLossCCM {
struct CanBeClosedParams {
uint256 takeProfitPrice;
uint256 stopLossPrice;
}
/**
* @notice Checks if the take profit has been reached based on the given parameters.
* @dev Used in closeBatchPositions() function.
* @param _params The encoded parameters.
* @param exchangeRate The exchange rate in WAD format.
* @return A boolean indicating whether the take profit has been reached.
*/
function isTakeProfitReached(bytes calldata _params, uint256 exchangeRate) external view returns (bool);
/**
* @notice Checks if the stop loss price has been reached for a given position.
* @param _position The position details.
* @param _stopLossPrice The stop loss price in WAD format to compare against.
* @return True if the stop loss price is reached, false otherwise.
*/
function isStopLossReached(
PositionLibrary.Position calldata _position,
uint256 _stopLossPrice,
bytes calldata _positionSoldAssetOracleData
) external returns (bool);
/**
* @notice Checks if the stop loss price has been reached on the given parameters.
* @dev The takeProfitPrice and stopLossPrice values can be obtained from the encoded data via CanBeClosedParams struct.
* @param _params The encoded closing condition parameters containing stop loss price.
* @param oracleExchangeRate The current exchange rate from the oracle in WAD format.
* @return True if the stop loss price is reached, false otherwise.
*/
function isStopLossReached(bytes calldata _params, uint256 oracleExchangeRate) external view returns (bool);
/**
* @notice Retrieves the take profit and stop loss prices from the given parameters.
* @param _params The encoded parameters for closing a position.
* @return takeProfitPrice The take profit price.
* @return stopLossPrice The stop loss price.
*/
function getTakeProfitStopLossPrices(bytes calldata _params) external view returns (uint256, uint256);
/**
* @notice Initializes the TakeProfitStopLossCCM contract.
* @dev This function should only be called once during the initial setup of the contract.
* @param _primexDNS The address of the PrimexDNS contract.
* @param _priceOracle The address of the PriceOracle contract.
*/
function initialize(address _primexDNS, address _priceOracle) external;
}// (c) 2024 Primex.finance
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.18;
interface ICurveCalc {
// solhint-disable func-name-mixedcase
function get_dx(
// solhint-disable-next-line var-name-mixedcase
int128 n_coins,
uint256[8] memory balances,
uint256 amp,
uint256 fee,
uint256[8] memory rates,
uint256[8] memory precisions,
bool underlying,
int128 i,
int128 j,
uint256 dy
) external pure returns (uint256);
}// (c) 2024 Primex.finance
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.18;
interface ICurveRegistry {
// solhint-disable func-name-mixedcase
function get_n_coins(address _pool) external view returns (uint256[2] memory);
function get_rates(address _pool) external view returns (uint256[8] memory);
function get_coin_indices(address _pool, address _from, address _to) external view returns (int128, int128, bool);
}// (c) 2024 Primex.finance
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.18;
import {IKeeperRewardDistributorStorage, IKeeperRewardDistributorStorageV2} from "./IKeeperRewardDistributorStorage.sol";
import {IPausable} from "../interfaces/IPausable.sol";
interface IKeeperRewardDistributorV3 is IKeeperRewardDistributorStorageV2, IPausable {
struct DecreasingGasByReasonParams {
DecreasingReason reason;
uint256 amount;
}
struct MaxGasPerPositionParams {
KeeperActionType actionType;
KeeperActionRewardConfig config;
}
/**
* @dev Params for initialize() function
* @param priceOracle Address of the PriceOracle contract
* @param registry Address of the Registry contract
* @param pmx Address of PMXToken
* @param treasury Address of the Treasury contract
* @param pmxPartInReward Percentage of PMX in reward (in WAD)
* @param nativePartInReward Percentage of native token in reward (in WAD)
* @param positionSizeCoefficient The reward param which is needed to calculate rewards, in WAD
* @param additionalGas Additional gas added to actual gas spent
* @param defaultMaxGasPrice Max gas price allowed during reward calculation (used when no oracle price found)
* @param oracleGasPriceTolerance Percentage by which oracle gas price can be exceeded (in WAD)
* @param paymentModel The model of payment for gas in the network
* @param maxGasPerPositionParams Parameters for the setMaxGasPerPosition function
* @param decreasingGasByReasonParams Parameters for the setDecreasingGasByReason function
*/
struct InitParams {
address priceOracle;
address registry;
address pmx;
address treasury;
address whiteBlackList;
uint256 pmxPartInReward;
uint256 nativePartInReward;
uint256 positionSizeCoefficient;
uint256 additionalGas;
uint256 defaultMaxGasPrice;
uint256 oracleGasPriceTolerance;
PaymentModel paymentModel;
MaxGasPerPositionParams[] maxGasPerPositionParams;
DecreasingGasByReasonParams[] decreasingGasByReasonParams;
}
event ClaimFees(address indexed keeper, address indexed asset, uint256 amount);
event DefaultMaxGasPriceChanged(uint256 indexed defaultMaxGasPrice);
event OracleGasPriceToleranceChanged(uint256 indexed oracleGasPriceTolerance);
event MaxGasPerPositionChanged(KeeperActionType indexed actionType, KeeperActionRewardConfig config);
event DataLengthRestrictionsChanged(KeeperCallingMethod callingMethod, uint256 maxRoutesLength, uint256 baseLength);
event DecreasingGasByReasonChanged(DecreasingReason indexed reason, uint256 amount);
event PmxPartInRewardChanged(uint256 indexed pmxPartInReward);
event NativePartInRewardChanged(uint256 indexed nativePartInReward);
event PositionSizeCoefficientChanged(uint256 indexed positionSizeCoefficient);
event AdditionalGasChanged(uint256 indexed additionalGas);
event KeeperRewardUpdated(address indexed keeper, uint256 rewardInPmx, uint256 rewardInNativeCurrency);
event MinPositionSizeAddendChanged(uint256 newMinPositionSizeAddend);
event OptimisticGasCoefficientChanged(uint256 newOptimismGasCoefficient);
/**
* @notice Initializes the KeeperRewardDistributor contract.
* @param _params Parameters for initialization
*/
function initialize(InitParams calldata _params) external;
/**
* @dev Params for the updateReward function
* @param keeper Address of the keeper
* @param positionAsset Address of the position asset
* @param positionSize Size of the position
* @param action The action that was performed by the keeper
* @param numberOfActions Number of actions performed by the keeper
* @param gasSpent Gas spent on executing transaction
* @param decreasingCounter An array where each index contains the number of decreasing reasons according to the DecreasingReason enum
* @param routesLength The length of routes provided as input to the protocol function,
* subject to an additional commission in the ARBITRUM payment model.
*/
struct UpdateRewardParams {
address keeper;
address positionAsset;
uint256 positionSize;
KeeperActionType action;
uint256 numberOfActions;
uint256 gasSpent;
uint256[] decreasingCounter;
uint256 routesLength;
bytes nativePmxOracleData;
bytes positionNativeAssetOracleData;
}
/**
* @notice Updates reward for keeper for closing position or executing order
* @dev Only callable by the PM_ROLE, LOM_ROLE, BATCH_MANAGER_ROLE roles.
* @param _params The UpdateRewardParams params
*/
function updateReward(UpdateRewardParams calldata _params) external;
/**
* @notice Claims earned reward of the keeper
* @param _pmxAmount Amount of PMX token to claim
* @param _nativeAmount Amount of native token to claim
*/
function claim(uint256 _pmxAmount, uint256 _nativeAmount) external;
/**
* @notice Sets the default maximum gas price allowed.
* @dev Only callable by the MEDIUM_TIMELOCK_ADMIN role.
* @param _defaultMaxGasPrice The new default maximum gas price value.
*/
function setDefaultMaxGasPrice(uint256 _defaultMaxGasPrice) external;
/**
* @notice Sets the amount of gas to be removed for the specified reason
* @dev Only callable by the MEDIUM_TIMELOCK_ADMIN role.
* @param _reason The reason for which an amount is set
* @param _amount Gas amount.
*/
function setDecreasingGasByReason(DecreasingReason _reason, uint256 _amount) external;
/**
* @notice Sets the KeeperActionRewardConfig for the specified action type
* @dev Only callable by the MEDIUM_TIMELOCK_ADMIN role.
* @param _actionType The action type for which the config is set
* @param _config The KeeperActionRewardConfig struct
*/
function setMaxGasPerPosition(KeeperActionType _actionType, KeeperActionRewardConfig calldata _config) external;
/**
* @notice Sets the dataLengthRestrictions for the specified KeeperCallingMethod.
* @dev Only callable by the MEDIUM_TIMELOCK_ADMIN role.
* @param _callingMethod The calling method for which dataLengthRestrictions is set
* @param _maxRoutesLength The maximum routes length for which an additional fee will be paid in the ARBITRUM payment model, in bytes
* @param _baseLength The length of the data entering the protocol function including method signature
* and excluding dynamic types(e.g, routesLength), in bytes
*/
function setDataLengthRestrictions(
KeeperCallingMethod _callingMethod,
uint256 _maxRoutesLength,
uint256 _baseLength
) external;
/**
* @notice Sets the tolerance for gas price fluctuations from the oracle price.
* @dev Only callable by the MEDIUM_TIMELOCK_ADMIN role.
* @param _oracleGasPriceTolerance The new oracle gas price tolerance value (percent expressed as WAD).
*/
function setOracleGasPriceTolerance(uint256 _oracleGasPriceTolerance) external;
/**
* @notice Sets the PMX token's portion in the reward calculation.
* @dev Only callable by the MEDIUM_TIMELOCK_ADMIN role.
* @param _pmxPartInReward The new PMX token's portion in the reward calculation (percent expressed as WAD).
*/
function setPmxPartInReward(uint256 _pmxPartInReward) external;
/**
* @notice Sets the native token's portion in the reward calculation.
* @dev Only callable by the MEDIUM_TIMELOCK_ADMIN role.
* @param _nativePartInReward The new native token's portion in the reward calculation (percent expressed as WAD).
*/
function setNativePartInReward(uint256 _nativePartInReward) external;
/**
* @notice Sets the position size coefficients for reward calculations.
* @dev Only callable by the MEDIUM_TIMELOCK_ADMIN role.
* @param _positionSizeCoefficient The new positionSizeCoefficient value (in WAD).
*/
function setPositionSizeCoefficient(uint256 _positionSizeCoefficient) external;
/**
* @notice Sets the additional gas value for reward calculations.
* @dev Only callable by the MEDIUM_TIMELOCK_ADMIN role.
* @param _additionalGas The new additionalGas value.
*/
function setAdditionalGas(uint256 _additionalGas) external;
/**
* @notice Sets the minPositionSizeAddend for reward calculations.
* @dev Only callable by the MEDIUM_TIMELOCK_ADMIN role.
* @param _minPositionSizeAddend The new minPositionSizeAddend value (in WAD).
*/
function setMinPositionSizeAddend(uint256 _minPositionSizeAddend) external;
/**
* @notice Retrieves gas calculation params.
*
* @return oracleGasPriceTolerance The tolerance for gas price fluctuations based on the oracle.
* @return defaultMaxGasPrice The default maximum gas price allowed.
*/
function getGasCalculationParams() external view returns (uint256, uint256, uint256, PaymentModel);
/**
* @notice Sets the optimisticGasCoefficient for optimism paymentModel.
* @dev Only callable by the MEDIUM_TIMELOCK_ADMIN role.
* @param _newOptimisticGasCoefficient The new optimisticGasCoefficient value (in WAD).
*/
function setOptimisticGasCoefficient(uint256 _newOptimisticGasCoefficient) external;
}// (c) 2024 Primex.finance
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.18;
interface IKeeperRewardDistributorStorage {
enum DecreasingReason {
NonExistentIdForLiquidation,
NonExistentIdForSLOrTP,
IncorrectConditionForLiquidation,
IncorrectConditionForSL,
ClosePostionInTheSameBlock
}
enum KeeperActionType {
OpenByOrder,
StopLoss,
TakeProfit,
Liquidation,
BucketDelisted
}
enum KeeperCallingMethod {
ClosePositionByCondition,
OpenPositionByOrder,
CloseBatchPositions
}
/**
* @dev Structure used in the calculation of keeper rewards in the ARBITRUM payment model
* @param maxRoutesLength The maximum length of routes for which will be paid keeper rewards, depending on KeeperCallingMethod
* @param baseLength The static length of the data entering the protocol function, depending on KeeperCallingMethod
*/
struct DataLengthRestrictions {
uint256 maxRoutesLength;
uint256 baseLength;
}
/**
* @dev Structure used in the calculation of maximum gas per position
* @param baseMaxGas1 Base gas amount that used to calculate max gas amount
* @param baseMaxGas2 Base gas amount that used to calculate max gas amount when number of keeper actions > inflectionPoint
* @param multiplier2 The multiplier which is multiplied by the number of keeper actions when number of keeper actions > inflectionPoint
* @param inflectionPoint Number of actions after which the multiplier2 takes effect
*/
struct KeeperActionRewardConfig {
uint256 baseMaxGas1;
uint256 baseMaxGas2;
uint256 multiplier1;
uint256 multiplier2;
uint256 inflectionPoint;
}
struct KeeperBalance {
uint256 pmxBalance;
uint256 nativeBalance;
}
enum PaymentModel {
DEFAULT,
ARBITRUM,
OPTIMISTIC
}
function priceOracle() external view returns (address);
function registry() external view returns (address);
function pmx() external view returns (address);
function treasury() external view returns (address payable);
function pmxPartInReward() external view returns (uint256);
function nativePartInReward() external view returns (uint256);
function positionSizeCoefficient() external view returns (uint256);
function positionSizeCoefficientB() external view returns (int256);
function additionalGas() external view returns (uint256);
function defaultMaxGasPrice() external view returns (uint256);
function oracleGasPriceTolerance() external view returns (uint256);
function paymentModel() external view returns (PaymentModel);
function keeperBalance(address) external view returns (uint256, uint256);
function maxGasPerPosition(KeeperActionType) external view returns (uint256, uint256, uint256, uint256, uint256);
function dataLengthRestrictions(KeeperCallingMethod) external view returns (uint256, uint256);
function decreasingGasByReason(DecreasingReason) external view returns (uint256);
function totalBalance() external view returns (uint256, uint256);
}
interface IKeeperRewardDistributorStorageV2 is IKeeperRewardDistributorStorage {
function minPositionSizeAddend() external view returns (uint256);
function optimisticGasCoefficient() external view returns (uint256);
}// (c) 2024 Primex.finance
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.26;
// solhint-disable-next-line func-visibility
function _require(bool condition, bytes4 selector) pure {
if (!condition) _revert(selector);
}
// solhint-disable-next-line func-visibility
function _revert(bytes4 selector) pure {
// solhint-disable-next-line no-inline-assembly
assembly ("memory-safe") {
let free_mem_ptr := mload(64)
mstore(free_mem_ptr, selector)
revert(free_mem_ptr, 4)
}
}
library Errors {
event Log(bytes4 error);
//common
error ADDRESS_NOT_SUPPORTED();
error FORBIDDEN();
error AMOUNT_IS_0();
error CALLER_IS_NOT_TRADER();
error CONDITION_INDEX_IS_OUT_OF_BOUNDS();
error INVALID_PERCENT_NUMBER();
error INVALID_SECURITY_BUFFER();
error INVALID_MAINTENANCE_BUFFER();
error TOKEN_ADDRESS_IS_ZERO();
error IDENTICAL_TOKEN_ADDRESSES();
error ASSET_DECIMALS_EXCEEDS_MAX_VALUE();
error CAN_NOT_ADD_WITH_ZERO_ADDRESS();
error SHOULD_BE_DIFFERENT_ASSETS_IN_SPOT();
error TOKEN_NOT_SUPPORTED();
error INSUFFICIENT_DEPOSIT();
error SHOULD_NOT_HAVE_DUPLICATES();
error SWAP_DEADLINE_PASSED();
// error LIMIT_PRICE_IS_ZERO();
error BUCKET_IS_NOT_ACTIVE();
error DIFFERENT_DATA_LENGTH();
error RECIPIENT_OR_SENDER_MUST_BE_ON_WHITE_LIST();
error SLIPPAGE_TOLERANCE_EXCEEDED();
error OPERATION_NOT_SUPPORTED();
error SENDER_IS_BLACKLISTED();
error NATIVE_CURRENCY_CANNOT_BE_ASSET();
error INVALID_AMOUNT();
error POOL_CALL_FAILED();
// bonus executor
error CALLER_IS_NOT_NFT();
error BONUS_FOR_BUCKET_ALREADY_ACTIVATED();
error WRONG_LENGTH();
error BONUS_DOES_NOT_EXIST();
error CALLER_IS_NOT_DEBT_TOKEN();
error CALLER_IS_NOT_P_TOKEN();
error MAX_BONUS_COUNT_EXCEEDED();
error TIER_IS_NOT_ACTIVE();
error BONUS_PERCENT_IS_ZERO();
// bucket
error INCORRECT_LIQUIDITY_MINING_PARAMS();
error PAIR_PRICE_DROP_IS_NOT_CORRECT();
error ASSET_IS_NOT_SUPPORTED();
error BUCKET_OUTSIDE_PRIMEX_PROTOCOL();
error DEADLINE_IS_PASSED();
error DEADLINE_IS_NOT_PASSED();
error BUCKET_IS_NOT_LAUNCHED();
error BURN_AMOUNT_EXCEEDS_PROTOCOL_DEBT();
error LIQUIDITY_INDEX_OVERFLOW();
error BORROW_INDEX_OVERFLOW();
error BAR_OVERFLOW();
error LAR_OVERFLOW();
error UR_IS_MORE_THAN_1();
error ASSET_ALREADY_SUPPORTED();
error DEPOSIT_IS_MORE_AMOUNT_PER_USER();
error DEPOSIT_EXCEEDS_MAX_TOTAL_DEPOSIT();
error MINING_AMOUNT_WITHDRAW_IS_LOCKED_ON_STABILIZATION_PERIOD();
error WITHDRAW_RATE_IS_MORE_10_PERCENT();
error INVALID_FEE_BUFFER();
error RESERVE_RATE_SHOULD_BE_LESS_THAN_1();
error MAX_TOTAL_DEPOSIT_IS_ZERO();
error AMOUNT_SCALED_SHOULD_BE_GREATER_THAN_ZERO();
error NOT_ENOUGH_LIQUIDITY_IN_THE_BUCKET();
// p/debt token, PMXToken
error BUCKET_IS_IMMUTABLE();
error INVALID_MINT_AMOUNT();
error INVALID_BURN_AMOUNT();
error TRANSFER_NOT_SUPPORTED();
error APPROVE_NOT_SUPPORTED();
error CALLER_IS_NOT_BUCKET();
error CALLER_IS_NOT_A_BUCKET_FACTORY();
error CALLER_IS_NOT_P_TOKEN_RECEIVER();
error DURATION_MUST_BE_MORE_THAN_0();
error INCORRECT_ID();
error THERE_ARE_NO_LOCK_DEPOSITS();
error LOCK_TIME_IS_NOT_EXPIRED();
error TRANSFER_AMOUNT_EXCEED_ALLOWANCE();
error CALLER_IS_NOT_A_MINTER();
error ACTION_ONLY_WITH_AVAILABLE_BALANCE();
error FEE_DECREASER_CALL_FAILED();
error TRADER_REWARD_DISTRIBUTOR_CALL_FAILED();
error INTEREST_INCREASER_CALL_FAILED();
error LENDER_REWARD_DISTRIBUTOR_CALL_FAILED();
error DEPOSIT_DOES_NOT_EXIST();
error RECIPIENT_IS_BLACKLISTED();
//LOM
error ORDER_CAN_NOT_BE_FILLED();
error ORDER_DOES_NOT_EXIST();
error ORDER_IS_NOT_SPOT();
error LEVERAGE_MUST_BE_MORE_THAN_1();
error CANNOT_CHANGE_SPOT_ORDER_TO_MARGIN();
error SHOULD_HAVE_OPEN_CONDITIONS();
error INCORRECT_LEVERAGE();
error INCORRECT_DEADLINE();
error LEVERAGE_SHOULD_BE_1();
error LEVERAGE_EXCEEDS_MAX_LEVERAGE();
error SHOULD_OPEN_POSITION();
error IS_SPOT_ORDER();
error SHOULD_NOT_HAVE_CLOSE_CONDITIONS();
error ORDER_HAS_EXPIRED();
error INCORRECT_BORROWED_AMOUNT();
// LiquidityMiningRewardDistributor
error BUCKET_IS_NOT_STABLE();
error ATTEMPT_TO_WITHDRAW_MORE_THAN_DEPOSITED();
error WITHDRAW_PMX_BY_ADMIN_FORBIDDEN();
// nft
error TOKEN_IS_BLOCKED();
error ONLY_MINTERS();
error PROGRAM_IS_NOT_ACTIVE();
error CALLER_IS_NOT_OWNER();
error TOKEN_IS_ALREADY_ACTIVATED();
error WRONG_NETWORK();
error ID_DOES_NOT_EXIST();
error WRONG_URIS_LENGTH();
// PM
error ASSET_ADDRESS_NOT_SUPPORTED();
error IDENTICAL_ASSET_ADDRESSES();
error POSITION_DOES_NOT_EXIST();
error AMOUNT_IS_MORE_THAN_POSITION_AMOUNT();
error BORROWED_AMOUNT_IS_ZERO();
error IS_SPOT_POSITION();
error AMOUNT_IS_MORE_THAN_DEPOSIT();
error DECREASE_AMOUNT_IS_ZERO();
error INSUFFICIENT_DEPOSIT_SIZE();
error IS_NOT_RISKY_OR_CANNOT_BE_CLOSED();
error BUCKET_SHOULD_BE_UNDEFINED();
error DEPOSIT_IN_THIRD_ASSET_ROUTES_LENGTH_SHOULD_BE_0();
error POSITION_CANNOT_BE_CLOSED_FOR_THIS_REASON();
error ADDRESS_IS_ZERO();
error WRONG_TRUSTED_MULTIPLIER();
error POSITION_SIZE_EXCEEDED();
error POSITION_BUCKET_IS_INCORRECT();
error THERE_MUST_BE_AT_LEAST_ONE_POSITION();
error NOTHING_TO_CLOSE();
// BatchManager
error PARAMS_LENGTH_MISMATCH();
error BATCH_CANNOT_BE_CLOSED_FOR_THIS_REASON();
error CLOSE_CONDITION_IS_NOT_CORRECT();
error SOLD_ASSET_IS_INCORRECT();
// Price Oracle
error THERE_IS_DIRECT_ROUTE();
error ZERO_EXCHANGE_RATE();
error NO_PRICEFEED_FOUND();
error NO_PRICE_DROP_FEED_FOUND();
error WRONG_ORACLE_ROUTES_LENGTH();
error WRONG_ASSET_B();
error INCORRECT_ROUTE_SEQUENCE();
error INCORRECT_PYTH_PRICE();
error TOKEN_PAIR_IS_NOT_TRUSTED();
error INCORRECT_TOKEN_TO();
error INCORRECT_PYTH_ROUTE();
error INCORRECT_CHAINLINK_ROUTE();
error NOT_ENOUGH_MSG_VALUE();
error PUBLISH_TIME_EXCEEDS_THRESHOLD_TIME();
//DNS
error INCORRECT_FEE_RATE();
error INCORRECT_RESTRICTIONS();
error BUCKET_ALREADY_FROZEN();
error BUCKET_IS_ALREADY_ADDED();
error DEX_IS_ALREADY_ACTIVATED();
error DEX_IS_ALREADY_FROZEN();
error DEX_IS_ALREADY_ADDED();
error BUCKET_NOT_ADDED();
error BUCKET_ALREADY_ACTIVATED();
error DEX_NOT_ADDED();
error BUCKET_IS_INACTIVE();
error WITHDRAWAL_NOT_ALLOWED();
error BUCKET_IS_ALREADY_DEPRECATED();
error LEVERAGE_TOLERANCE_IS_NOT_CORRECT();
// Primex upkeep
error NUMBER_IS_0();
//referral program, WhiteBlackList
error CALLER_ALREADY_REGISTERED();
error MISMATCH();
error PARENT_NOT_WHITELISTED();
error ADDRESS_ALREADY_WHITELISTED();
error ADDRESS_ALREADY_BLACKLISTED();
error ADDRESS_NOT_BLACKLISTED();
error ADDRESS_NOT_WHITELISTED();
error ADDRESS_NOT_UNLISTED();
error ADDRESS_IS_WHITELISTED();
error ADDRESS_IS_NOT_CONTRACT();
//Reserve
error BURN_AMOUNT_IS_ZERO();
error CALLER_IS_NOT_EXECUTOR();
error ADDRESS_NOT_PRIMEX_BUCKET();
error NOT_SUFFICIENT_RESERVE_BALANCE();
error INCORRECT_TRANSFER_RESTRICTIONS();
//Vault
error AMOUNT_EXCEEDS_AVAILABLE_BALANCE();
error INSUFFICIENT_FREE_ASSETS();
error CALLER_IS_NOT_SPENDER();
//Pricing Library
error IDENTICAL_ASSETS();
error SUM_OF_SHARES_SHOULD_BE_GREATER_THAN_ZERO();
error DIFFERENT_PRICE_DEX_AND_ORACLE();
error LEVERAGE_TOLERANCE_EXCEEDED();
error TAKE_PROFIT_IS_LTE_LIMIT_PRICE();
error STOP_LOSS_IS_GTE_LIMIT_PRICE();
error STOP_LOSS_IS_LTE_LIQUIDATION_PRICE();
error INSUFFICIENT_POSITION_SIZE();
error INCORRECT_PATH();
error DEPOSITED_TO_BORROWED_ROUTES_LENGTH_SHOULD_BE_0();
error INCORRECT_CM_TYPE();
error FEE_RATE_IN_NATIVE_IS_ZERO();
error MIN_PROTOCOL_FEE_IS_GREATER_THAN_PAYMENT_AMOUNT();
// Token transfers
error TOKEN_TRANSFER_IN_FAILED();
error TOKEN_TRANSFER_IN_OVERFLOW();
error TOKEN_TRANSFER_OUT_FAILED();
error TOKEN_APPROVE_FAILED();
error NATIVE_TOKEN_TRANSFER_FAILED();
// Conditional Managers
error LOW_PRICE_ROUND_IS_LESS_HIGH_PRICE_ROUND();
error TRAILING_DELTA_IS_INCORRECT();
error DATA_FOR_ROUND_DOES_NOT_EXIST();
error HIGH_PRICE_TIMESTAMP_IS_INCORRECT();
error NO_PRICE_FEED_INTERSECTION();
error SHOULD_BE_CCM();
error SHOULD_BE_COM();
//Lens
error DEPOSITED_AMOUNT_IS_0();
error SPOT_DEPOSITED_ASSET_SHOULD_BE_EQUAL_BORROWED_ASSET();
error ZERO_ASSET_ADDRESS();
error ASSETS_SHOULD_BE_DIFFERENT();
error ZERO_SHARES();
error SHARES_AMOUNT_IS_GREATER_THAN_AMOUNT_TO_SELL();
error NO_ACTIVE_DEXES();
//Bots
error WRONG_BALANCES();
error INVALID_INDEX();
error INVALID_DIVIDER();
error ARRAYS_LENGTHS_IS_NOT_EQUAL();
error DENOMINATOR_IS_0();
//DexAdapter
error ZERO_AMOUNT_IN();
error ZERO_AMOUNT();
error UNKNOWN_DEX_TYPE();
error REVERTED_WITHOUT_A_STRING_TRY_TO_CHECK_THE_ANCILLARY_DATA();
error DELTA_OF_TOKEN_OUT_HAS_POSITIVE_VALUE();
error DELTA_OF_TOKEN_IN_HAS_NEGATIVE_VALUE();
error QUOTER_IS_NOT_PROVIDED();
error DEX_ROUTER_NOT_SUPPORTED();
error QUOTER_NOT_SUPPORTED();
//SpotTradingRewardDistributor
error PERIOD_DURATION_IS_ZERO();
error REWARD_AMOUNT_IS_ZERO();
error REWARD_PER_PERIOD_IS_NOT_CORRECT();
//ActivityRewardDistributor
error TOTAL_REWARD_AMOUNT_IS_ZERO();
error REWARD_PER_DAY_IS_NOT_CORRECT();
error ZERO_BUCKET_ADDRESS();
//KeeperRewardDistributor
error INCORRECT_PART_IN_REWARD();
error INCORRECT_MULTIPLIER();
error INCORRECT_OPTIMISM_GAS_COEFFICIENT();
//Treasury
error TRANSFER_RESTRICTIONS_NOT_MET();
error INSUFFICIENT_NATIVE_TOKEN_BALANCE();
error INSUFFICIENT_TOKEN_BALANCE();
error EXCEEDED_MAX_AMOUNT_DURING_TIMEFRAME();
error EXCEEDED_MAX_SPENDING_LIMITS();
error SPENDING_LIMITS_ARE_INCORRECT();
error SPENDER_IS_NOT_EXIST();
//FlashLoan
error INCONSISTENT_FLASHLOAN_PARAMS();
error INVALID_FLASHLOAN_EXECUTOR_RETURN();
error FLASH_LOAN_FEE_RATE_IS_MORE_10_PERCENT();
error FLASH_LOAN_PROTOCOL_RATE_IS_MORE_50_PERCENT();
// DepositManager
error REWARD_PERCENT_SHOULD_BE_GREATER_THAN_ZERO();
error TOKEN_CANNOT_BE_P_TOKEN();
}// (c) 2024 Primex.finance
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.26;
import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import {IAccessControl} from "@openzeppelin/contracts/access/IAccessControl.sol";
import {IERC165Upgradeable} from "@openzeppelin/contracts-upgradeable/utils/introspection/IERC165Upgradeable.sol";
import {WadRayMath} from "./utils/WadRayMath.sol";
import {PrimexPricingLibrary} from "./PrimexPricingLibrary.sol";
import {TokenTransfersLibrary} from "./TokenTransfersLibrary.sol";
import {NATIVE_CURRENCY} from "../Constants.sol";
import {ITraderBalanceVault} from "../TraderBalanceVault/ITraderBalanceVault.sol";
import {IPrimexDNSV3} from "../PrimexDNS/IPrimexDNS.sol";
import {IPrimexDNSStorageV3} from "../PrimexDNS/IPrimexDNSStorage.sol";
import {IPriceOracleV2} from "../PriceOracle/IPriceOracle.sol";
import {IBucketV3} from "../Bucket/IBucket.sol";
import {IConditionalOpeningManager} from "../interfaces/IConditionalOpeningManager.sol";
import {IConditionalClosingManager} from "../interfaces/IConditionalClosingManager.sol";
import {IPositionManagerV2} from "../PositionManager/IPositionManager.sol";
import {ISwapManager} from "../SwapManager/ISwapManager.sol";
import "./Errors.sol";
library LimitOrderLibrary {
using WadRayMath for uint256;
enum CloseReason {
FilledMargin,
FilledSpot,
FilledSwap,
Cancelled
}
struct Condition {
uint256 managerType;
bytes params;
}
/**
* @dev Creates a limit order and locks the deposit asset in the traderBalanceVault
* @param bucket The bucket, from which the loan will be taken
* @param positionAsset The address of output token for exchange
* @param depositAsset The address of the deposit token
* @param depositAmount The amount of deposit trader funds for deal
* @param feeToken An asset in which the fee will be paid. At this point it could be the pmx, the epmx or a positionAsset
* @param trader The trader, who has created the order
* @param deadline Unix timestamp after which the order will not be filled
* @param id The unique id of the order
* @param leverage leverage for trading
* @param shouldOpenPosition The flag to indicate whether position should be opened
* @param createdAt The timeStamp when the order was created
* @param updatedConditionsAt The timestamp when the open condition was updated
*/
struct LimitOrder {
IBucketV3 bucket;
address positionAsset;
address depositAsset;
uint256 depositAmount;
address feeToken;
uint256 protocolFee;
address trader;
uint256 deadline;
uint256 id;
uint256 leverage;
bool shouldOpenPosition;
uint256 createdAt;
uint256 updatedConditionsAt;
// The byte-encoded params, can be used for future updates
bytes extraParams;
}
/**
* @dev Structure for the сreateLimitOrder with parameters necessary to create limit order
* @param bucket The bucket, from which the loan will be taken
* @param depositAsset The address of the deposit token (collateral for margin trade or
* locked funds for spot)
* @param depositAmount The amount of deposit funds for deal
* @param positionAsset The address output token for exchange
* @param deadline Unix timestamp after which the order will not be filled
* @param takeDepositFromWallet Bool, add a collateral deposit within the current transaction
* @param leverage leverage for trading
* @param shouldOpenPosition Bool, indicate whether position should be opened
* @param openingManagerAddresses Array of contract addresses that will be called in canBeFilled
* @param openingManagerParams Array of bytes representing params for contracts in openingManagerAddresses
* @param closingManagerAddresses Array of contract addresses that will be called in canBeClosed
* @param closingManagerParams Array of bytes representing params for contracts in closingManagerAddresses
*/
struct CreateLimitOrderParams {
string bucket;
uint256 depositAmount;
address depositAsset;
address positionAsset;
uint256 deadline;
bool takeDepositFromWallet;
uint256 leverage;
bool shouldOpenPosition;
Condition[] openConditions;
Condition[] closeConditions;
bool isProtocolFeeInPmx;
bytes nativeDepositAssetOracleData;
bytes[][] pullOracleData;
uint256[] pullOracleTypes;
}
struct CreateLimitOrderVars {
bool isSpot;
IBucketV3 bucket;
uint256 positionSize;
address priceOracle;
uint256 rate;
IPrimexDNSStorageV3.TradingOrderType tradingOrderType;
bool isThirdAsset;
}
/**
* @dev Opens a position on an existing order
* @param orderId order id
* @param com address of ConditionalOpeningManager
* @param comAdditionalParams params needed for ConditionalOpeningManager to calc canBeFilled
* @param firstAssetMegaRoutes routes to swap first asset
* @param depositInThirdAssetMegaRoutes routes to swap deposit asset
*/
struct OpenPositionParams {
uint256 orderId;
uint256 conditionIndex;
bytes comAdditionalParams;
PrimexPricingLibrary.MegaRoute[] firstAssetMegaRoutes;
PrimexPricingLibrary.MegaRoute[] depositInThirdAssetMegaRoutes;
address keeper;
bytes firstAssetOracleData;
bytes thirdAssetOracleData;
bytes depositSoldAssetOracleData;
bytes nativePmxOracleData;
bytes positionNativeAssetOracleData;
bytes nativePositionAssetOracleData;
bytes pmxPositionAssetOracleData;
bytes positionUsdOracleData;
bytes nativeSoldAssetOracleData;
bytes[][] pullOracleData;
uint256[] pullOracleTypes;
uint256 borrowedAmount;
}
struct OpenPositionByOrderVars {
address assetIn;
address assetOut;
uint256 amountIn;
uint256 amountOut;
CloseReason closeReason;
uint256 newPositionId;
uint256 exchangeRate;
uint256 feeInPositionAsset;
uint256 feeInPmx;
}
/**
* @dev Params for PositionManager to open position
* @param order order
* @param firstAssetMegaRoutes routes to swap first asset on dex
* (borrowedAmount + depositAmount if deposit in borrowedAsset)
* @param depositInThirdAssetMegaRoutes routes to swap deposit in third asset on dex
*/
struct OpenPositionByOrderParams {
address sender;
LimitOrder order;
Condition[] closeConditions;
PrimexPricingLibrary.MegaRoute[] firstAssetMegaRoutes;
PrimexPricingLibrary.MegaRoute[] depositInThirdAssetMegaRoutes;
bytes firstAssetOracleData;
bytes thirdAssetOracleData;
bytes depositSoldAssetOracleData;
bytes positionUsdOracleData;
bytes nativePositionAssetOracleData;
bytes pmxPositionAssetOracleData;
bytes nativeSoldAssetOracleData;
uint256 borrowedAmount;
}
/**
* @dev Structure for the updateOrder with parameters necessary to update limit order
* @param orderId order id to update
* @param depositAmount The amount of deposit funds for deal
* @param makeDeposit Bool, add a collateral deposit within the current transaction
* @param leverage leverage for trading
* @param takeDepositFromWallet Bool, add a collateral deposit within the current transaction
*/
struct UpdateLimitOrderParams {
uint256 orderId;
uint256 depositAmount;
uint256 leverage;
bool isProtocolFeeInPmx;
bool takeDepositFromWallet;
bytes nativeDepositOracleData;
bytes[][] pullOracleData;
uint256[] pullOracleTypes;
}
/**
* @notice Updates the leverage of a limit order.
* @param _order The limit order to update.
* @param _leverage The new leverage value in WAD format for the order.
* @param _primexDNS The instance of the PrimexDNS contract
*/
function updateLeverage(LimitOrder storage _order, uint256 _leverage, IPrimexDNSV3 _primexDNS) public {
_require(_leverage > WadRayMath.WAD, Errors.LEVERAGE_MUST_BE_MORE_THAN_1.selector);
_require(_order.leverage != WadRayMath.WAD, Errors.CANNOT_CHANGE_SPOT_ORDER_TO_MARGIN.selector);
_require(
_leverage <
_order.bucket.maxAssetLeverage(
_order.positionAsset,
_primexDNS.protocolFeeRates(IPrimexDNSStorageV3.FeeRateType.MarginLimitOrderExecuted)
),
Errors.LEVERAGE_EXCEEDS_MAX_LEVERAGE.selector
);
_order.leverage = _leverage;
}
/**
* @notice Updates the deposit details of a LimitOrder.
* @param _order The LimitOrder to update.
* @param _amount The amount of the asset being deposited.
* @param _takeDepositFromWallet Boolean indicating whether to make a deposit or unlock the deposited asset.
* @param traderBalanceVault The instance of ITraderBalanceVault used for deposit and unlock operations.
*/
function updateDeposit(
LimitOrderLibrary.LimitOrder storage _order,
uint256 _amount,
bool _takeDepositFromWallet,
ITraderBalanceVault traderBalanceVault
) public {
depositLockOrUnlock(
traderBalanceVault,
_order.depositAsset,
(_amount > _order.depositAmount) ? _amount - _order.depositAmount : _order.depositAmount - _amount,
_takeDepositFromWallet,
_amount > _order.depositAmount
);
_order.depositAmount = _amount;
}
/**
* @notice Sets the open conditions for a LimitOrder.
* @param _order The limit order.
* @param openConditionsMap The mapping of order IDs to open conditions.
* @param openConditions The array of open conditions.
* @param primexDNS The instance of the Primex DNS contract.
*/
function setOpenConditions(
LimitOrderLibrary.LimitOrder memory _order,
mapping(uint256 => Condition[]) storage openConditionsMap,
Condition[] memory openConditions,
IPrimexDNSV3 primexDNS
) public {
_require(hasNoConditionManagerTypeDuplicates(openConditions), Errors.SHOULD_NOT_HAVE_DUPLICATES.selector);
_require(openConditions.length > 0, Errors.SHOULD_HAVE_OPEN_CONDITIONS.selector);
if (openConditionsMap[_order.id].length > 0) {
delete openConditionsMap[_order.id];
}
Condition memory condition;
for (uint256 i; i < openConditions.length; i++) {
condition = openConditions[i];
_require(
IERC165Upgradeable(primexDNS.cmTypeToAddress(condition.managerType)).supportsInterface(
type(IConditionalOpeningManager).interfaceId
),
Errors.SHOULD_BE_COM.selector
);
openConditionsMap[_order.id].push(condition);
}
}
/**
* @notice Sets the close conditions for a LimitOrder.
* @param _order The limit order.
* @param closeConditionsMap The mapping of order IDs to close conditions.
* @param closeConditions The array of close conditions to set.
* @param primexDNS The Primex DNS contract address.
*/
function setCloseConditions(
LimitOrderLibrary.LimitOrder memory _order,
mapping(uint256 => Condition[]) storage closeConditionsMap,
Condition[] memory closeConditions,
IPrimexDNSV3 primexDNS
) public {
_require(hasNoConditionManagerTypeDuplicates(closeConditions), Errors.SHOULD_NOT_HAVE_DUPLICATES.selector);
_require(
_order.shouldOpenPosition || closeConditions.length == 0,
Errors.SHOULD_NOT_HAVE_CLOSE_CONDITIONS.selector
);
if (closeConditionsMap[_order.id].length > 0) {
delete closeConditionsMap[_order.id];
}
Condition memory condition;
for (uint256 i; i < closeConditions.length; i++) {
condition = closeConditions[i];
_require(
IERC165Upgradeable(primexDNS.cmTypeToAddress(condition.managerType)).supportsInterface(
type(IConditionalClosingManager).interfaceId
),
Errors.SHOULD_BE_CCM.selector
);
closeConditionsMap[_order.id].push(condition);
}
}
/**
* @notice Creates a limit order.
* @param _params The struct containing the order parameters.
* @param pm The instance of the PositionManager contract.
* @param traderBalanceVault The instance of the TraderBalanceVault contract.
* @param primexDNS The instance of the PrimexDNS contract.
* @return The created limit order.
*/
function createLimitOrder(
CreateLimitOrderParams calldata _params,
IPositionManagerV2 pm,
ITraderBalanceVault traderBalanceVault,
IPrimexDNSV3 primexDNS
) public returns (LimitOrder memory) {
_require(_params.leverage >= WadRayMath.WAD, Errors.INCORRECT_LEVERAGE.selector);
_require(_params.deadline > block.timestamp, Errors.INCORRECT_DEADLINE.selector);
CreateLimitOrderVars memory vars;
vars.isSpot = bytes(_params.bucket).length == 0;
vars.positionSize = _params.depositAmount.wmul(_params.leverage);
vars.priceOracle = address(pm.priceOracle());
if (vars.isSpot) {
_require(_params.leverage == WadRayMath.WAD, Errors.LEVERAGE_SHOULD_BE_1.selector);
_require(_params.depositAsset != _params.positionAsset, Errors.SHOULD_BE_DIFFERENT_ASSETS_IN_SPOT.selector);
vars.tradingOrderType = _params.shouldOpenPosition
? IPrimexDNSStorageV3.TradingOrderType.SpotLimitOrder
: IPrimexDNSStorageV3.TradingOrderType.SwapLimitOrder;
} else {
_require(_params.shouldOpenPosition, Errors.SHOULD_OPEN_POSITION.selector);
_require(_params.leverage > WadRayMath.WAD, Errors.LEVERAGE_MUST_BE_MORE_THAN_1.selector);
vars.bucket = IBucketV3(primexDNS.getBucketAddress(_params.bucket));
_require(vars.bucket.getLiquidityMiningParams().isBucketLaunched, Errors.BUCKET_IS_NOT_LAUNCHED.selector);
(, bool tokenAllowed) = vars.bucket.allowedAssets(_params.positionAsset);
_require(tokenAllowed, Errors.TOKEN_NOT_SUPPORTED.selector);
_require(
_params.leverage <
vars.bucket.maxAssetLeverage(
_params.positionAsset,
primexDNS.protocolFeeRates(IPrimexDNSStorageV3.FeeRateType.MarginLimitOrderExecuted)
),
Errors.LEVERAGE_EXCEEDS_MAX_LEVERAGE.selector
);
vars.isThirdAsset =
_params.depositAsset != address(vars.bucket.borrowedAsset()) &&
_params.depositAsset != _params.positionAsset;
vars.tradingOrderType = vars.isThirdAsset
? IPrimexDNSStorageV3.TradingOrderType.MarginLimitOrderDepositInThirdAsset
: IPrimexDNSStorageV3.TradingOrderType.MarginLimitOrder;
}
LimitOrder memory order = LimitOrder({
bucket: IBucketV3(address(0)),
positionAsset: _params.positionAsset,
depositAsset: _params.depositAsset,
depositAmount: _params.depositAmount,
feeToken: _params.isProtocolFeeInPmx ? primexDNS.pmx() : _params.positionAsset,
protocolFee: 0,
trader: msg.sender,
deadline: _params.deadline,
id: 0,
leverage: _params.leverage,
shouldOpenPosition: _params.shouldOpenPosition,
createdAt: block.timestamp,
updatedConditionsAt: block.timestamp,
extraParams: ""
});
order.bucket = vars.bucket;
PrimexPricingLibrary.validateMinPositionSize(
vars.positionSize,
order.depositAsset,
vars.priceOracle,
pm.keeperRewardDistributor(),
primexDNS,
vars.tradingOrderType,
_params.nativeDepositAssetOracleData
);
// deposit locking
depositLockOrUnlock(
traderBalanceVault,
order.depositAsset,
order.depositAmount,
_params.takeDepositFromWallet,
true
);
return order;
}
/**
* @notice Opens a position by order.
* @param order The LimitOrder storage containing order details.
* @param _params The OpenPositionParams calldata containing additional position parameters.
* @param _closeConditions The Condition array containing close conditions for the position.
* @param pm The instance of the PositionManager contract.
* @param traderBalanceVault The instance of the TraderBalanceVault contract.
* @param swapManager The instance of the SwapManager contract.
* @return vars The OpenPositionByOrderVars struct containing the result of the open position operation.
*/
function openPositionByOrder(
LimitOrder storage order,
OpenPositionParams calldata _params,
Condition[] memory _closeConditions,
IPositionManagerV2 pm,
ITraderBalanceVault traderBalanceVault,
ISwapManager swapManager,
uint256 _initialGasLeft
) public returns (OpenPositionByOrderVars memory) {
OpenPositionByOrderVars memory vars;
bool isSpot = address(order.bucket) == address(0);
if (order.protocolFee != 0) {
traderBalanceVault.unlockAsset(
ITraderBalanceVault.UnlockAssetParams({
trader: order.trader,
receiver: order.trader,
asset: order.feeToken,
amount: order.protocolFee
})
);
order.protocolFee = 0;
order.feeToken = order.positionAsset;
}
if (order.shouldOpenPosition) {
vars.closeReason = isSpot ? CloseReason.FilledSpot : CloseReason.FilledMargin;
(vars.amountIn, vars.amountOut, vars.newPositionId, vars.exchangeRate, vars.feeInPositionAsset) = pm
.openPositionByOrder(
OpenPositionByOrderParams({
sender: msg.sender,
order: order,
closeConditions: _closeConditions,
firstAssetMegaRoutes: _params.firstAssetMegaRoutes,
depositInThirdAssetMegaRoutes: _params.depositInThirdAssetMegaRoutes,
firstAssetOracleData: _params.firstAssetOracleData,
thirdAssetOracleData: _params.thirdAssetOracleData,
depositSoldAssetOracleData: _params.depositSoldAssetOracleData,
positionUsdOracleData: _params.positionUsdOracleData,
nativePositionAssetOracleData: _params.nativePositionAssetOracleData,
pmxPositionAssetOracleData: _params.pmxPositionAssetOracleData,
nativeSoldAssetOracleData: _params.nativeSoldAssetOracleData,
borrowedAmount: _params.borrowedAmount
})
);
} else {
_require(
_params.depositInThirdAssetMegaRoutes.length == 0,
Errors.DEPOSIT_IN_THIRD_ASSET_ROUTES_LENGTH_SHOULD_BE_0.selector
);
vars.closeReason = CloseReason.FilledSwap;
vars.amountIn = order.depositAmount;
traderBalanceVault.unlockAsset(
ITraderBalanceVault.UnlockAssetParams({
trader: order.trader,
receiver: address(this),
asset: order.depositAsset,
amount: order.depositAmount
})
);
(vars.amountOut, vars.feeInPositionAsset) = swapManager.swapInLimitOrder(
ISwapManager.SwapInLimitOrderParams({
depositAsset: order.depositAsset,
positionAsset: order.positionAsset,
depositAmount: order.depositAmount,
megaRoutes: _params.firstAssetMegaRoutes,
trader: order.trader,
deadline: order.deadline,
feeToken: order.feeToken,
keeperRewardDistributor: address(pm.keeperRewardDistributor()),
gasSpent: _initialGasLeft - gasleft(),
depositPositionAssetOracleData: _params.firstAssetOracleData,
pmxPositionAssetOracleData: _params.pmxPositionAssetOracleData,
nativePositionAssetOracleData: _params.nativePositionAssetOracleData
}),
pm.getOracleTolerableLimit(order.depositAsset, order.positionAsset)
);
uint256 multiplierDepositAsset = 10 ** (18 - IERC20Metadata(order.depositAsset).decimals());
uint256 multiplierPositionAsset = 10 ** (18 - IERC20Metadata(order.positionAsset).decimals());
vars.exchangeRate =
(vars.amountIn * multiplierDepositAsset).wdiv(
(vars.amountOut + vars.feeInPositionAsset) * multiplierPositionAsset
) /
multiplierDepositAsset;
}
vars.assetIn = isSpot ? order.depositAsset : address(order.bucket.borrowedAsset());
vars.assetOut = order.positionAsset;
return vars;
}
/**
* @notice Checks if an array of Condition structs has no duplicate manager types.
* @param conditions The array of Condition structs to be checked.
* @return bool Boolean value indicating whether the array has no duplicate manager types.
*/
function hasNoConditionManagerTypeDuplicates(Condition[] memory conditions) public pure returns (bool) {
if (conditions.length == 0) {
return true;
}
for (uint256 i; i < conditions.length - 1; i++) {
for (uint256 j = i + 1; j < conditions.length; j++) {
if (conditions[i].managerType == conditions[j].managerType) {
return false;
}
}
}
return true;
}
/**
* @notice This function is used to either deposit or unlock assets in the trader balance vault.
* @param traderBalanceVault The instance of the trader balance vault.
* @param _depositAsset The address of the asset to be deposited or unlocked.
* @param _amount The amount of the asset to be deposited or unlocked.
* @param _takeDepositFromWallet Boolean indicating whether to make a deposit or not.
* @param _isAdd Boolean indicating whether to lock or unlock asset. Should lock asset, if true.
*/
function depositLockOrUnlock(
ITraderBalanceVault traderBalanceVault,
address _depositAsset,
uint256 _amount,
bool _takeDepositFromWallet,
bool _isAdd
) internal {
if (!_isAdd) {
traderBalanceVault.unlockAsset(
ITraderBalanceVault.UnlockAssetParams(msg.sender, msg.sender, _depositAsset, _amount)
);
return;
}
if (_takeDepositFromWallet) {
if (_depositAsset == NATIVE_CURRENCY) {
_require(msg.value >= _amount, Errors.INSUFFICIENT_DEPOSIT.selector);
traderBalanceVault.increaseLockedBalance{value: _amount}(msg.sender, _depositAsset, _amount);
if (msg.value > _amount) {
uint256 rest = msg.value - _amount;
traderBalanceVault.topUpAvailableBalance{value: rest}(msg.sender, NATIVE_CURRENCY, rest);
}
return;
}
TokenTransfersLibrary.doTransferFromTo(_depositAsset, msg.sender, address(traderBalanceVault), _amount);
traderBalanceVault.increaseLockedBalance(msg.sender, _depositAsset, _amount);
return;
}
traderBalanceVault.useTraderAssets(
ITraderBalanceVault.LockAssetParams(
msg.sender,
address(0),
_depositAsset,
_amount,
ITraderBalanceVault.OpenType.CREATE_LIMIT_ORDER
)
);
}
}// (c) 2024 Primex.finance
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.26;
import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import {IAccessControl} from "@openzeppelin/contracts/access/IAccessControl.sol";
import {IERC165Upgradeable} from "@openzeppelin/contracts-upgradeable/utils/introspection/IERC165Upgradeable.sol";
import {WadRayMath} from "./utils/WadRayMath.sol";
import {PrimexPricingLibrary} from "./PrimexPricingLibrary.sol";
import {TokenTransfersLibrary} from "./TokenTransfersLibrary.sol";
import {LimitOrderLibrary} from "./LimitOrderLibrary.sol";
import "./Errors.sol";
import {NATIVE_CURRENCY} from "../Constants.sol";
import {ITraderBalanceVault} from "../TraderBalanceVault/ITraderBalanceVault.sol";
import {IPrimexDNSV3} from "../PrimexDNS/IPrimexDNS.sol";
import {IPrimexDNSStorageV3} from "../PrimexDNS/IPrimexDNSStorage.sol";
import {IPriceOracleV2} from "../PriceOracle/IPriceOracle.sol";
import {IBucketV3} from "../Bucket/IBucket.sol";
import {IConditionalClosingManager} from "../interfaces/IConditionalClosingManager.sol";
import {ITakeProfitStopLossCCM} from "../interfaces/ITakeProfitStopLossCCM.sol";
import {IKeeperRewardDistributorStorage, IKeeperRewardDistributorV3} from "../KeeperRewardDistributor/IKeeperRewardDistributor.sol";
library PositionLibrary {
using WadRayMath for uint256;
event ClosePosition(
uint256 indexed positionId,
address indexed trader,
address indexed closedBy,
address bucketAddress,
address soldAsset,
address positionAsset,
uint256 decreasePositionAmount,
int256 profit,
uint256 positionDebt,
uint256 amountOut,
PositionLibrary.CloseReason reason
);
event PaidProtocolFee(
uint256 indexed positionId,
address indexed trader,
address paymentAsset,
IPrimexDNSStorageV3.FeeRateType indexed feeRateType,
uint256 feeInPaymentAsset,
uint256 feeInPmx
);
/**
* @notice This struct represents a trading position
* @param id unique identifier for the position
* @param scaledDebtAmount scaled debt amount associated with the position
* @param bucket instance of the Bucket associated for trading
* @param soldAsset bucket asset in the case of margin trading or deposit asset in the case of spot trading
* @param depositAmountInSoldAsset equivalent of trader deposit size (this deposit can be in any asset) in the sold asset
* or just deposit amount for spot trading
* @param positionAsset asset of the trading position
* @param positionAmount amount of the trading position
* @param trader address of the trader holding the position
* @param openBorrowIndex variable borrow index when position was opened
* @param createdAt timestamp when the position was created
* @param updatedConditionsAt timestamp when the close condition was updated
* @param extraParams byte-encoded params, utilized for the feeToken address
*/
struct Position {
uint256 id;
uint256 scaledDebtAmount;
IBucketV3 bucket;
address soldAsset;
uint256 depositAmountInSoldAsset;
address positionAsset;
uint256 positionAmount;
address trader;
uint256 openBorrowIndex;
uint256 createdAt;
uint256 updatedConditionsAt;
bytes extraParams;
}
struct IncreaseDepositParams {
uint256 amount;
address asset;
bool takeDepositFromWallet;
PrimexPricingLibrary.MegaRoute[] megaRoutes;
IPrimexDNSV3 primexDNS;
IPriceOracleV2 priceOracle;
ITraderBalanceVault traderBalanceVault;
uint256 amountOutMin;
}
struct DecreaseDepositParams {
uint256 amount;
IPrimexDNSV3 primexDNS;
IPriceOracleV2 priceOracle;
ITraderBalanceVault traderBalanceVault;
uint256 pairPriceDrop;
uint256 securityBuffer;
uint256 oracleTolerableLimit;
uint256 maintenanceBuffer;
address keeperRewardDistributor;
bytes positionSoldAssetOracleData;
bytes nativeSoldAssetOracleData;
}
struct MegaSwapParams {
address tokenA;
address tokenB;
uint256 amountTokenA;
PrimexPricingLibrary.MegaRoute[] megaRoutes;
address receiver;
uint256 deadline;
bool takeDepositFromWallet;
IPrimexDNSV3 primexDNS;
IPriceOracleV2 priceOracle;
ITraderBalanceVault traderBalanceVault;
}
struct ClosePositionParams {
uint256 closeAmount;
uint256 depositDecrease;
uint256 scaledDebtAmount;
address depositReceiver;
PrimexPricingLibrary.MegaRoute[] megaRoutes;
uint256 amountOutMin;
uint256 oracleTolerableLimit;
IPrimexDNSV3 primexDNS;
IPriceOracleV2 priceOracle;
ITraderBalanceVault traderBalanceVault;
LimitOrderLibrary.Condition closeCondition;
bytes ccmAdditionalParams;
bool borrowedAmountIsNotZero;
uint256 pairPriceDrop;
uint256 securityBuffer;
bool needOracleTolerableLimitCheck;
uint256 initialGasLeft;
address keeperRewardDistributor;
bytes positionSoldAssetOracleData;
bytes pmxSoldAssetOracleData;
bytes nativeSoldAssetOracleData;
}
struct ClosePositionVars {
address payable dexAdapter;
uint256 borowedAssetAmount;
uint256 amountToReturn;
uint256 permanentLoss;
uint256 fee;
uint256 gasSpent;
}
struct ClosePositionEventData {
int256 profit;
uint256 debtAmount;
uint256 amountOut;
uint256 amountOutAfterFee;
IKeeperRewardDistributorStorage.KeeperActionType actionType;
address trader;
address paymentAsset;
IPrimexDNSStorageV3.FeeRateType feeRateType;
uint256 feeInPaymentAsset;
uint256 feeInPmx;
}
struct OpenPositionVars {
PrimexPricingLibrary.MegaRoute[] firstAssetMegaRoutes;
PrimexPricingLibrary.MegaRoute[] depositInThirdAssetMegaRoutes;
PrimexPricingLibrary.DepositData depositData;
uint256 borrowedAmount;
uint256 amountOutMin;
uint256 deadline;
bool isSpot;
bool isThirdAsset;
bool takeDepositFromWallet;
bool byOrder;
uint256 orderLeverage;
address sender;
LimitOrderLibrary.Condition[] closeConditions;
bool needOracleTolerableLimitCheck;
bytes firstAssetOracleData;
bytes thirdAssetOracleData;
bytes positionUsdOracleData;
bytes nativePositionAssetOracleData;
bytes pmxPositionAssetOracleData;
bytes nativeSoldAssetOracleData;
}
struct OpenPositionEventData {
uint256 feeInPositionAsset;
uint256 feeInPmx;
uint256 entryPrice;
uint256 leverage;
IPrimexDNSStorageV3.FeeRateType feeRateType;
}
/**
* The struct for openPosition function local vars
*/
struct OpenPositionLocalData {
uint256 amountToTransfer;
address payable dexAdapter;
address depositReceiver;
uint256 depositInPositionAsset;
bool isSpot;
IPrimexDNSStorageV3.TradingOrderType tradingOrderType;
uint256 positionAmountAfterFeeInSoldAsset;
uint256 borrowedAmountInPositionAsset;
uint256 leverage;
uint256 multiplierBorrowedAsset;
uint256 multiplierPositionAsset;
address positionAsset;
uint256 positionAmount;
}
/**
* @dev Structure for the OpenPositionParams when margin trading is activated
* @param bucket The bucket, from which the loan will be taken
* @param borrowedAmount The amount of tokens borrowed to be exchanged
* @param depositInThirdAssetMegaRoutes routes to swap deposit in third asset on dex
*/
struct OpenPositionMarginParams {
string bucket;
uint256 borrowedAmount;
PrimexPricingLibrary.MegaRoute[] depositInThirdAssetMegaRoutes;
}
/**
* @dev Structure for the openPosition with parameters necessary to open a position
* @param marginParams margin trading related params
* @param firstAssetMegaRoutes routes to swap first asset on dex
* (borrowedAmount + depositAmount if deposit in borrowedAsset)
* @param depositAsset The address of the deposit token (collateral for margin trade or
* locked funds for spot)
* @param depositAmount The amount of deposit funds for deal
* @param positionAsset The address output token for exchange
* @param amountOutMin The minimum amount of output tokens
* that must be received for the transaction not to revert.
* @param deadline Unix timestamp after which the transaction will revert.
* @param takeDepositFromWallet Bool, add a deposit within the current transaction
* @param closeConditions Array of conditions that position can be closed by
*/
struct OpenPositionParams {
OpenPositionMarginParams marginParams;
PrimexPricingLibrary.MegaRoute[] firstAssetMegaRoutes;
address depositAsset;
uint256 depositAmount;
address positionAsset;
uint256 amountOutMin;
uint256 deadline;
bool takeDepositFromWallet;
bool isProtocolFeeInPmx;
LimitOrderLibrary.Condition[] closeConditions;
bytes firstAssetOracleData;
bytes thirdAssetOracleData;
bytes depositSoldAssetOracleData;
bytes positionUsdOracleData;
bytes nativePositionAssetOracleData;
bytes pmxPositionAssetOracleData;
bytes nativeSoldAssetOracleData;
bytes[][] pullOracleData;
uint256[] pullOracleTypes;
}
struct PositionManagerParams {
IPrimexDNSV3 primexDNS;
IPriceOracleV2 priceOracle;
ITraderBalanceVault traderBalanceVault;
uint256 oracleTolerableLimit;
uint256 oracleTolerableLimitForThirdAsset;
uint256 maxPositionSize;
uint256 initialGasLeft;
address keeperRewardDistributor;
}
struct ScaledParams {
uint256 decreasePercent;
uint256 scaledDebtAmount;
uint256 depositDecrease;
bool borrowedAmountIsNotZero;
}
enum CloseReason {
CLOSE_BY_TRADER,
RISKY_POSITION,
BUCKET_DELISTED,
LIMIT_CONDITION,
BATCH_LIQUIDATION,
BATCH_STOP_LOSS,
BATCH_TAKE_PROFIT
}
/**
* @dev Increases the deposit amount for a position.
* @param position The storage reference to the position.
* @param params The parameters for increasing the deposit.
* @return The amount of trader debtTokens burned.
*/
function increaseDeposit(Position storage position, IncreaseDepositParams memory params) public returns (uint256) {
_require(msg.sender == position.trader, Errors.CALLER_IS_NOT_TRADER.selector);
_require(position.scaledDebtAmount != 0, Errors.BORROWED_AMOUNT_IS_ZERO.selector);
address borrowedAsset = position.soldAsset;
uint256 depositAmountInBorrowed;
address depositReceiver = params.primexDNS.dexAdapter();
if (params.asset == borrowedAsset) {
depositReceiver = address(position.bucket);
depositAmountInBorrowed = params.amount;
}
if (params.takeDepositFromWallet) {
TokenTransfersLibrary.doTransferFromTo(params.asset, msg.sender, depositReceiver, params.amount);
} else {
params.traderBalanceVault.useTraderAssets(
ITraderBalanceVault.LockAssetParams(
msg.sender,
depositReceiver,
params.asset,
params.amount,
ITraderBalanceVault.OpenType.OPEN
)
);
}
if (params.asset != borrowedAsset) {
depositAmountInBorrowed = PrimexPricingLibrary.megaSwap(
PrimexPricingLibrary.MegaSwapParams({
tokenA: params.asset,
tokenB: borrowedAsset,
amountTokenA: params.amount,
megaRoutes: params.megaRoutes,
receiver: address(position.bucket),
deadline: block.timestamp
}),
0,
payable(params.primexDNS.dexAdapter()),
address(params.priceOracle),
false,
new bytes(0)
);
_require(depositAmountInBorrowed >= params.amountOutMin, Errors.SLIPPAGE_TOLERANCE_EXCEEDED.selector);
}
uint256 debt = getDebt(position);
uint256 amountToTrader;
uint256 debtToBurn = depositAmountInBorrowed;
if (depositAmountInBorrowed >= debt) {
amountToTrader = depositAmountInBorrowed - debt;
debtToBurn = debt;
position.scaledDebtAmount = 0;
if (amountToTrader > 0)
params.traderBalanceVault.topUpAvailableBalance(position.trader, borrowedAsset, amountToTrader);
} else {
position.scaledDebtAmount =
position.scaledDebtAmount -
debtToBurn.rdiv(position.bucket.getNormalizedVariableDebt());
}
position.depositAmountInSoldAsset += debtToBurn;
position.bucket.decreaseTraderDebt(
position.trader,
debtToBurn,
address(params.traderBalanceVault),
amountToTrader,
0
);
return debtToBurn;
}
/**
* @dev Decreases the deposit amount for a position.
* @param position The storage reference to the position.
* @param params The parameters for the decrease deposit operation.
*/
function decreaseDeposit(Position storage position, DecreaseDepositParams memory params) public {
_require(msg.sender == position.trader, Errors.CALLER_IS_NOT_TRADER.selector);
_require(position.bucket != IBucketV3(address(0)), Errors.IS_SPOT_POSITION.selector);
_require(position.bucket.isActive(), Errors.BUCKET_IS_NOT_ACTIVE.selector);
_require(params.amount > 0, Errors.DECREASE_AMOUNT_IS_ZERO.selector);
_require(params.amount <= position.depositAmountInSoldAsset, Errors.AMOUNT_IS_MORE_THAN_DEPOSIT.selector);
position.depositAmountInSoldAsset -= params.amount;
position.scaledDebtAmount =
position.scaledDebtAmount +
params.amount.rdiv(position.bucket.getNormalizedVariableDebt());
params.traderBalanceVault.topUpAvailableBalance(position.trader, position.soldAsset, params.amount);
uint256 feeInPaymentAsset = decodeFeeTokenAddress(position.extraParams) == address(0)
? 0
: PrimexPricingLibrary.calculateFeeInPaymentAsset(
PrimexPricingLibrary.CalculateFeeInPaymentAssetParams({
primexDNS: params.primexDNS,
priceOracle: address(params.priceOracle),
feeRateType: IPrimexDNSStorageV3.FeeRateType.MarginPositionClosedByKeeper,
paymentAsset: position.soldAsset,
paymentAmount: params.amount,
keeperRewardDistributor: params.keeperRewardDistributor,
gasSpent: 0,
isFeeProhibitedInPmx: true,
nativePaymentAssetOracleData: params.nativeSoldAssetOracleData
})
);
_require(
health(
position,
params.priceOracle,
params.pairPriceDrop,
params.securityBuffer,
params.oracleTolerableLimit,
feeInPaymentAsset,
params.positionSoldAssetOracleData
) >= WadRayMath.WAD + params.maintenanceBuffer,
Errors.INSUFFICIENT_DEPOSIT_SIZE.selector
);
position.bucket.increaseDebt(position.trader, params.amount, address(params.traderBalanceVault));
}
/**
* @notice Closes a position.
* @param position The position to be closed.
* @param params The parameters for closing the position.
* @param reason The reason for closing the position.
* @return posEventData The event data for the closed position.
*/
function closePosition(
Position memory position,
ClosePositionParams memory params,
CloseReason reason
) public returns (ClosePositionEventData memory) {
ClosePositionEventData memory posEventData;
ClosePositionVars memory vars;
if (params.borrowedAmountIsNotZero) {
posEventData.debtAmount = params.scaledDebtAmount.rmul(position.bucket.getNormalizedVariableDebt());
}
vars.dexAdapter = payable(params.primexDNS.dexAdapter());
TokenTransfersLibrary.doTransferOut(position.positionAsset, vars.dexAdapter, params.closeAmount);
posEventData.amountOut = PrimexPricingLibrary.megaSwap(
PrimexPricingLibrary.MegaSwapParams({
tokenA: position.positionAsset,
tokenB: position.soldAsset,
amountTokenA: params.closeAmount,
megaRoutes: params.megaRoutes,
receiver: address(this),
deadline: block.timestamp
}),
params.oracleTolerableLimit,
vars.dexAdapter,
address(params.priceOracle),
params.needOracleTolerableLimitCheck,
params.positionSoldAssetOracleData
);
posEventData.paymentAsset = decodeFeeTokenAddress(position.extraParams);
if (reason == CloseReason.CLOSE_BY_TRADER) {
posEventData.feeRateType = params.borrowedAmountIsNotZero
? IPrimexDNSStorageV3.FeeRateType.MarginPositionClosedByTrader
: IPrimexDNSStorageV3.FeeRateType.SpotPositionClosedByTrader;
vars.gasSpent = 0;
} else {
posEventData.feeRateType = params.borrowedAmountIsNotZero
? IPrimexDNSStorageV3.FeeRateType.MarginPositionClosedByKeeper
: IPrimexDNSStorageV3.FeeRateType.SpotPositionClosedByKeeper;
vars.gasSpent = params.initialGasLeft - gasleft();
}
(posEventData.feeInPaymentAsset, posEventData.feeInPmx) = PrimexPricingLibrary.payProtocolFee(
PrimexPricingLibrary.ProtocolFeeParams({
feeToken: posEventData.paymentAsset,
trader: position.trader,
priceOracle: address(params.priceOracle),
feeRateType: posEventData.feeRateType,
traderBalanceVault: params.traderBalanceVault,
swapManager: address(0),
keeperRewardDistributor: params.keeperRewardDistributor,
primexDNS: params.primexDNS,
paymentAsset: position.soldAsset,
paymentAmount: posEventData.amountOut,
gasSpent: vars.gasSpent,
isFeeProhibitedInPmx: reason == CloseReason.RISKY_POSITION,
pmxPaymentAssetOracleData: params.pmxSoldAssetOracleData,
nativePaymentAssetOracleData: params.nativeSoldAssetOracleData
})
);
posEventData.amountOutAfterFee = posEventData.amountOut - posEventData.feeInPaymentAsset;
TokenTransfersLibrary.doTransferOut({
token: position.soldAsset,
to: params.borrowedAmountIsNotZero ? address(position.bucket) : address(params.traderBalanceVault),
amount: posEventData.amountOutAfterFee
});
_require(
posEventData.amountOut >= params.amountOutMin && posEventData.amountOut > 0,
Errors.SLIPPAGE_TOLERANCE_EXCEEDED.selector
);
bool canBeClosed;
if (reason == CloseReason.CLOSE_BY_TRADER) {
canBeClosed = position.trader == msg.sender;
} else if (reason == CloseReason.RISKY_POSITION) {
canBeClosed =
health(
position,
params.priceOracle,
params.pairPriceDrop,
params.securityBuffer,
params.oracleTolerableLimit,
posEventData.feeInPaymentAsset,
params.positionSoldAssetOracleData
) <
WadRayMath.WAD;
posEventData.actionType = IKeeperRewardDistributorStorage.KeeperActionType.Liquidation;
} else if (reason == CloseReason.LIMIT_CONDITION) {
address cm = params.primexDNS.cmTypeToAddress(params.closeCondition.managerType);
_require(cm != address(0), Errors.INCORRECT_CM_TYPE.selector);
canBeClosed = IConditionalClosingManager(cm).canBeClosedAfterSwap(
position,
params.closeCondition.params,
params.ccmAdditionalParams,
params.closeAmount,
posEventData.amountOut,
params.positionSoldAssetOracleData
);
posEventData.actionType = IKeeperRewardDistributorStorage.KeeperActionType.StopLoss;
} else if (reason == CloseReason.BUCKET_DELISTED) {
canBeClosed = position.bucket != IBucketV3(address(0)) && position.bucket.isDelisted();
posEventData.actionType = IKeeperRewardDistributorStorage.KeeperActionType.BucketDelisted;
}
_require(canBeClosed, Errors.POSITION_CANNOT_BE_CLOSED_FOR_THIS_REASON.selector);
if (posEventData.amountOutAfterFee > posEventData.debtAmount) {
unchecked {
vars.amountToReturn = posEventData.amountOutAfterFee - posEventData.debtAmount;
}
} else {
unchecked {
vars.permanentLoss = posEventData.debtAmount - posEventData.amountOutAfterFee;
}
}
posEventData.profit = -int256(params.depositDecrease);
if (reason != CloseReason.RISKY_POSITION) {
if (vars.amountToReturn > 0) {
posEventData.profit += int256(vars.amountToReturn);
params.traderBalanceVault.topUpAvailableBalance(
reason == CloseReason.CLOSE_BY_TRADER ? params.depositReceiver : position.trader,
position.soldAsset,
vars.amountToReturn
);
}
}
if (params.borrowedAmountIsNotZero) {
position.bucket.decreaseTraderDebt(
position.trader,
posEventData.debtAmount,
reason == CloseReason.RISKY_POSITION ? params.primexDNS.treasury() : address(params.traderBalanceVault),
vars.amountToReturn,
vars.permanentLoss
);
}
// to avoid stack to deep
CloseReason _reason = reason;
if (params.closeAmount == position.positionAmount) {
emit ClosePosition({
positionId: position.id,
trader: position.trader,
closedBy: msg.sender,
bucketAddress: address(position.bucket),
soldAsset: position.soldAsset,
positionAsset: position.positionAsset,
decreasePositionAmount: position.positionAmount,
profit: posEventData.profit,
positionDebt: posEventData.debtAmount,
amountOut: posEventData.amountOutAfterFee,
reason: _reason
});
}
posEventData.trader = position.trader;
return posEventData;
}
/**
* @dev Sets the maximum position size between two tokens.
* @param maxPositionSize The storage mapping for maximum position sizes.
* @param token0 The address of token0.
* @param token1 The address of token1.
* @param amountInToken0 The maximum position size in token0.
* @param amountInToken1 The maximum position size in token1.
*/
function setMaxPositionSize(
mapping(address => mapping(address => uint256)) storage maxPositionSize,
address token0,
address token1,
uint256 amountInToken0,
uint256 amountInToken1
) public {
_require(token0 != address(0) && token1 != address(0), Errors.TOKEN_ADDRESS_IS_ZERO.selector);
_require(token0 != token1, Errors.IDENTICAL_ASSET_ADDRESSES.selector);
maxPositionSize[token1][token0] = amountInToken0;
maxPositionSize[token0][token1] = amountInToken1;
}
/**
* @dev Sets the tolerable limit for an oracle between two assets.
* @param oracleTolerableLimits The mapping to store oracle tolerable limits.
* @param assetA The address of the first asset.
* @param assetB The address of the second asset.
* @param percent The percentage tolerable limit for the oracle in WAD format (1 WAD = 100%).
*/
function setOracleTolerableLimit(
mapping(address => mapping(address => uint256)) storage oracleTolerableLimits,
address assetA,
address assetB,
uint256 percent
) public {
_require(assetA != address(0) && assetB != address(0), Errors.ASSET_ADDRESS_NOT_SUPPORTED.selector);
_require(assetA != assetB, Errors.IDENTICAL_ASSET_ADDRESSES.selector);
_require(percent <= WadRayMath.WAD && percent > 0, Errors.INVALID_PERCENT_NUMBER.selector);
oracleTolerableLimits[assetA][assetB] = percent;
oracleTolerableLimits[assetB][assetA] = percent;
}
/**
* @dev Sets the close conditions for a given position.
* @param position The position for which to set the close conditions.
* @param closeConditionsMap The storage mapping of close conditions for each position ID.
* @param closeConditions The array of close conditions to be set.
* @param primexDNS The address of the IPrimexDNS contract.
*/
function setCloseConditions(
Position memory position,
mapping(uint256 => LimitOrderLibrary.Condition[]) storage closeConditionsMap,
LimitOrderLibrary.Condition[] memory closeConditions,
IPrimexDNSV3 primexDNS
) public {
_require(
LimitOrderLibrary.hasNoConditionManagerTypeDuplicates(closeConditions),
Errors.SHOULD_NOT_HAVE_DUPLICATES.selector
);
if (closeConditionsMap[position.id].length > 0) {
delete closeConditionsMap[position.id];
}
LimitOrderLibrary.Condition memory condition;
for (uint256 i; i < closeConditions.length; i++) {
condition = closeConditions[i];
_require(
IERC165Upgradeable(primexDNS.cmTypeToAddress(condition.managerType)).supportsInterface(
type(IConditionalClosingManager).interfaceId
),
Errors.SHOULD_BE_CCM.selector
);
closeConditionsMap[position.id].push(condition);
}
}
/**
* @notice Opens a position by depositing assets and borrowing funds (except when the position is spot)
* @param _position The position to be opened
* @param _vars Variables related to the position opening
* @param _pmParams Parameters for the PositionManager contract
* @return The updated position and event data
*/
function openPosition(
Position memory _position,
OpenPositionVars memory _vars,
PositionManagerParams memory _pmParams
) public returns (Position memory, OpenPositionEventData memory) {
OpenPositionLocalData memory data;
if (_vars.isSpot) {
data.tradingOrderType = _vars.byOrder
? IPrimexDNSStorageV3.TradingOrderType.SpotLimitOrder
: IPrimexDNSStorageV3.TradingOrderType.SpotMarketOrder;
} else {
if (_vars.byOrder) {
data.tradingOrderType = _vars.isThirdAsset
? IPrimexDNSStorageV3.TradingOrderType.MarginLimitOrderDepositInThirdAsset
: IPrimexDNSStorageV3.TradingOrderType.MarginLimitOrder;
} else {
data.tradingOrderType = IPrimexDNSStorageV3.TradingOrderType.MarginMarketOrder;
}
}
PrimexPricingLibrary.validateMinPositionSize(
_vars.borrowedAmount + _position.depositAmountInSoldAsset,
_position.soldAsset,
address(_pmParams.priceOracle),
IKeeperRewardDistributorV3(_pmParams.keeperRewardDistributor),
_pmParams.primexDNS,
data.tradingOrderType,
_vars.nativeSoldAssetOracleData
);
data.amountToTransfer = _vars.borrowedAmount;
data.dexAdapter = payable(_pmParams.primexDNS.dexAdapter());
data.depositReceiver = data.dexAdapter;
if (_vars.depositData.depositAsset == _position.positionAsset) {
_position.positionAmount = _vars.depositData.depositAmount;
data.depositInPositionAsset = _vars.depositData.depositAmount;
data.depositReceiver = address(this);
} else if (_vars.depositData.depositAsset == _position.soldAsset) {
data.amountToTransfer += _vars.depositData.depositAmount;
}
data.isSpot = _vars.borrowedAmount == 0;
if (data.isSpot) _vars.depositData.depositAsset = _position.soldAsset;
if (_vars.takeDepositFromWallet) {
TokenTransfersLibrary.doTransferFromTo(
_vars.depositData.depositAsset,
msg.sender,
data.depositReceiver,
_vars.depositData.depositAmount
);
} else {
_pmParams.traderBalanceVault.useTraderAssets(
ITraderBalanceVault.LockAssetParams({
trader: _position.trader,
depositReceiver: data.depositReceiver,
depositAsset: _vars.depositData.depositAsset,
depositAmount: _vars.depositData.depositAmount,
openType: _vars.byOrder
? ITraderBalanceVault.OpenType.OPEN_BY_ORDER
: ITraderBalanceVault.OpenType.OPEN
})
);
}
if (!data.isSpot) {
_position.bucket.increaseDebt(_position.trader, _vars.borrowedAmount, data.dexAdapter);
// @note You need to write index only after opening a position in bucket.
// Since when opening position in the bucket, index becomes relevant (containing accumulated profit)
_position.openBorrowIndex = _position.bucket.variableBorrowIndex();
_position.scaledDebtAmount = _vars.borrowedAmount.rdiv(_position.openBorrowIndex);
}
if (_vars.isThirdAsset) {
data.depositInPositionAsset = PrimexPricingLibrary.megaSwap(
PrimexPricingLibrary.MegaSwapParams({
tokenA: _vars.depositData.depositAsset,
tokenB: _position.positionAsset,
amountTokenA: _vars.depositData.depositAmount,
megaRoutes: _vars.depositInThirdAssetMegaRoutes,
receiver: address(this),
deadline: _vars.deadline
}),
_pmParams.oracleTolerableLimitForThirdAsset,
data.dexAdapter,
address(_pmParams.priceOracle),
true,
_vars.thirdAssetOracleData
);
_position.positionAmount += data.depositInPositionAsset;
} else {
_require(
_vars.depositInThirdAssetMegaRoutes.length == 0,
Errors.DEPOSIT_IN_THIRD_ASSET_ROUTES_LENGTH_SHOULD_BE_0.selector
);
}
data.borrowedAmountInPositionAsset = PrimexPricingLibrary.megaSwap(
PrimexPricingLibrary.MegaSwapParams({
tokenA: _position.soldAsset,
tokenB: _position.positionAsset,
amountTokenA: data.isSpot ? _vars.depositData.depositAmount : data.amountToTransfer,
megaRoutes: _vars.firstAssetMegaRoutes,
receiver: address(this),
deadline: _vars.deadline
}),
_pmParams.oracleTolerableLimit,
data.dexAdapter,
address(_pmParams.priceOracle),
_vars.needOracleTolerableLimitCheck,
_vars.firstAssetOracleData
);
_position.positionAmount += data.borrowedAmountInPositionAsset;
OpenPositionEventData memory posEventData;
if (_vars.byOrder) {
posEventData.feeRateType = data.isSpot
? IPrimexDNSStorageV3.FeeRateType.SpotLimitOrderExecuted
: IPrimexDNSStorageV3.FeeRateType.MarginLimitOrderExecuted;
(posEventData.feeInPositionAsset, posEventData.feeInPmx) = PrimexPricingLibrary.payProtocolFee(
PrimexPricingLibrary.ProtocolFeeParams({
feeToken: decodeFeeTokenAddress(_position.extraParams),
trader: _position.trader,
priceOracle: address(_pmParams.priceOracle),
feeRateType: posEventData.feeRateType,
traderBalanceVault: _pmParams.traderBalanceVault,
swapManager: address(0),
keeperRewardDistributor: _pmParams.keeperRewardDistributor,
primexDNS: _pmParams.primexDNS,
paymentAsset: _position.positionAsset,
paymentAmount: _position.positionAmount,
gasSpent: _pmParams.initialGasLeft - gasleft(),
isFeeProhibitedInPmx: false,
pmxPaymentAssetOracleData: _vars.pmxPositionAssetOracleData,
nativePaymentAssetOracleData: _vars.nativePositionAssetOracleData
})
);
_position.positionAmount -= posEventData.feeInPositionAsset;
}
_require(_position.positionAmount >= _vars.amountOutMin, Errors.SLIPPAGE_TOLERANCE_EXCEEDED.selector);
data.leverage = WadRayMath.WAD;
if (!data.isSpot) {
_require(_pmParams.maxPositionSize >= _position.positionAmount, Errors.POSITION_SIZE_EXCEEDED.selector);
if (_vars.depositData.depositAsset == _position.soldAsset) {
data.positionAmountAfterFeeInSoldAsset =
(data.amountToTransfer * _position.positionAmount) /
(_position.positionAmount + posEventData.feeInPositionAsset);
_require(
data.positionAmountAfterFeeInSoldAsset > _vars.borrowedAmount,
Errors.INSUFFICIENT_DEPOSIT.selector
);
data.leverage = data.positionAmountAfterFeeInSoldAsset.wdiv(
data.positionAmountAfterFeeInSoldAsset - _vars.borrowedAmount
);
} else {
_require(
data.depositInPositionAsset > posEventData.feeInPositionAsset,
Errors.INSUFFICIENT_DEPOSIT.selector
);
data.leverage = _position.positionAmount.wdiv(
data.depositInPositionAsset - posEventData.feeInPositionAsset
);
}
// to avoid stack to deep
data.positionAsset = _position.positionAsset;
data.positionAmount = _position.positionAmount;
// protocolFee calculated in position Asset
_require(
data.leverage <=
_position.bucket.maxAssetLeverage(
_position.positionAsset,
PrimexPricingLibrary
.calculateFeeInPaymentAsset(
PrimexPricingLibrary.CalculateFeeInPaymentAssetParams({
primexDNS: _pmParams.primexDNS,
priceOracle: address(_pmParams.priceOracle),
feeRateType: IPrimexDNSStorageV3.FeeRateType.MarginPositionClosedByKeeper,
paymentAsset: data.positionAsset,
paymentAmount: data.positionAmount,
keeperRewardDistributor: _pmParams.keeperRewardDistributor,
gasSpent: 0,
isFeeProhibitedInPmx: true,
nativePaymentAssetOracleData: _vars.nativePositionAssetOracleData
})
)
.wdiv(data.positionAmount)
),
Errors.INSUFFICIENT_DEPOSIT.selector
);
if (_vars.byOrder) {
uint256 leverageTolerance = _pmParams.primexDNS.leverageTolerance();
_require(
data.leverage <= _vars.orderLeverage.wmul(WadRayMath.WAD + leverageTolerance) &&
data.leverage >= _vars.orderLeverage.wmul(WadRayMath.WAD - leverageTolerance),
Errors.LEVERAGE_TOLERANCE_EXCEEDED.selector
);
}
}
if (!_vars.byOrder) {
_vars.depositData.leverage = data.leverage;
}
data.multiplierBorrowedAsset = 10 ** (18 - IERC20Metadata(_position.soldAsset).decimals());
data.multiplierPositionAsset = 10 ** (18 - IERC20Metadata(_position.positionAsset).decimals());
posEventData.entryPrice =
((_vars.borrowedAmount + _position.depositAmountInSoldAsset) * data.multiplierBorrowedAsset).wdiv(
(_position.positionAmount + posEventData.feeInPositionAsset) * data.multiplierPositionAsset
) /
data.multiplierBorrowedAsset;
posEventData.leverage = _vars.depositData.leverage;
return (_position, posEventData);
}
/**
* @dev Retrieves the debt amount for a given position.
* @param position The Position struct representing the position to get the debt amount for.
* @return The debt amount in debtTokens.
*/
function getDebt(Position memory position) public view returns (uint256) {
if (position.scaledDebtAmount == 0) return 0;
return position.scaledDebtAmount.rmul(position.bucket.getNormalizedVariableDebt());
}
/**
* @dev Calculates the health of a position.
* @dev health = ((1 - securityBuffer) * (1 - oracleTolerableLimit) * (1 - priceDrop) * positionAmountInBorrowedAsset) /
(feeBuffer * debt)
* @param position The position object containing relevant information.
* @param priceOracle The price oracle contract used for obtaining asset prices.
* @param pairPriceDrop The priceDrop in WAD format of the asset pair.
* @param securityBuffer The security buffer in WAD format for the position.
* @param oracleTolerableLimit The tolerable limit in WAD format for the price oracle.
* @return The health value in WAD format of the position.
*/
function health(
Position memory position,
IPriceOracleV2 priceOracle,
uint256 pairPriceDrop,
uint256 securityBuffer,
uint256 oracleTolerableLimit,
uint256 feeInPaymentAsset,
bytes memory positionSoldAssetOracleData
) public returns (uint256) {
if (position.scaledDebtAmount == 0) return WadRayMath.WAD;
return
health(
PrimexPricingLibrary.getOracleAmountsOut(
position.positionAsset,
position.soldAsset,
position.positionAmount,
address(priceOracle),
positionSoldAssetOracleData
) - feeInPaymentAsset,
pairPriceDrop,
securityBuffer,
oracleTolerableLimit,
getDebt(position),
position.bucket.feeBuffer()
);
}
/**
* @dev Creates a new position based on the given parameters.
* @param _params The input parameters for creating the position.
* @param primexDNS The address of the PrimexDNS contract.
* @param priceOracle The address of the PriceOracle contract.
* @return position The created Position struct.
* @return vars The OpenPositionVars struct.
*/
function createPosition(
OpenPositionParams calldata _params,
IPrimexDNSV3 primexDNS,
IPriceOracleV2 priceOracle
) public returns (Position memory, OpenPositionVars memory) {
OpenPositionVars memory vars = OpenPositionVars({
firstAssetMegaRoutes: _params.firstAssetMegaRoutes,
depositInThirdAssetMegaRoutes: _params.marginParams.depositInThirdAssetMegaRoutes,
depositData: PrimexPricingLibrary.DepositData({
depositAsset: address(0),
depositAmount: _params.depositAmount,
leverage: 0
}),
borrowedAmount: _params.marginParams.borrowedAmount,
amountOutMin: _params.amountOutMin,
deadline: _params.deadline,
isSpot: _params.marginParams.borrowedAmount == 0,
isThirdAsset: false,
takeDepositFromWallet: _params.takeDepositFromWallet,
byOrder: false,
orderLeverage: 0,
sender: address(0),
closeConditions: _params.closeConditions,
needOracleTolerableLimitCheck: _params.marginParams.borrowedAmount > 0,
firstAssetOracleData: _params.firstAssetOracleData,
thirdAssetOracleData: _params.thirdAssetOracleData,
positionUsdOracleData: _params.positionUsdOracleData,
nativePositionAssetOracleData: _params.nativePositionAssetOracleData,
pmxPositionAssetOracleData: _params.pmxPositionAssetOracleData,
nativeSoldAssetOracleData: _params.nativeSoldAssetOracleData
});
PositionLibrary.Position memory position = PositionLibrary.Position({
id: 0,
scaledDebtAmount: 0,
bucket: IBucketV3(address(0)),
soldAsset: address(0),
depositAmountInSoldAsset: 0,
positionAsset: _params.positionAsset,
positionAmount: 0,
trader: msg.sender,
openBorrowIndex: 0,
createdAt: block.timestamp,
updatedConditionsAt: block.timestamp,
extraParams: ""
});
if (vars.isSpot) {
_require(_params.depositAsset != _params.positionAsset, Errors.SHOULD_BE_DIFFERENT_ASSETS_IN_SPOT.selector);
_require(bytes(_params.marginParams.bucket).length == 0, Errors.BUCKET_SHOULD_BE_UNDEFINED.selector);
position.soldAsset = _params.depositAsset;
position.depositAmountInSoldAsset = vars.depositData.depositAmount;
vars.depositData.leverage = WadRayMath.WAD;
} else {
position.bucket = IBucketV3(primexDNS.getBucketAddress(_params.marginParams.bucket));
position.soldAsset = address(position.bucket.borrowedAsset());
vars.depositData.depositAsset = _params.depositAsset;
(, bool tokenAllowed) = position.bucket.allowedAssets(_params.positionAsset);
_require(tokenAllowed, Errors.TOKEN_NOT_SUPPORTED.selector);
vars.isThirdAsset =
_params.depositAsset != position.soldAsset &&
_params.depositAsset != _params.positionAsset;
position.depositAmountInSoldAsset = PrimexPricingLibrary.getOracleAmountsOut(
_params.depositAsset,
position.soldAsset,
_params.depositAmount,
address(priceOracle),
_params.depositSoldAssetOracleData
);
}
address feeToken = _params.isProtocolFeeInPmx ? primexDNS.pmx() : position.soldAsset;
position.extraParams = abi.encode(feeToken);
return (position, vars);
}
/**
* @notice Creates a position based on the provided order parameters.
* @dev This function calculates and returns a Position and OpenPositionVars struct.
* @param _params The OpenPositionByOrderParams struct containing the order parameters.
* @param priceOracle The price oracle contract used for retrieving asset prices.
* @return position The Position struct representing the created position.
* @return vars The OpenPositionVars struct containing additional variables related to the position.
*/
function createPositionByOrder(
LimitOrderLibrary.OpenPositionByOrderParams calldata _params,
IPriceOracleV2 priceOracle,
IPrimexDNSV3 primexDNS
) public returns (Position memory, OpenPositionVars memory) {
OpenPositionVars memory vars = OpenPositionVars({
firstAssetMegaRoutes: _params.firstAssetMegaRoutes,
depositInThirdAssetMegaRoutes: _params.depositInThirdAssetMegaRoutes,
depositData: PrimexPricingLibrary.DepositData({
depositAsset: address(0),
depositAmount: _params.order.depositAmount,
leverage: _params.order.leverage
}),
borrowedAmount: _params.borrowedAmount,
amountOutMin: 0,
orderLeverage: _params.order.leverage,
deadline: _params.order.deadline,
isSpot: _params.order.leverage == WadRayMath.WAD,
isThirdAsset: false,
takeDepositFromWallet: false,
byOrder: true,
sender: _params.sender,
closeConditions: _params.closeConditions,
needOracleTolerableLimitCheck: address(_params.order.bucket) != address(0),
firstAssetOracleData: _params.firstAssetOracleData,
thirdAssetOracleData: _params.thirdAssetOracleData,
positionUsdOracleData: _params.positionUsdOracleData,
nativePositionAssetOracleData: _params.nativePositionAssetOracleData,
pmxPositionAssetOracleData: _params.pmxPositionAssetOracleData,
nativeSoldAssetOracleData: _params.nativeSoldAssetOracleData
});
Position memory position = Position({
id: 0,
scaledDebtAmount: 0,
bucket: IBucketV3(address(0)),
soldAsset: address(0),
depositAmountInSoldAsset: 0,
positionAsset: _params.order.positionAsset,
positionAmount: 0,
trader: _params.order.trader,
openBorrowIndex: 0,
createdAt: block.timestamp,
updatedConditionsAt: block.timestamp,
extraParams: ""
});
if (vars.isSpot) {
position.soldAsset = _params.order.depositAsset;
position.depositAmountInSoldAsset = vars.depositData.depositAmount;
} else {
position.bucket = _params.order.bucket;
position.soldAsset = address(position.bucket.borrowedAsset());
vars.depositData.depositAsset = _params.order.depositAsset;
vars.isThirdAsset =
_params.order.depositAsset != position.soldAsset &&
_params.order.depositAsset != _params.order.positionAsset;
position.depositAmountInSoldAsset = PrimexPricingLibrary.getOracleAmountsOut(
_params.order.depositAsset,
position.soldAsset,
_params.order.depositAmount,
address(priceOracle),
_params.depositSoldAssetOracleData
);
if (_params.order.depositAsset == position.soldAsset) {
_require(
vars.borrowedAmount == _params.order.depositAmount.wmul(_params.order.leverage - WadRayMath.WAD),
Errors.INCORRECT_BORROWED_AMOUNT.selector
);
}
}
address feeToken = _params.order.feeToken == primexDNS.pmx() ? primexDNS.pmx() : position.soldAsset;
position.extraParams = abi.encode(feeToken);
return (position, vars);
}
/**
* @notice Decodes a fee token address from the provided encoded data.
* @param data The encoded data containing the fee token address.
* @return The decoded fee token address.
*/
function decodeFeeTokenAddress(bytes memory data) public pure returns (address) {
// Check if there is data in the bytes extraParams
if (data.length == 0) {
// If there is no data, return address(0)
return address(0);
} else {
// Decode the data into an address and return the result
return abi.decode(data, (address));
}
}
/**
* @notice Calculates the health score for a position.
* @param positionAmountInBorrowedAsset The position size in borrow asset.
* @param pairPriceDrop The priceDrop in WAD format of the pair.
* @param securityBuffer The security buffer in WAD format.
* @param oracleTolerableLimit The tolerable limit in WAD format for the oracle.
* @param positionDebt The debt of the position.
* @param feeBuffer The buffer for fees.
* @return The health score of the position.
*/
function health(
uint256 positionAmountInBorrowedAsset,
uint256 pairPriceDrop,
uint256 securityBuffer,
uint256 oracleTolerableLimit,
uint256 positionDebt,
uint256 feeBuffer
) public pure returns (uint256) {
return
(
(WadRayMath.WAD - securityBuffer)
.wmul(WadRayMath.WAD - oracleTolerableLimit)
.wmul(WadRayMath.WAD - pairPriceDrop)
.wmul(positionAmountInBorrowedAsset)
).wdiv(feeBuffer.wmul(positionDebt));
}
}// (c) 2024 Primex.finance
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.26;
import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import {AggregatorV3Interface} from "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol";
import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
import {BytesLib} from "./utils/BytesLib.sol";
import {WadRayMath} from "./utils/WadRayMath.sol";
import {NATIVE_CURRENCY, USD, USD_MULTIPLIER, ARB_NITRO_ORACLE, OVM_GASPRICEORACLE, GAS_FOR_BYTE, TRANSACTION_METADATA_BYTES} from "../Constants.sol";
import {IDexAdapter} from "../interfaces/IDexAdapter.sol";
import {IPriceOracle} from "../PriceOracle/IPriceOracle.sol";
import {IKeeperRewardDistributorStorage, IKeeperRewardDistributorV3} from "../KeeperRewardDistributor/IKeeperRewardDistributor.sol";
import {IPrimexDNSV3, IPrimexDNSStorageV3} from "../PrimexDNS/IPrimexDNS.sol";
import {IBucketV3} from "../Bucket/IBucket.sol";
import {IPositionManagerV2} from "../PositionManager/IPositionManager.sol";
import {ITraderBalanceVault} from "../TraderBalanceVault/ITraderBalanceVault.sol";
import {TokenTransfersLibrary} from "./TokenTransfersLibrary.sol";
import {IPriceOracleStorageV2} from "../PriceOracle/IPriceOracleStorage.sol";
import {IPriceOracleV2} from "../PriceOracle/IPriceOracle.sol";
import "./Errors.sol";
library PrimexPricingLibrary {
using WadRayMath for uint256;
using BytesLib for bytes;
/**
* @param dexName The name of the DEX.
* @param shares the share that will be allocated from the total amount for the route
* @param payload payload data encoded in bytes
*/
struct Path {
string dexName;
uint256 shares;
bytes payload;
}
/**
* @param to the destination token of the route
* @param paths path array through which the swap will be made up to the destination token this the route
*/
struct Route {
address to;
Path[] paths;
}
/**
* @param shares the share that will be allocated from the total amount for this MegaRoute
* @param routes array of routes through which the swap will be made up to TokenB
*/
struct MegaRoute {
uint256 shares;
Route[] routes;
}
struct MegaSwapParams {
address tokenA;
address tokenB;
uint256 amountTokenA;
MegaRoute[] megaRoutes;
address receiver;
uint256 deadline;
}
struct AmountParams {
address tokenA;
address tokenB;
uint256 amount;
MegaRoute[] megaRoutes;
address dexAdapter;
address primexDNS;
}
struct DepositData {
address depositAsset;
uint256 depositAmount;
uint256 leverage;
}
/**
* @param feeToken An asset in which the fee will be paid.
* @param trader The trader address
* @param priceOracle PriceOracle contract address
* @param orderType Type of possible order in Primex protocol
* @param traderBalanceVault TraderBalanceVault contract address
* @param primexDNS PrimexDNS contract address
*/
struct ProtocolFeeParams {
address feeToken;
address trader;
address priceOracle;
IPrimexDNSStorageV3.FeeRateType feeRateType;
ITraderBalanceVault traderBalanceVault;
address swapManager;
address keeperRewardDistributor;
IPrimexDNSV3 primexDNS;
address paymentAsset;
uint256 paymentAmount;
uint256 gasSpent;
bool isFeeProhibitedInPmx;
bytes pmxPaymentAssetOracleData;
bytes nativePaymentAssetOracleData;
}
struct ProtocolFeeParamsBatchClose {
uint256 numberOfPositions;
address[] feeTokens;
address[] traders;
uint256[] paymentAmounts;
address paymentAsset;
address priceOracle;
IPrimexDNSStorageV3.FeeRateType feeRateType;
ITraderBalanceVault traderBalanceVault;
address keeperRewardDistributor;
IPrimexDNSV3 primexDNS;
uint256 estimatedGasAmount;
bool isFeeProhibitedInPmx;
uint256 estimatedBaseLength;
bytes nativePaymentAssetOracleData;
bytes pmxPaymentAssetOracleData;
}
struct CalculateFeeInPaymentAssetParams {
IPrimexDNSV3 primexDNS;
address priceOracle;
IPrimexDNSStorageV3.FeeRateType feeRateType;
address paymentAsset;
uint256 paymentAmount;
address keeperRewardDistributor;
uint256 gasSpent;
bool isFeeProhibitedInPmx;
bytes nativePaymentAssetOracleData;
}
struct MinProtocolFeeParams {
uint256 restrictedGasSpent;
address paymentAsset;
address priceOracle;
IKeeperRewardDistributorV3 keeperRewardDistributor;
IPrimexDNSV3 primexDNS;
bool isFeeProhibitedInPmx;
IPrimexDNSStorageV3.FeeRateType feeRateType;
bytes nativePaymentAssetOracleData;
}
/**
* The struct for payProtocolFee function
*/
struct ProtocolFeeVars {
address pmx;
address treasury;
uint256 feeInPaymentAssetWithDiscount;
uint256 pmxTraderBalance;
uint256 pmxTraderBalanceInPaymentAsset;
uint256 pmxDiscountMultiplier;
}
/**
* The struct for calculateFeeInPaymentAssetVars function
*/
struct FeeInPaymentAssetVars {
uint256 protocolFeeRate;
uint256 maxProtocolFee;
uint256 feeInPaymentAsset;
uint256 maxProtocolFeeInPaymentAsset;
uint256 minProtocolFeeInPaymentAsset;
}
/**
* The struct for minProtocolFee function
*/
struct MinProtocolFeeVars {
uint256 maxGasAmount;
uint256 restrictedGasPrice;
uint256 l1CostWei;
uint256 liquidationGasAmount;
uint256 protocolFeeCoefficient;
uint256 additionalGasSpent;
uint256 minProtocolFeeInNativeAsset;
uint256 totalGasSpent;
uint256 baseLength;
uint256 optimisticGasCoefficient;
IPrimexDNSStorageV3.CallingMethod callingMethod;
IKeeperRewardDistributorStorage.PaymentModel paymentModel;
}
/**
* The struct for calculateFeeInPaymentAssetBatchClose function
*/
struct CalculateFeeInPaymentAssetBatchCloseVars {
uint256[] feeInPaymentAsset;
uint256 protocolFeeRate;
uint256 maxProtocolFee;
uint256 maxProtocolFeeInPaymentAsset;
uint256 minProtocolFeeInPaymentAsset;
}
/**
* The struct for calculateRestrictedGasPrice function
*/
struct RestrictedGasPriceVars {
int256 oracleGasPrice;
uint256 maxGasPrice;
uint256 defaultMaxGasPrice;
uint256 oracleGasPriceTolerance;
}
/**
* The struct for getLiquidationPrice and getLiquidationPriceByOrder functions
*/
struct LiquidationPriceData {
IBucketV3 bucket;
IPositionManagerV2 positionManager;
IPriceOracleV2 priceOracle;
IERC20Metadata borrowedAsset;
}
event Withdraw(
address indexed withdrawer,
address borrowAssetReceiver,
address borrowedAsset,
uint256 amount,
uint256 timestamp
);
/**
* @notice Encodes the given parameters into a bytes array based on the specified DEX type.
* @param path The token path for the swap.
* @param dexRouter The address of the DEX router.
* @param ancillaryData Additional data required for certain DEX types.
* @param dexAdapter The address of the DEX adapter.
* @param isAmountToBuy A flag indicating whether it is the path for the swap with fixed amountIn or amountOut.
* Swap with fixed amountIn, if true.
* @return The encoded bytes array.
*/
function encodePath(
address[] memory path,
address dexRouter,
bytes32 ancillaryData,
address payable dexAdapter,
bool isAmountToBuy
) external view returns (bytes memory) {
IDexAdapter.DexType type_ = IDexAdapter(dexAdapter).dexType(dexRouter);
if (type_ == IDexAdapter.DexType.UniswapV2 || type_ == IDexAdapter.DexType.Meshswap) {
return abi.encode(path);
}
if (type_ == IDexAdapter.DexType.UniswapV3) {
if (isAmountToBuy)
return bytes.concat(bytes20(path[1]), bytes3(uint24(uint256(ancillaryData))), bytes20(path[0]));
return bytes.concat(bytes20(path[0]), bytes3(uint24(uint256(ancillaryData))), bytes20(path[1]));
}
if (type_ == IDexAdapter.DexType.AlgebraV3) {
if (isAmountToBuy) return bytes.concat(bytes20(path[1]), bytes20(path[0]));
return bytes.concat(bytes20(path[0]), bytes20(path[1]));
}
if (type_ == IDexAdapter.DexType.Curve) {
address[] memory pools = new address[](1);
pools[0] = address(uint160(uint256(ancillaryData)));
return abi.encode(path, pools);
}
if (type_ == IDexAdapter.DexType.Balancer) {
int256[] memory limits = new int256[](2);
limits[0] = type(int256).max;
bytes32[] memory pools = new bytes32[](1);
pools[0] = ancillaryData;
return abi.encode(path, pools, limits);
}
_revert(Errors.UNKNOWN_DEX_TYPE.selector);
}
/**
* @notice Calculates the amount of deposit assets in borrowed assets.
* @param _params The parameters for the calculation.
* @param _isThirdAsset A flag indicating if deposit is in a third asset.
* @param _priceOracle The address of the price oracle.
* @return The amount of deposit assets is measured in borrowed assets.
*/
function getDepositAmountInBorrowed(
IDexAdapter.AmountParams calldata _params,
bool _isThirdAsset,
address payable _dexAdapter,
address _priceOracle,
bytes calldata _oracleData
) public returns (uint256) {
_require(
IERC165(_priceOracle).supportsInterface(type(IPriceOracleV2).interfaceId),
Errors.ADDRESS_NOT_SUPPORTED.selector
);
if (_params.tokenA == _params.tokenB) {
_require(_params.megaRoutes.length == 0, Errors.DEPOSITED_TO_BORROWED_ROUTES_LENGTH_SHOULD_BE_0.selector);
return _params.amount;
}
uint256 depositAmountInBorrowed = IDexAdapter(_dexAdapter).getAmountOutByMegaRoutes(_params);
if (_isThirdAsset) {
uint256 oracleDepositAmountOut = getOracleAmountsOut(
_params.tokenA,
_params.tokenB,
_params.amount,
_priceOracle,
_oracleData
);
if (depositAmountInBorrowed > oracleDepositAmountOut) depositAmountInBorrowed = oracleDepositAmountOut;
}
return depositAmountInBorrowed;
}
/**
* @notice Performs a multi-hop swap transaction using the specified parameters.
* @dev This function executes a series of token swaps on different DEXs based on the provided routes.
* @param _params The struct containing all the necessary parameters for the multi-hop swap.
* @param _maximumOracleTolerableLimit The maximum tolerable limit in WAD format (1 WAD = 100%)
* for the price difference between DEX and the oracle.
* @param _dexAdapter The address of the Dex adapter contract.
* @param _priceOracle The address of the price oracle contract.
* @param _needOracleTolerableLimitCheck Flag indicating whether to perform an oracle tolerable limit check.
* @return The final balance of the _params.tokenB in the receiver's address after the multi-hop swap.
*/
function megaSwap(
MegaSwapParams calldata _params,
uint256 _maximumOracleTolerableLimit,
address payable _dexAdapter,
address _priceOracle,
bool _needOracleTolerableLimitCheck,
bytes calldata _oracleData
) public returns (uint256) {
uint256 balance = IERC20Metadata(_params.tokenB).balanceOf(_params.receiver);
IDexAdapter(_dexAdapter).performMegaRoutesSwap(_params);
balance = IERC20Metadata(_params.tokenB).balanceOf(_params.receiver) - balance;
if (_needOracleTolerableLimitCheck) {
_require(
balance >=
getOracleAmountsOut(_params.tokenA, _params.tokenB, _params.amountTokenA, _priceOracle, _oracleData)
.wmul(WadRayMath.WAD - _maximumOracleTolerableLimit),
Errors.DIFFERENT_PRICE_DEX_AND_ORACLE.selector
);
}
return balance;
}
/**
* @notice Pays the protocol fee.
* @dev This function transfers the protocol fee from the trader to the protocol treasury.
* @param params The parameters for paying the protocol fee.
* @return feeInPaymentAsset The amount of the protocol fee in a payment asset
* (position asset for the limit order execution, sold asset when the position is closed.)
* @return feeInPmx The amount of the protocol fee in pmx asset paid.
*/
function payProtocolFee(
ProtocolFeeParams memory params
) public returns (uint256 feeInPaymentAsset, uint256 feeInPmx) {
// This is done to ensure that after upgrading the contracts, positions that have already been opened
// and had fees paid for them will not incur additional fees upon closure
if (params.feeToken == address(0)) {
return (0, 0);
}
ProtocolFeeVars memory vars;
(vars.pmx, vars.treasury, , , vars.pmxDiscountMultiplier) = params.primexDNS.getPrimexDNSParams(
params.feeRateType
);
feeInPaymentAsset = calculateFeeInPaymentAsset(
CalculateFeeInPaymentAssetParams({
primexDNS: params.primexDNS,
priceOracle: params.priceOracle,
feeRateType: params.feeRateType,
paymentAsset: params.paymentAsset,
paymentAmount: params.paymentAmount,
keeperRewardDistributor: params.keeperRewardDistributor,
gasSpent: params.gasSpent,
isFeeProhibitedInPmx: params.isFeeProhibitedInPmx,
nativePaymentAssetOracleData: params.nativePaymentAssetOracleData
})
);
(vars.pmxTraderBalance, ) = params.traderBalanceVault.balances(params.trader, vars.pmx);
if (params.feeToken == vars.pmx && vars.pmxTraderBalance > 0 && !params.isFeeProhibitedInPmx) {
// pmx => payment asset data
uint256 pmxTraderBalanceInPaymentAsset = getOracleAmountsOut(
vars.pmx,
params.paymentAsset,
vars.pmxTraderBalance,
params.priceOracle,
params.pmxPaymentAssetOracleData
);
uint256 feeInPaymentAssetWithDiscount = feeInPaymentAsset.wmul(vars.pmxDiscountMultiplier);
feeInPmx = (feeInPaymentAssetWithDiscount * vars.pmxTraderBalance) / pmxTraderBalanceInPaymentAsset;
if (pmxTraderBalanceInPaymentAsset >= feeInPaymentAssetWithDiscount) {
feeInPaymentAsset = 0;
params.traderBalanceVault.withdrawFrom(params.trader, vars.treasury, vars.pmx, feeInPmx, false);
} else {
feeInPmx = vars.pmxTraderBalance;
feeInPaymentAsset -= pmxTraderBalanceInPaymentAsset.wdiv(vars.pmxDiscountMultiplier);
params.traderBalanceVault.withdrawFrom(
params.trader,
vars.treasury,
vars.pmx,
vars.pmxTraderBalance,
false
);
TokenTransfersLibrary.doTransferOut(params.paymentAsset, vars.treasury, feeInPaymentAsset);
}
} else {
TokenTransfersLibrary.doTransferOut(params.paymentAsset, vars.treasury, feeInPaymentAsset);
}
}
/**
* @notice Calculate and return protocol fee
* @return The amount of the protocol fee in '_feeToken' which needs to be paid according to the specified deposit parameters.
*/
function calculateFeeInPaymentAsset(CalculateFeeInPaymentAssetParams memory params) public returns (uint256) {
FeeInPaymentAssetVars memory vars;
(, , vars.protocolFeeRate, vars.maxProtocolFee, ) = params.primexDNS.getPrimexDNSParams(params.feeRateType);
// Calculate protocol fee in position asset
vars.feeInPaymentAsset = params.paymentAmount.wmul(vars.protocolFeeRate);
// Calculate max protocol fee in position asset
vars.maxProtocolFeeInPaymentAsset = vars.maxProtocolFee == type(uint256).max
? type(uint256).max
: getOracleAmountsOut(
NATIVE_CURRENCY,
params.paymentAsset,
vars.maxProtocolFee,
params.priceOracle,
params.nativePaymentAssetOracleData
);
// The minProtocolFee is applied only if the order/position is processed by Keepers
if (
params.feeRateType == IPrimexDNSStorageV3.FeeRateType.MarginPositionClosedByTrader ||
params.feeRateType == IPrimexDNSStorageV3.FeeRateType.SpotPositionClosedByTrader ||
params.feeRateType == IPrimexDNSStorageV3.FeeRateType.SwapMarketOrder
) {
vars.feeInPaymentAsset = min(vars.feeInPaymentAsset, vars.maxProtocolFeeInPaymentAsset);
} else {
vars.minProtocolFeeInPaymentAsset = minProtocolFee(
MinProtocolFeeParams({
restrictedGasSpent: params.gasSpent,
paymentAsset: params.paymentAsset,
priceOracle: params.priceOracle,
keeperRewardDistributor: IKeeperRewardDistributorV3(params.keeperRewardDistributor),
primexDNS: params.primexDNS,
isFeeProhibitedInPmx: params.isFeeProhibitedInPmx,
feeRateType: params.feeRateType,
nativePaymentAssetOracleData: params.nativePaymentAssetOracleData
})
);
_require(
vars.minProtocolFeeInPaymentAsset < params.paymentAmount,
Errors.MIN_PROTOCOL_FEE_IS_GREATER_THAN_PAYMENT_AMOUNT.selector
);
vars.feeInPaymentAsset = min(
max(vars.feeInPaymentAsset, vars.minProtocolFeeInPaymentAsset),
vars.maxProtocolFeeInPaymentAsset
);
}
return vars.feeInPaymentAsset;
}
function payProtocolFeeBatchClose(
ProtocolFeeParamsBatchClose calldata params
) public returns (uint256[] memory, uint256[] memory) {
ProtocolFeeVars memory vars;
uint256[] memory feeInPaymentAsset = new uint256[](params.numberOfPositions);
uint256[] memory feeInPmx = new uint256[](params.numberOfPositions);
(vars.pmx, vars.treasury, , , vars.pmxDiscountMultiplier) = params.primexDNS.getPrimexDNSParams(
params.feeRateType
);
feeInPaymentAsset = calculateFeeInPaymentAssetBatchClose(
params.numberOfPositions,
params.primexDNS,
params.priceOracle,
params.feeRateType,
params.paymentAsset,
params.paymentAmounts,
params.keeperRewardDistributor,
params.estimatedGasAmount,
params.estimatedBaseLength,
params.nativePaymentAssetOracleData
);
for (uint256 i; i < params.numberOfPositions; i++) {
// This is done to ensure that after upgrading the contracts, positions that have already been opened
// and had fees paid for them will not incur additional fees upon closure
if (params.feeTokens[i] == address(0)) {
feeInPaymentAsset[i] = 0;
feeInPmx[i] = 0;
continue;
}
(vars.pmxTraderBalance, ) = params.traderBalanceVault.balances(params.traders[i], vars.pmx);
if (!params.isFeeProhibitedInPmx && params.feeTokens[i] == vars.pmx && vars.pmxTraderBalance > 0) {
vars.pmxTraderBalanceInPaymentAsset = getOracleAmountsOut(
vars.pmx,
params.paymentAsset,
vars.pmxTraderBalance,
params.priceOracle,
params.pmxPaymentAssetOracleData
);
vars.feeInPaymentAssetWithDiscount = feeInPaymentAsset[i].wmul(vars.pmxDiscountMultiplier);
feeInPmx[i] =
(vars.feeInPaymentAssetWithDiscount * vars.pmxTraderBalance) /
vars.pmxTraderBalanceInPaymentAsset;
if (vars.pmxTraderBalanceInPaymentAsset >= vars.feeInPaymentAssetWithDiscount) {
feeInPaymentAsset[i] = 0;
params.traderBalanceVault.withdrawFrom(
params.traders[i],
vars.treasury,
vars.pmx,
feeInPmx[i],
false
);
} else {
feeInPmx[i] = vars.pmxTraderBalance;
feeInPaymentAsset[i] -= vars.pmxTraderBalanceInPaymentAsset.wdiv(vars.pmxDiscountMultiplier);
params.traderBalanceVault.withdrawFrom(
params.traders[i],
vars.treasury,
vars.pmx,
vars.pmxTraderBalance,
false
);
}
}
}
return (feeInPaymentAsset, feeInPmx);
}
/**
* @notice Calculate and return protocol fee
* @return The amount of the protocol fee in '_feeToken' which needs to be paid according to the specified deposit parameters.
*/
function calculateFeeInPaymentAssetBatchClose(
uint256 numberOfPositions,
IPrimexDNSV3 primexDNS,
address priceOracle,
IPrimexDNSStorageV3.FeeRateType feeRateType,
address paymentAsset,
uint256[] memory paymentAmounts,
address keeperRewardDistributor,
uint256 estimatedGasAmount,
uint256 estimatedBaseLength,
bytes calldata _nativePaymentAssetOracleData
) public returns (uint256[] memory) {
CalculateFeeInPaymentAssetBatchCloseVars memory vars;
(, , vars.protocolFeeRate, vars.maxProtocolFee, ) = primexDNS.getPrimexDNSParams(feeRateType);
// Calculate max protocol fee in payment (sold) asset
vars.maxProtocolFeeInPaymentAsset = vars.maxProtocolFee == type(uint256).max
? type(uint256).max
: getOracleAmountsOut(
NATIVE_CURRENCY,
paymentAsset,
vars.maxProtocolFee,
priceOracle,
_nativePaymentAssetOracleData
);
vars.minProtocolFeeInPaymentAsset = minProtocolFeeCloseBatch(
paymentAsset,
priceOracle,
IKeeperRewardDistributorV3(keeperRewardDistributor),
estimatedGasAmount,
estimatedBaseLength,
primexDNS,
_nativePaymentAssetOracleData
);
vars.feeInPaymentAsset = new uint256[](numberOfPositions);
// Calculate protocol fee in position asset
for (uint256 i; i < numberOfPositions; i++) {
vars.feeInPaymentAsset[i] = paymentAmounts[i].wmul(vars.protocolFeeRate);
_require(
vars.minProtocolFeeInPaymentAsset < paymentAmounts[i],
Errors.MIN_PROTOCOL_FEE_IS_GREATER_THAN_PAYMENT_AMOUNT.selector
);
vars.feeInPaymentAsset[i] = min(
max(vars.feeInPaymentAsset[i], vars.minProtocolFeeInPaymentAsset),
vars.maxProtocolFeeInPaymentAsset
);
}
return vars.feeInPaymentAsset;
}
/**
* @notice Calculate minProtocolFee based on the gas price
*/
function minProtocolFee(MinProtocolFeeParams memory params) public returns (uint256 minProtocolFeeInPositionAsset) {
MinProtocolFeeVars memory vars;
(vars.restrictedGasPrice) = calculateRestrictedGasPrice(params.priceOracle, params.keeperRewardDistributor);
if (
params.feeRateType == IPrimexDNSStorageV3.FeeRateType.MarginPositionClosedByKeeper ||
params.feeRateType == IPrimexDNSStorageV3.FeeRateType.SpotPositionClosedByKeeper
) {
vars.callingMethod = IPrimexDNSStorageV3.CallingMethod.ClosePositionByCondition;
} else {
vars.callingMethod = IPrimexDNSStorageV3.CallingMethod.OpenPositionByOrder;
}
(
vars.liquidationGasAmount,
vars.protocolFeeCoefficient,
vars.additionalGasSpent,
vars.maxGasAmount,
vars.baseLength
) = params.primexDNS.getParamsForMinProtocolFee(vars.callingMethod);
vars.l1CostWei = _calculateL1CostWei(vars.baseLength, params.keeperRewardDistributor);
if (params.isFeeProhibitedInPmx) {
vars.minProtocolFeeInNativeAsset =
vars.liquidationGasAmount *
vars.restrictedGasPrice +
vars.l1CostWei +
vars.protocolFeeCoefficient;
} else {
if (vars.callingMethod == IPrimexDNSStorageV3.CallingMethod.ClosePositionByCondition) {
vars.minProtocolFeeInNativeAsset =
vars.maxGasAmount *
vars.restrictedGasPrice +
vars.l1CostWei +
vars.protocolFeeCoefficient;
} else {
vars.totalGasSpent = params.restrictedGasSpent + vars.additionalGasSpent;
vars.totalGasSpent = min(vars.totalGasSpent, vars.maxGasAmount);
vars.minProtocolFeeInNativeAsset =
vars.totalGasSpent *
vars.restrictedGasPrice +
vars.l1CostWei +
vars.protocolFeeCoefficient;
}
}
minProtocolFeeInPositionAsset = getOracleAmountsOut(
NATIVE_CURRENCY,
params.paymentAsset,
vars.minProtocolFeeInNativeAsset,
params.priceOracle,
params.nativePaymentAssetOracleData
);
}
/**
* @notice Calculate minProtocolFee based on the gas price in closeBatchPositions
*/
function minProtocolFeeCloseBatch(
address _paymentAsset,
address _priceOracle,
IKeeperRewardDistributorV3 _keeperRewardDistributor,
uint256 _estimatedGasAmount,
uint256 _estimatedBaseLength,
IPrimexDNSV3 primexDNS,
bytes calldata _nativePaymentAssetOracleData
) public returns (uint256 minProtocolFeeInPositionAsset) {
uint256 restrictedGasPrice = calculateRestrictedGasPrice(_priceOracle, _keeperRewardDistributor);
uint256 l1CostWei = _calculateL1CostWei(_estimatedBaseLength, _keeperRewardDistributor);
uint256 minProtocolFeeInNativeAsset = _estimatedGasAmount *
restrictedGasPrice +
l1CostWei +
primexDNS.protocolFeeCoefficient();
minProtocolFeeInPositionAsset = getOracleAmountsOut(
NATIVE_CURRENCY,
_paymentAsset,
minProtocolFeeInNativeAsset,
_priceOracle,
_nativePaymentAssetOracleData
);
}
/**
* @notice Calculate minPositionSize based on the gas price
*/
function minPositionSize(
address _priceOracle,
IKeeperRewardDistributorV3 _keeperRewardDistributor,
IPrimexDNSV3 _primexDNS,
IPrimexDNSStorageV3.TradingOrderType _tradingOrderType
) public view returns (uint256 minPositionSizeInNativeAsset) {
uint256 restrictedGasPrice = calculateRestrictedGasPrice(_priceOracle, _keeperRewardDistributor);
(
uint256 baseLength,
uint256 averageGasPerAction,
uint256 protocolFeeCoefficient,
uint256 gasPriceBuffer
) = _primexDNS.getParamsForMinPositionSize(_tradingOrderType);
uint256 l1CostWei = _calculateL1CostWei(baseLength, _keeperRewardDistributor);
minPositionSizeInNativeAsset = (averageGasPerAction * restrictedGasPrice + l1CostWei + protocolFeeCoefficient)
.wmul(gasPriceBuffer);
}
function calculateRestrictedGasPrice(
address _priceOracle,
IKeeperRewardDistributorV3 _keeperRewardDistributor
) internal view returns (uint256 restrictedGasPrice) {
RestrictedGasPriceVars memory vars;
restrictedGasPrice = tx.gasprice;
vars.oracleGasPrice = IPriceOracle(_priceOracle).getGasPrice();
(vars.oracleGasPriceTolerance, vars.defaultMaxGasPrice, , ) = _keeperRewardDistributor
.getGasCalculationParams();
vars.maxGasPrice = vars.oracleGasPrice > 0
? uint256(vars.oracleGasPrice).wmul(WadRayMath.WAD + vars.oracleGasPriceTolerance)
: vars.defaultMaxGasPrice;
if (restrictedGasPrice > vars.maxGasPrice || restrictedGasPrice == 0) {
restrictedGasPrice = vars.maxGasPrice;
}
}
function getOracleAmountsOut(
address _tokenA,
address _tokenB,
uint256 _amountAssetA,
address _priceOracle,
bytes memory _oracleData
) public returns (uint256) {
_require(
IERC165(_priceOracle).supportsInterface(type(IPriceOracleV2).interfaceId),
Errors.ADDRESS_NOT_SUPPORTED.selector
);
if (_tokenA == _tokenB) {
return _amountAssetA;
}
uint256 exchangeRate = IPriceOracleV2(_priceOracle).getExchangeRate(_tokenA, _tokenB, _oracleData);
return (_amountAssetA * _getAssetMultiplier(_tokenA)).wmul(exchangeRate) / _getAssetMultiplier(_tokenB);
}
/**
* @param _tokenA asset for sell
* @param _tokenB asset to buy
* @param _amountsAssetA An array of amounts of tokenA to sell
* @param _priceOracle PriceOracle contract address
* @return returns an array of amounts of `tokenB` by the `amountsAssetA` by the price of the oracle
*/
function getBatchOracleAmountsOut(
address _tokenA,
address _tokenB,
uint256[] memory _amountsAssetA,
address _priceOracle,
bytes calldata _oracleData
) public returns (uint256[] memory) {
_require(
IERC165(_priceOracle).supportsInterface(type(IPriceOracleV2).interfaceId),
Errors.ADDRESS_NOT_SUPPORTED.selector
);
if (_tokenA == _tokenB) {
return _amountsAssetA;
}
uint256[] memory amountsAssetB = new uint256[](_amountsAssetA.length);
uint256 exchangeRate = IPriceOracleV2(_priceOracle).getExchangeRate(_tokenA, _tokenB, _oracleData);
uint256 multiplier1 = 10 ** (18 - IERC20Metadata(_tokenA).decimals());
uint256 multiplier2 = 10 ** (18 - IERC20Metadata(_tokenB).decimals());
for (uint256 i; i < _amountsAssetA.length; i++) {
amountsAssetB[i] = (_amountsAssetA[i] * multiplier1).wmul(exchangeRate) / multiplier2;
}
return amountsAssetB;
}
/**
* @notice Calculates the liquidation price for a position.
* @dev liquidationPrice = (feeBuffer * debt) /
* ((1 - securityBuffer) * (1 - oracleTolerableLimit) * (1 - priceDrop) * positionAmount))
* @param _bucket The address of the related bucket.
* @param _positionAsset The address of the position asset.
* @param _positionAmount The size of the opened position.
* @param _positionDebt The debt amount in debtTokens associated with the position.
* @return The calculated liquidation price in borrowed asset.
*/
function getLiquidationPrice(
address _bucket,
address _positionAsset,
uint256 _positionAmount,
uint256 _positionDebt,
address _primexDNS
) public view returns (uint256) {
_require(_positionAsset != address(0), Errors.ADDRESS_NOT_SUPPORTED.selector);
LiquidationPriceData memory data;
data.bucket = IBucketV3(_bucket);
data.positionManager = data.bucket.positionManager();
data.borrowedAsset = data.bucket.borrowedAsset();
data.priceOracle = data.positionManager.priceOracle();
uint256 multiplier1 = 10 ** (18 - data.borrowedAsset.decimals());
uint256 denominator = (WadRayMath.WAD - data.positionManager.securityBuffer())
.wmul(
WadRayMath.WAD -
data.positionManager.getOracleTolerableLimit(_positionAsset, address(data.borrowedAsset))
)
.wmul(WadRayMath.WAD - data.priceOracle.getPairPriceDrop(_positionAsset, address(data.borrowedAsset)))
.wmul(_positionAmount)
.wmul(
WadRayMath.WAD -
IPrimexDNSV3(_primexDNS).protocolFeeRates(
IPrimexDNSStorageV3.FeeRateType.MarginPositionClosedByKeeper
)
) * 10 ** (18 - IERC20Metadata(_positionAsset).decimals());
// numerator = data.bucket.feeBuffer().wmul(_positionDebt) * multiplier1;
return (data.bucket.feeBuffer().wmul(_positionDebt) * multiplier1).wdiv(denominator) / multiplier1;
}
/**
* @notice Validates if a position meets the minimum size requirement.
* @param _amount The amount of the asset in the position.
* @param _asset The asset associated with the position.
* @param _priceOracle The address of the price oracle contract.
* @param _nativeAssetOracleData NativeCurrency => Asset
*/
function validateMinPositionSize(
uint256 _amount,
address _asset,
address _priceOracle,
IKeeperRewardDistributorV3 _keeperRewardDistributor,
IPrimexDNSV3 _primexDNS,
IPrimexDNSStorageV3.TradingOrderType _tradingOrderType,
bytes calldata _nativeAssetOracleData
) public {
_require(
isGreaterThanMinPositionSize(
_asset,
_amount,
_priceOracle,
_keeperRewardDistributor,
_primexDNS,
_tradingOrderType,
_nativeAssetOracleData
),
Errors.INSUFFICIENT_POSITION_SIZE.selector
);
}
/**
* @notice Checks if the given amount of _asset corresponds to the minimum position size _minPositionSize,
* based on the _minPositionAsset and the provided _priceOracle.
* Returns true if the amount corresponds to or exceeds the minimum position size, otherwise returns false.
* @param _asset The address of the asset being checked.
* @param _amount The amount of _asset being checked.
* @param _priceOracle The address of the price oracle contract.
* @return A boolean value indicating whether the amount corresponds to or exceeds the minimum position size.
*/
function isGreaterThanMinPositionSize(
address _asset,
uint256 _amount,
address _priceOracle,
IKeeperRewardDistributorV3 _keeperRewardDistributor,
IPrimexDNSV3 _primexDNS,
IPrimexDNSStorageV3.TradingOrderType _tradingOrderType,
bytes calldata _nativeAssetOracleData
) public returns (bool) {
uint256 minPositionSizeInNativeCurrency = minPositionSize(
_priceOracle,
_keeperRewardDistributor,
_primexDNS,
_tradingOrderType
);
uint256 minPositionSizeInAsset = getOracleAmountsOut(
NATIVE_CURRENCY,
_asset,
minPositionSizeInNativeCurrency,
_priceOracle,
_nativeAssetOracleData
);
return _amount >= minPositionSizeInAsset;
}
/**
* @notice Decodes an encoded path and returns an array of addresses.
* @param encodedPath The encoded path to be decoded.
* @param dexRouter The address of the DEX router.
* @param dexAdapter The address of the DEX adapter.
* @return path An array of addresses representing the decoded path.
*/
function decodePath(
bytes memory encodedPath,
address dexRouter,
address payable dexAdapter
) public view returns (address[] memory path) {
IDexAdapter.DexType type_ = IDexAdapter(dexAdapter).dexType(dexRouter);
if (type_ == IDexAdapter.DexType.UniswapV2 || type_ == IDexAdapter.DexType.Meshswap) {
path = abi.decode(encodedPath, (address[]));
} else if (type_ == IDexAdapter.DexType.UniswapV3) {
uint256 skip;
uint256 offsetSize = 23; // address size(20) + fee size(3)
uint256 pathLength = encodedPath.length / offsetSize + 1;
path = new address[](pathLength);
for (uint256 i; i < pathLength; i++) {
path[i] = encodedPath.toAddress(skip, encodedPath.length);
skip += offsetSize;
}
} else if (type_ == IDexAdapter.DexType.Curve) {
(path, ) = abi.decode(encodedPath, (address[], address[]));
} else if (type_ == IDexAdapter.DexType.Balancer) {
(path, , ) = abi.decode(encodedPath, (address[], bytes32[], int256[]));
} else if (type_ == IDexAdapter.DexType.AlgebraV3) {
uint256 skip;
uint256 offsetSize = 20; // address size(20)
uint256 pathLength = encodedPath.length / offsetSize;
path = new address[](pathLength);
for (uint256 i; i < pathLength; i++) {
path[i] = encodedPath.toAddress(skip, encodedPath.length);
skip += offsetSize;
}
} else {
_revert(Errors.UNKNOWN_DEX_TYPE.selector);
}
}
/**
* @notice Returns the asset multiplier for a given asset.
* @dev If the asset is the native currency, the function returns 1.
* If the asset is USD, the function returns the value stored in the constant USD_MULTIPLIER.
* For any other asset, the function calculates the multiplier based on the number of decimals of the token.
* @param _asset The address of the asset.
* @return The asset multiplier. It is a number with 10 raised to a power of decimals of a given asset.
*/
function _getAssetMultiplier(address _asset) internal view returns (uint256) {
if (_asset == NATIVE_CURRENCY) return 1;
if (_asset == USD) return USD_MULTIPLIER;
return 10 ** (18 - IERC20Metadata(_asset).decimals());
}
function _calculateL1CostWei(
uint256 _baseLength,
IKeeperRewardDistributorV3 _keeperRewardDistributor
) internal view returns (uint256 l1CostWei) {
(
,
,
uint256 optimisticGasCoefficient,
IKeeperRewardDistributorStorage.PaymentModel paymentModel
) = _keeperRewardDistributor.getGasCalculationParams();
if (paymentModel == IKeeperRewardDistributorStorage.PaymentModel.ARBITRUM) {
return
l1CostWei =
ARB_NITRO_ORACLE.getL1BaseFeeEstimate() *
GAS_FOR_BYTE *
(_baseLength + TRANSACTION_METADATA_BYTES);
}
if (paymentModel == IKeeperRewardDistributorStorage.PaymentModel.OPTIMISTIC) {
// Adds 68 bytes of padding to account for the fact that the input does not have a signature.
uint256 l1GasUsed = GAS_FOR_BYTE * (_baseLength + OVM_GASPRICEORACLE.overhead() + 68);
return
l1CostWei =
(OVM_GASPRICEORACLE.l1BaseFee() *
l1GasUsed *
OVM_GASPRICEORACLE.scalar() *
optimisticGasCoefficient) /
10 ** 6;
}
return 0;
}
/**
* @notice Utility function to get the minimum of two values
*/
function min(uint256 a, uint256 b) internal pure returns (uint256) {
return a < b ? a : b;
}
/**
* @notice Utility function to get the maximum of two values
*/
function max(uint256 a, uint256 b) internal pure returns (uint256) {
return a > b ? a : b;
}
}// Copyright 2020 Compound Labs, Inc.
// (c) 2024 Primex.finance
// SPDX-License-Identifier: BSD-3-Clause
// Modified version of token transfer logic that allows working with non-standart ERC-20 tokens, added method doTransferFromTo,
// modified doTransferIn
pragma solidity 0.8.26;
import "./Errors.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {EIP20NonStandardInterface} from "../interfaces/EIP20NonStandardInterface.sol";
library TokenTransfersLibrary {
function doTransferIn(address token, address from, uint256 amount) public returns (uint256) {
return doTransferFromTo(token, from, address(this), amount);
}
function doTransferFromTo(address token, address from, address to, uint256 amount) public returns (uint256) {
uint256 balanceBefore = IERC20(token).balanceOf(to);
// The returned value is checked in the assembly code below.
// Arbitrary `from` should be checked at a higher level. The library function cannot be called by the user.
// slither-disable-next-line unchecked-transfer arbitrary-send-erc20
EIP20NonStandardInterface(token).transferFrom(from, to, amount);
bool success;
// solhint-disable-next-line no-inline-assembly
assembly ("memory-safe") {
switch returndatasize()
case 0 {
// This is a non-standard ERC-20
success := not(0) // set success to true
}
case 32 {
// This is a compliant ERC-20
returndatacopy(0, 0, 32)
success := mload(0) // Set `success = returndata` of external call
}
default {
// This is an excessively non-compliant ERC-20, revert.
revert(0, 0)
}
}
_require(success, Errors.TOKEN_TRANSFER_IN_FAILED.selector);
// Calculate the amount that was *actually* transferred
uint256 balanceAfter = IERC20(token).balanceOf(to);
_require(balanceAfter >= balanceBefore, Errors.TOKEN_TRANSFER_IN_OVERFLOW.selector);
return balanceAfter - balanceBefore; // underflow already checked above, just subtract
}
function doTransferOut(address token, address to, uint256 amount) public {
// The returned value is checked in the assembly code below.
// slither-disable-next-line unchecked-transfer
EIP20NonStandardInterface(token).transfer(to, amount);
bool success;
// solhint-disable-next-line no-inline-assembly
assembly ("memory-safe") {
switch returndatasize()
case 0 {
// This is a non-standard ERC-20
success := not(0) // set success to true
}
case 32 {
// This is a complaint ERC-20
returndatacopy(0, 0, 32)
success := mload(0) // Set `success = returndata` of external call
}
default {
// This is an excessively non-compliant ERC-20, revert.
revert(0, 0)
}
}
_require(success, Errors.TOKEN_TRANSFER_OUT_FAILED.selector);
}
function doTransferOutETH(address to, uint256 value) internal {
// solhint-disable-next-line avoid-low-level-calls
(bool success, ) = to.call{value: value}(new bytes(0));
_require(success, Errors.NATIVE_TOKEN_TRANSFER_FAILED.selector);
}
}// SPDX-License-Identifier: GPL-3.0-or-later
// A modified version of BytesLib
// Origin: https://github.com/1inch/universal-router/blob/b972662f8d3f0ba55ef99411720f613f77c3fab5/contracts/modules/uniswap/v3/BytesLib.sol
// Unused methods and constants were removed
pragma solidity 0.8.26;
library BytesLib {
error ToAddressOverflow();
error ToAddressOutOfBounds();
/// @notice Returns the address starting at byte `_start`
/// @dev _bytesLength must equal _bytes.length for this to function correctly
/// @param _bytes The input bytes string to slice
/// @param _start The starting index of the address
/// @param _bytesLength The length of _bytes
/// @return tempAddress The address starting at _start
function toAddress(
bytes memory _bytes,
uint256 _start,
uint256 _bytesLength
) internal pure returns (address tempAddress) {
unchecked {
if (_start + 20 < _start) revert ToAddressOverflow();
if (_bytesLength < _start + 20) revert ToAddressOutOfBounds();
}
assembly {
tempAddress := mload(add(add(_bytes, 0x14), _start))
}
}
}// SPDX-License-Identifier: GPL-3.0
// A modified version of ds-math library
// Origin: https://github.com/dapphub/ds-math/blob/master/src/math.sol
// Unused methods were removed, errors changed
pragma solidity 0.8.26;
error DS_MATH_ADD_OVERFLOW();
error DS_MATH_MUL_OVERFLOW();
library WadRayMath {
function add(uint256 x, uint256 y) internal pure returns (uint256 z) {
if ((z = x + y) < x) revert DS_MATH_ADD_OVERFLOW();
}
function mul(uint256 x, uint256 y) internal pure returns (uint256 z) {
if (!(y == 0 || (z = x * y) / y == x)) revert DS_MATH_MUL_OVERFLOW();
}
uint256 internal constant WAD = 10 ** 18;
uint256 internal constant RAY = 10 ** 27;
//rounds to zero if x*y < WAD / 2
function wmul(uint256 x, uint256 y) internal pure returns (uint256 z) {
z = add(mul(x, y), WAD / 2) / WAD;
}
//rounds to zero if x*y < RAY / 2
function rmul(uint256 x, uint256 y) internal pure returns (uint256 z) {
z = add(mul(x, y), RAY / 2) / RAY;
}
//rounds to zero if x*y < WAD / 2
function wdiv(uint256 x, uint256 y) internal pure returns (uint256 z) {
z = add(mul(x, WAD), y / 2) / y;
}
//rounds to zero if x*y < RAY / 2
function rdiv(uint256 x, uint256 y) internal pure returns (uint256 z) {
z = add(mul(x, RAY), y / 2) / y;
}
}// (c) 2024 Primex.finance
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.18;
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {IPrimexDNSV3} from "../PrimexDNS/IPrimexDNS.sol";
import {ITraderBalanceVault} from "../TraderBalanceVault/ITraderBalanceVault.sol";
import {IWhiteBlackList} from "../WhiteBlackList/WhiteBlackList/IWhiteBlackList.sol";
import {ILiquidityMiningRewardDistributorStorage} from "./ILiquidityMiningRewardDistributorStorage.sol";
import {IPausable} from "../interfaces/IPausable.sol";
interface ILiquidityMiningRewardDistributor is ILiquidityMiningRewardDistributorStorage, IPausable {
struct RewardsInPMX {
uint256 minReward;
uint256 maxReward;
uint256 extraReward;
}
/**
* @notice Emitted when a reward is claimed by a receiver from a specific bucket.
* @param receiver The address of the receiver.
* @param bucket The address of the bucket from which the reward is claimed.
* @param amount The amount of the claimed reward.
*/
event ClaimedReward(address indexed receiver, address indexed bucket, uint256 amount);
/**
* @notice Emitted when PMX tokens are withdrawn by an admin.
* @param amount The amount of PMX tokens withdrawn.
*/
event WithdrawPmxByAdmin(uint256 indexed amount);
/**
* @notice Initializes the contract with the specified parameters.
* @param _primexDNS The address of the IPrimexDNS contract.
* @param _pmx The address of the PMX token contract.
* @param _traderBalanceVault The address of the TraderBalanceVault contract.
* @param _registry The address of the registry contract.
* @param _treasury The address of the treasury contract.
* @param _reinvestmentRate The rate at which rewards are reinvested.
* @param _reinvestmentDuration The duration for which rewards are reinvested.
* @param _whiteBlackList The address of the WhiteBlackList contract.
*/
function initialize(
IPrimexDNSV3 _primexDNS,
IERC20 _pmx,
ITraderBalanceVault _traderBalanceVault,
address _registry,
address _treasury,
uint256 _reinvestmentRate,
uint256 _reinvestmentDuration,
IWhiteBlackList _whiteBlackList
) external;
/**
* @notice Updates the reward amount for a specific bucket.
* @dev Only callable by the PrimexDNS contract.
* @param _bucketName The name of the bucket.
* @param _pmxRewardsAmount The amount of PMX rewards to be allocated to the bucket.
*/
function updateBucketReward(string memory _bucketName, uint256 _pmxRewardsAmount) external;
/**
* @notice Adds points for a user for future reward distribution.
* @dev Only callable by the Bucket contract.
* @param _bucketName The name of the bucket.
* @param _user The address of the user.
* @param _miningAmount The amount of mining points to be added.
* @param _maxStabilizationPeriodEnd The maximum end timestamp of the stabilization period.
* @param _maxPeriodTime The maximum period time.
* @param _currentTimestamp The current timestamp.
*/
function addPoints(
string memory _bucketName,
address _user,
uint256 _miningAmount,
uint256 _maxStabilizationPeriodEnd,
uint256 _maxPeriodTime,
uint256 _currentTimestamp
) external;
/**
* @notice Removes points for a user.
* @dev Only callable by the Bucket contract.
* @param _name The name of the bucket.
* @param _user The address of the user.
* @param _amount The amount of mining points to be removed.
*/
function removePoints(string memory _name, address _user, uint256 _amount) external;
/**
* @notice Claims the accumulated rewards for a specific bucket.
* @param _bucketName The name of the bucket.
*/
function claimReward(string memory _bucketName) external;
/**
* @notice Moves rewards from one bucket to another.
* @dev Only callable by the Bucket contract.
* @param _bucketFrom The name of the source bucket.
* @param _bucketTo The name of the destination bucket.
* @param _user The address of the user.
* @param _isBucketLaunched A flag indicating if the destination bucket is launched.
* @param _liquidityMiningDeadline The deadline for liquidity mining
*/
function reinvest(
string memory _bucketFrom,
string memory _bucketTo,
address _user,
bool _isBucketLaunched,
uint256 _liquidityMiningDeadline
) external;
/**
* @dev The function to withdraw PMX from a delisted bucket or a bucket where liquidity mining failed (after reinvesting period).
* Emits WithdrawPmxByAdmin event.
* @param _bucketFrom Name of the bucket with failed liquidity mining event.
*/
function withdrawPmxByAdmin(string memory _bucketFrom) external;
/**
* @notice Retrieves information about a lender in a specific bucket.
* @param _bucketName The name of the bucket.
* @param _lender The address of the lender.
* @param _timestamp The timestamp for which the information is queried.
* @return amountInMining The amount of tokens the lender has in mining for the given bucket.
* @return currentPercent The current percentage of rewards the lender is eligible to receive for the given bucket.
* Measured in WAD (1 WAD = 100%).
* @return rewardsInPMX An object containing information about the lender's rewards in PMX for the given bucket.
*/
function getLenderInfo(
string calldata _bucketName,
address _lender,
uint256 _timestamp
) external view returns (uint256 amountInMining, uint256 currentPercent, RewardsInPMX memory rewardsInPMX);
/**
* @notice Retrieves rewards information about a specific bucket.
* @param _bucketName The name of the bucket.
* @return totalPmxReward The total amount of PMX reward in the bucket.
* @return withdrawnRewards The total amount of withdrawn rewards from the bucket.
* @return totalPoints The total number of mining points in the bucket.
*/
function getBucketInfo(
string calldata _bucketName
) external view returns (uint256 totalPmxReward, uint256 withdrawnRewards, uint256 totalPoints);
/**
* @notice Retrieves the amount of tokens a lender has in mining for a specific bucket.
* @param _bucket The name of the bucket.
* @param _lender The address of the lender.
* @return The amount of tokens the lender has in mining for the given bucket.
*/
function getLenderAmountInMining(string calldata _bucket, address _lender) external view returns (uint256);
}// (c) 2024 Primex.finance
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.18;
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {IPrimexDNSV3} from "../PrimexDNS/IPrimexDNS.sol";
import {ITraderBalanceVault} from "../TraderBalanceVault/ITraderBalanceVault.sol";
interface ILiquidityMiningRewardDistributorStorage {
struct LenderInfo {
uint256 points;
uint256 depositedAmount;
}
struct BucketInfo {
uint256 totalPoints;
uint256 totalPmxReward;
uint256 withdrawnRewards;
mapping(address => LenderInfo) lendersInfo;
}
function primexDNS() external view returns (IPrimexDNSV3);
function pmx() external view returns (IERC20);
function traderBalanceVault() external view returns (ITraderBalanceVault);
function registry() external view returns (address);
function reinvestmentRate() external view returns (uint256);
function reinvestmentDuration() external view returns (uint256);
function extraRewards(address, string calldata) external view returns (uint256);
}// (c) 2024 Primex.finance
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.18;
import {LimitOrderLibrary} from "../libraries/LimitOrderLibrary.sol";
import {PrimexPricingLibrary} from "../libraries/PrimexPricingLibrary.sol";
import {PositionLibrary} from "../libraries/PositionLibrary.sol";
import {IPositionManagerStorage, IPositionManagerStorageV2, IKeeperRewardDistributorV3} from "./IPositionManagerStorage.sol";
import {IPositionManagerEvents} from "./IPositionManagerEvents.sol";
import {IPrimexDNSStorageV3} from "../PrimexDNS/IPrimexDNSStorage.sol";
import {IPausable} from "../interfaces/IPausable.sol";
interface IPositionManagerV2 is IPositionManagerStorageV2, IPositionManagerStorage, IPositionManagerEvents, IPausable {
struct ClosePositionVars {
PositionLibrary.Position position;
bool borrowedAmountIsNotZero;
uint256 oracleTolerableLimit;
bool needOracleTolerableLimitCheck;
}
event ChangePositionManagerExtension(address indexed newPositionManagerExtension);
event IncreaseDeposit(
uint256 indexed positionId,
address indexed trader,
uint256 depositDelta,
uint256 scaledDebtAmount
);
struct ClosePositionByConditionParams {
uint256 id;
address keeper;
PrimexPricingLibrary.MegaRoute[] megaRoutes;
uint256 conditionIndex;
bytes ccmAdditionalParams;
PositionLibrary.CloseReason closeReason;
bytes positionSoldAssetOracleData;
bytes nativePmxOracleData;
bytes positionNativeAssetOracleData;
bytes pmxSoldAssetOracleData;
bytes nativeSoldAssetOracleData;
bytes[][] pullOracleData;
uint256[] pullOracleTypes;
}
/**
* @notice Initializes the contract with the specified addresses and initializes inherited contracts.
* @param _registry The address of the Registry contract.
* @param _primexDNS The address of the PrimexDNS contract.
* @param _traderBalanceVault The address of the TraderBalanceVault contract.
* @param _priceOracle The address of the PriceOracle contract.
* @param _keeperRewardDistributor The address of the KeeperRewardDistributor contract.
* @param _whiteBlackList The address of the WhiteBlacklist contract.
* @param _positionManagerExtension The address of the PositionManagerExtension contract.
*/
function initialize(
address _registry,
address _primexDNS,
address payable _traderBalanceVault,
address _priceOracle,
address _keeperRewardDistributor,
address _whiteBlackList,
address _positionManagerExtension
) external;
/**
* @notice Sets the positionManagerExtension.
* @dev Only callable by the BIG_TIMELOCK_ADMIN role.
* @param _newPositionManagerExtension The address of PositionManagerExtension contract.
*/
function setPositionManagerExtension(address _newPositionManagerExtension) external;
/**
* @notice Sets protocol parameters through an administrative delegatecall.
* @dev This function allows an admin to update protocol parameters using a delegatecall to the PositionManagerExtension contract.
* @param _data The data containing the encoded function call to be executed by the delegatecall.
*/
function setProtocolParamsByAdmin(bytes calldata _data) external;
/**
* @notice Opens a position based on the provided order parameters.
* @dev Only callable by the LOM_ROLE role.
* @param _params The parameters for opening a position.
* @return The total borrowed amount, position amount, position ID, and entry price of the new position.
*/
function openPositionByOrder(
LimitOrderLibrary.OpenPositionByOrderParams calldata _params
) external returns (uint256, uint256, uint256, uint256, uint256);
/**
* @notice Opens margin position.
* @dev Locks trader's collateral in TraderBalanceVault. Takes loan from bucket for deal.
* Makes swap bucket borrowedAsset amount on '_dex'. Updates rates and indexes in the '_bucket'.
* Mints debtToken for trader (msg.sender)
* @param _params The parameters required to open a position.
*/
function openPosition(PositionLibrary.OpenPositionParams calldata _params) external payable;
/**
* @notice Close trader's active position or liquidate risky position.
* @dev Protocol will fall down (revert) if two conditions occur both:
* 1. (token1Price + position.depositedAmount).wdiv(positionDebt) will become lower than 1,
* so position will make loss for Protocol.
* 2. Not enough liquidity in bucket to pay that loss.
* @param _id Position id for `msg.sender`.
* @param _dealReceiver The receiver of the rest of trader's deposit.
* @param _megaRoutes swap routes on dexes
* @param _amountOutMin minimum allowed amount out for position
*/
function closePosition(
uint256 _id,
address _dealReceiver,
PrimexPricingLibrary.MegaRoute[] calldata _megaRoutes,
uint256 _amountOutMin,
bytes calldata _positionSoldAssetOracleData,
bytes calldata _pmxSoldAssetOracleData,
bytes calldata _nativeSoldAssetOracleData,
bytes[][] calldata _pullOracleData,
uint256[] calldata _pullOracleTypes
) external payable;
// /**
// * @notice Closes trader's active position by closing condition
// * @param _id Position id.
// * @param _keeper The address of the keeper or the recipient of the reward.
// * @param _megaRoutes An array of routes for executing trades, swap routes on dexes.
// * @param _conditionIndex The index of the condition to be used for closing the position.
// * @param _ccmAdditionalParams Additional params needed for canBeClosedAfterSwap of the ConditionalClosingManager.
// * @param _closeReason The reason for closing the position.
// */
function closePositionByCondition(ClosePositionByConditionParams calldata _params) external payable;
/**
* @notice Updates the position with the given position ID by setting new close conditions.
* @param _positionId The ID of the position to update.
* @param _closeConditions An array of close conditions for the position.
* @dev The caller of this function must be the trader who owns the position.
* @dev Emits an `UpdatePositionConditions` event upon successful update.
*/
function updatePositionConditions(
uint256 _positionId,
LimitOrderLibrary.Condition[] calldata _closeConditions
) external;
/**
* @notice Increases the deposit amount for a given position.
* @param _positionId The ID of the position to increase the deposit for.
* @param _amount The amount to increase the deposit by.
* @param _asset The address of the asset to deposit.
* @param _takeDepositFromWallet A flag indicating whether to make the deposit immediately.
* @param _megaRoutes An array of routes to use for trading.
* @param _amountOutMin The minimum amount of the output asset to receive from trading.
*/
function increaseDeposit(
uint256 _positionId,
uint256 _amount,
address _asset,
bool _takeDepositFromWallet,
PrimexPricingLibrary.MegaRoute[] calldata _megaRoutes,
uint256 _amountOutMin
) external;
/**
* @notice Decreases the deposit amount for a given position.
* @param _positionId The ID of the position.
* @param _amount The amount to decrease the deposit by.
*/
function decreaseDeposit(
uint256 _positionId,
uint256 _amount,
bytes calldata _positionSoldAssetOracleData,
bytes calldata _nativeSoldAssetOracleData,
bytes[][] calldata _pullOracleData,
uint256[] calldata _pullOracleTypes
) external payable;
/**
* @notice Deletes a positions by their IDs from a specific bucket for a given traders.
* @param _ids The IDs of the positions to be deleted.
* @param _traders The addresses of the traders who owns the position.
* @param _length The length of the traders array.
* @param _bucket The address of the bucket from which the position is to be deleted.
*/
function deletePositions(
uint256[] calldata _ids,
address[] calldata _traders,
uint256 _length,
address _bucket
) external;
/**
* @notice Allows the trader to partially close a position.
* @param _positionId The ID of the position to be partially closed.
* @param _amount The amount of the position asset to be closed from the position.
* @param _depositReceiver The address where the remaining deposit will be sent.
* @param _megaRoutes The routing information for swapping assets.
* @param _amountOutMin The minimum amount to be received after swapping, measured in the same decimal format as the position's asset.
*/
function partiallyClosePosition(
uint256 _positionId,
uint256 _amount,
address _depositReceiver,
PrimexPricingLibrary.MegaRoute[] calldata _megaRoutes,
uint256 _amountOutMin,
bytes calldata _positionSoldAssetOracleData,
bytes calldata _nativePositionAssetOracleData,
bytes calldata _nativeSoldAssetOracleData,
bytes calldata _pmxSoldAssetOracleData,
bytes[][] calldata _pullOracleData,
uint256[] calldata _pullOracleTypes
) external payable;
/**
* @notice Transfers a specified amount of tokens from the contract to a specified address.
* @dev Only callable by the BATCH_MANAGER_ROLE role.
* @param _token The address of the token to be transferred.
* @param _to The address to which the tokens will be transferred.
* @param _amount The amount of tokens to be transferred.
*/
function doTransferOut(address _token, address _to, uint256 _amount) external;
/**
* @notice Returns the oracle tolerable limit for the given asset pair.
* @param assetA The address of the first asset in the pair.
* @param assetB The address of the second asset in the pair.
* @return The oracle tolerable limit in WAD format (1 WAD = 100%) for the asset pair.
*/
function getOracleTolerableLimit(address assetA, address assetB) external view returns (uint256);
/**
* @notice Retrieves the position information for a given ID.
* @param _id The ID of the position to retrieve.
* @return position The position information associated with the given ID.
*/
function getPosition(uint256 _id) external view returns (PositionLibrary.Position memory);
/**
* @notice Retrieves the position at the specified index.
* @param _index The index of the position to retrieve.
* @return The Position struct at the specified index.
*/
function getPositionByIndex(uint256 _index) external view returns (PositionLibrary.Position memory);
/**
* @notice Returns the length of the positions array.
* @return The length of the positions array.
*/
function getAllPositionsLength() external view returns (uint256);
/**
* @notice Returns the length of the array containing the positions of a specific trader.
* @param _trader The address of the trader.
* @return The number of positions the trader has.
*/
function getTraderPositionsLength(address _trader) external view returns (uint256);
/**
* @notice Returns the length of the array containing the positions of a specific bucket.
* @param _bucket The address of the bucket.
* @return The number of positions the bucket has.
*/
function getBucketPositionsLength(address _bucket) external view returns (uint256);
/**
* @notice Returns the debt of a position with the given ID.
* @param _id The ID of the position.
* @return The debt of the position, measured in the same decimal format as debtTokens.
*/
function getPositionDebt(uint256 _id) external view returns (uint256);
/**
* @notice Retrieves the close conditions for a specific position.
* @param _positionId The ID of the position.
* @return An array of close conditions associated with the position.
*/
function getCloseConditions(uint256 _positionId) external view returns (LimitOrderLibrary.Condition[] memory);
/**
* @notice Retrieves the close condition for a given position and index.
* @param _positionId The identifier of the position.
* @param _index The index of the close condition.
* @return The close condition at the specified position and index.
*/
function getCloseCondition(
uint256 _positionId,
uint256 _index
) external view returns (LimitOrderLibrary.Condition memory);
/**
* @notice Checks if a position with the given ID is delisted.
* @param _id The ID of the position.
* @return A boolean indicating whether the position is delisted or not.
*/
function isDelistedPosition(uint256 _id) external view returns (bool);
}
interface IPositionManager is IPositionManagerStorage, IPausable {
struct ClosePositionVars {
PositionLibrary.Position position;
bool borrowedAmountIsNotZero;
uint256 oracleTolerableLimit;
bool needOracleTolerableLimitCheck;
}
event SetMaxPositionSize(address token0, address token1, uint256 amountInToken0, uint256 amountInToken1);
event SetDefaultOracleTolerableLimit(uint256 indexed oracleTolerableLimit);
event SecurityBufferChanged(uint256 indexed securityBuffer);
event MaintenanceBufferChanged(uint256 indexed maintenanceBuffer);
event SetOracleTolerableLimit(address indexed assetA, address indexed assetB, uint256 oracleTolerableLimit);
event KeeperRewardDistributorChanged(address indexed _keeperRewardDistributor);
event MinPositionSizeAndAssetChanged(uint256 indexed _minPositionSize, address indexed _minPositionAsset);
event OracleTolerableLimitMultiplierChanged(uint256 indexed newMultiplier);
event OpenPosition(
uint256 indexed positionId,
address indexed trader,
address indexed openedBy,
PositionLibrary.Position position,
address feeToken,
uint256 protocolFee,
uint256 entryPrice,
uint256 leverage,
LimitOrderLibrary.Condition[] closeConditions
);
event PartialClosePosition(
uint256 indexed positionId,
address indexed trader,
address bucketAddress,
address soldAsset,
address positionAsset,
uint256 decreasePositionAmount,
uint256 depositedAmount,
uint256 scaledDebtAmount,
int256 profit,
uint256 positionDebt,
uint256 amountOut
);
event IncreaseDeposit(
uint256 indexed positionId,
address indexed trader,
uint256 depositDelta,
uint256 scaledDebtAmount
);
event DecreaseDeposit(
uint256 indexed positionId,
address indexed trader,
uint256 depositDelta,
uint256 scaledDebtAmount
);
event UpdatePositionConditions(
uint256 indexed positionId,
address indexed trader,
LimitOrderLibrary.Condition[] closeConditions
);
/**
* @notice Initializes the contract with the specified addresses and initializes inherited contracts.
* @param _registry The address of the Registry contract.
* @param _primexDNS The address of the PrimexDNS contract.
* @param _traderBalanceVault The address of the TraderBalanceVault contract.
* @param _priceOracle The address of the PriceOracle contract.
* @param _keeperRewardDistributor The address of the KeeperRewardDistributor contract.
* @param _whiteBlackList The address of the WhiteBlacklist contract.
*/
function initialize(
address _registry,
address _primexDNS,
address payable _traderBalanceVault,
address _priceOracle,
address _keeperRewardDistributor,
address _whiteBlackList
) external;
/**
* @notice Sets the maximum position size for a pair of tokens.
* @dev Only callable by the MEDIUM_TIMELOCK_ADMIN role.
* @param _token0 The address of the first token in the pair.
* @param _token1 The address of the second token in the pair.
* @param _amountInToken0 The maximum amount of token0 allowed in the position.
* @param _amountInToken1 The maximum amount of token1 allowed in the position.
*/
function setMaxPositionSize(
address _token0,
address _token1,
uint256 _amountInToken0,
uint256 _amountInToken1
) external;
/**
* @notice Sets the default oracle tolerable limit for the protocol.
* @dev Only callable by the MEDIUM_TIMELOCK_ADMIN role.
* @param _percent The new value for the default oracle tolerable limit. Measured in WAD (1 WAD = 100%).
*/
function setDefaultOracleTolerableLimit(uint256 _percent) external;
/**
* @notice Sets the oracle tolerable limit between two assets.
* @dev Only callable by the BIG_TIMELOCK_ADMIN role.
* @param _assetA The address of the first asset.
* @param _assetB The address of the second asset.
* @param _percent The new value for the oracle tolerable limit between two assets. Measured in WAD (1 WAD = 100%).
*/
function setOracleTolerableLimit(address _assetA, address _assetB, uint256 _percent) external;
/**
* @notice Function to set oracleTolerableLimitMultiplier.
* @dev Only callable by the MEDIUM_TIMELOCK_ADMIN role.
* @param newMultiplier New multiplier in WAD format.
*/
function setOracleTolerableLimitMultiplier(uint256 newMultiplier) external;
/**
* @notice Sets the security buffer value.
* @dev Only callable by the MEDIUM_TIMELOCK_ADMIN role.
* 0 <= newSecurityBuffer < 1.
* Buffer security parameter is used in calculating the liquidation conditions
* https://docs.google.com/document/d/1kR8eaqV4289MAbLKgIfKsZ2NgjFpeC0vpVL7jVUTvho/edit#bookmark=id.i9v508hvrv42
* @param newSecurityBuffer The new value of the security buffer in WAD format.
*/
function setSecurityBuffer(uint256 newSecurityBuffer) external;
/**
* @notice Sets the maintenance buffer value.
* @dev Only callable by the MEDIUM_TIMELOCK_ADMIN role.
* The new maintenance buffer value should be greater than zero and less than one.
* Maintenance buffer is used in calculating the maximum leverage
* https://docs.google.com/document/d/1kR8eaqV4289MAbLKgIfKsZ2NgjFpeC0vpVL7jVUTvho/edit#bookmark=id.87oc1j1s9z21
* @param newMaintenanceBuffer The new value of the maintenance buffer in WAD format.
*/
function setMaintenanceBuffer(uint256 newMaintenanceBuffer) external;
/**
* @notice Sets the address of the SpotTradingRewardDistributor contract.
* @dev Only callable by the BIG_TIMELOCK_ADMIN role.
* @param _spotTradingRewardDistributor The address of the SpotTradingRewardDistributor contract.
*/
function setSpotTradingRewardDistributor(address _spotTradingRewardDistributor) external;
/**
* @notice Sets the KeeperRewardDistributor contract.
* @dev Only callable by the BIG_TIMELOCK_ADMIN role.
* @param _keeperRewardDistributor The address of the KeeperRewardDistributor contract.
*/
function setKeeperRewardDistributor(IKeeperRewardDistributorV3 _keeperRewardDistributor) external;
/**
* @notice Opens a position based on the provided order parameters.
* @dev Only callable by the LOM_ROLE role.
* @param _params The parameters for opening a position.
* @return The total borrowed amount, position amount, position ID, and entry price of the new position.
*/
function openPositionByOrder(
LimitOrderLibrary.OpenPositionByOrderParams calldata _params
) external returns (uint256, uint256, uint256, uint256);
/**
* @notice Opens margin position.
* @dev Locks trader's collateral in TraderBalanceVault. Takes loan from bucket for deal.
* Makes swap bucket borrowedAsset amount on '_dex'. Updates rates and indexes in the '_bucket'.
* Mints debtToken for trader (msg.sender)
* @param _params The parameters required to open a position.
*/
function openPosition(PositionLibrary.OpenPositionParams calldata _params) external payable;
/**
* @notice Close trader's active position or liquidate risky position.
* @dev Protocol will fall down (revert) if two conditions occur both:
* 1. (token1Price + position.depositedAmount).wdiv(positionDebt) will become lower than 1,
* so position will make loss for Protocol.
* 2. Not enough liquidity in bucket to pay that loss.
* @param _id Position id for `msg.sender`.
* @param _dealReceiver The receiver of the rest of trader's deposit.
* @param _megaRoutes swap routes on dexes
* @param _amountOutMin minimum allowed amount out for position
*/
function closePosition(
uint256 _id,
address _dealReceiver,
PrimexPricingLibrary.MegaRoute[] memory _megaRoutes,
uint256 _amountOutMin
) external;
/**
* @notice Closes trader's active position by closing condition
* @param _id Position id.
* @param _keeper The address of the keeper or the recipient of the reward.
* @param _megaRoutes An array of routes for executing trades, swap routes on dexes.
* @param _conditionIndex The index of the condition to be used for closing the position.
* @param _ccmAdditionalParams Additional params needed for canBeClosedAfterSwap of the ConditionalClosingManager.
* @param _closeReason The reason for closing the position.
*/
function closePositionByCondition(
uint256 _id,
address _keeper,
PrimexPricingLibrary.MegaRoute[] calldata _megaRoutes,
uint256 _conditionIndex,
bytes calldata _ccmAdditionalParams,
PositionLibrary.CloseReason _closeReason
) external;
/**
* @notice Allows the trader to partially close a position.
* @param _positionId The ID of the position to be partially closed.
* @param _amount The amount of the position asset to be closed from the position.
* @param _depositReceiver The address where the remaining deposit will be sent.
* @param _megaRoutes The routing information for swapping assets.
* @param _amountOutMin The minimum amount to be received after swapping, measured in the same decimal format as the position's asset.
*/
function partiallyClosePosition(
uint256 _positionId,
uint256 _amount,
address _depositReceiver,
PrimexPricingLibrary.MegaRoute[] calldata _megaRoutes,
uint256 _amountOutMin
) external;
/**
* @notice Updates the position with the given position ID by setting new close conditions.
* @param _positionId The ID of the position to update.
* @param _closeConditions An array of close conditions for the position.
* @dev The caller of this function must be the trader who owns the position.
* @dev Emits an `UpdatePositionConditions` event upon successful update.
*/
function updatePositionConditions(
uint256 _positionId,
LimitOrderLibrary.Condition[] calldata _closeConditions
) external;
/**
* @notice Increases the deposit amount for a given position.
* @param _positionId The ID of the position to increase the deposit for.
* @param _amount The amount to increase the deposit by.
* @param _asset The address of the asset to deposit.
* @param _takeDepositFromWallet A flag indicating whether to make the deposit immediately.
* @param _megaRoutes An array of routes to use for trading.
* @param _amountOutMin The minimum amount of the output asset to receive from trading.
*/
function increaseDeposit(
uint256 _positionId,
uint256 _amount,
address _asset,
bool _takeDepositFromWallet,
PrimexPricingLibrary.MegaRoute[] calldata _megaRoutes,
uint256 _amountOutMin
) external;
/**
* @notice Decreases the deposit amount for a given position.
* @param _positionId The ID of the position.
* @param _amount The amount to decrease the deposit by.
*/
function decreaseDeposit(uint256 _positionId, uint256 _amount) external;
/**
* @notice Sets the minimum position size and the corresponding asset for positions.
* @dev Only callable by the MEDIUM_TIMELOCK_ADMIN role.
* @param _minPositionSize The new minimum position size.
* @param _minPositionAsset The address of the asset associated with the minimum position size.
*/
function setMinPositionSize(uint256 _minPositionSize, address _minPositionAsset) external;
/**
* @notice Deletes a positions by their IDs from a specific bucket for a given traders.
* @param _ids The IDs of the positions to be deleted.
* @param _traders The addresses of the traders who owns the position.
* @param _length The length of the traders array.
* @param _bucket The address of the bucket from which the position is to be deleted.
*/
function deletePositions(
uint256[] calldata _ids,
address[] calldata _traders,
uint256 _length,
address _bucket
) external;
/**
* @notice Transfers a specified amount of tokens from the contract to a specified address.
* @dev Only callable by the BATCH_MANAGER_ROLE role.
* @param _token The address of the token to be transferred.
* @param _to The address to which the tokens will be transferred.
* @param _amount The amount of tokens to be transferred.
*/
function doTransferOut(address _token, address _to, uint256 _amount) external;
/**
* @notice Returns the oracle tolerable limit for the given asset pair.
* @param assetA The address of the first asset in the pair.
* @param assetB The address of the second asset in the pair.
* @return The oracle tolerable limit in WAD format (1 WAD = 100%) for the asset pair.
*/
function getOracleTolerableLimit(address assetA, address assetB) external view returns (uint256);
/**
* @notice Retrieves the position information for a given ID.
* @param _id The ID of the position to retrieve.
* @return position The position information associated with the given ID.
*/
function getPosition(uint256 _id) external view returns (PositionLibrary.Position memory);
/**
* @notice Retrieves the position at the specified index.
* @param _index The index of the position to retrieve.
* @return The Position struct at the specified index.
*/
function getPositionByIndex(uint256 _index) external view returns (PositionLibrary.Position memory);
/**
* @notice Returns the length of the positions array.
* @return The length of the positions array.
*/
function getAllPositionsLength() external view returns (uint256);
/**
* @notice Returns the length of the array containing the positions of a specific trader.
* @param _trader The address of the trader.
* @return The number of positions the trader has.
*/
function getTraderPositionsLength(address _trader) external view returns (uint256);
/**
* @notice Returns the length of the array containing the positions of a specific bucket.
* @param _bucket The address of the bucket.
* @return The number of positions the bucket has.
*/
function getBucketPositionsLength(address _bucket) external view returns (uint256);
/**
* @notice Returns the debt of a position with the given ID.
* @param _id The ID of the position.
* @return The debt of the position, measured in the same decimal format as debtTokens.
*/
function getPositionDebt(uint256 _id) external view returns (uint256);
/**
* @notice Retrieves the close conditions for a specific position.
* @param _positionId The ID of the position.
* @return An array of close conditions associated with the position.
*/
function getCloseConditions(uint256 _positionId) external view returns (LimitOrderLibrary.Condition[] memory);
/**
* @notice Retrieves the close condition for a given position and index.
* @param _positionId The identifier of the position.
* @param _index The index of the close condition.
* @return The close condition at the specified position and index.
*/
function getCloseCondition(
uint256 _positionId,
uint256 _index
) external view returns (LimitOrderLibrary.Condition memory);
/**
* @notice Checks if a position with the given ID is delisted.
* @param _id The ID of the position.
* @return A boolean indicating whether the position is delisted or not.
*/
function isDelistedPosition(uint256 _id) external view returns (bool);
}// (c) 2024 Primex.finance
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.18;
import {LimitOrderLibrary} from "../libraries/LimitOrderLibrary.sol";
import {PositionLibrary} from "../libraries/PositionLibrary.sol";
interface IPositionManagerEvents {
event SetMaxPositionSize(address token0, address token1, uint256 amountInToken0, uint256 amountInToken1);
event SetDefaultOracleTolerableLimit(uint256 indexed oracleTolerableLimit);
event SecurityBufferChanged(uint256 indexed securityBuffer);
event MaintenanceBufferChanged(uint256 indexed maintenanceBuffer);
event SetOracleTolerableLimit(address indexed assetA, address indexed assetB, uint256 oracleTolerableLimit);
event KeeperRewardDistributorChanged(address indexed _keeperRewardDistributor);
event OracleTolerableLimitMultiplierChanged(uint256 indexed newMultiplier);
event OpenPosition(
uint256 indexed positionId,
address indexed trader,
address indexed openedBy,
PositionLibrary.Position position,
uint256 entryPrice,
uint256 leverage,
LimitOrderLibrary.Condition[] closeConditions
);
event PartialClosePosition(
uint256 indexed positionId,
address indexed trader,
address bucketAddress,
address soldAsset,
address positionAsset,
uint256 decreasePositionAmount,
uint256 depositedAmount,
uint256 scaledDebtAmount,
int256 profit,
uint256 positionDebt,
uint256 amountOut
);
event DecreaseDeposit(
uint256 indexed positionId,
address indexed trader,
uint256 depositDelta,
uint256 scaledDebtAmount
);
event UpdatePositionConditions(
uint256 indexed positionId,
address indexed trader,
LimitOrderLibrary.Condition[] closeConditions
);
}// (c) 2024 Primex.finance
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.18;
import {LimitOrderLibrary} from "../libraries/LimitOrderLibrary.sol";
import {PrimexPricingLibrary} from "../libraries/PrimexPricingLibrary.sol";
import {PositionLibrary} from "../libraries/PositionLibrary.sol";
import {IPositionManagerStorageV2} from "../PositionManager/IPositionManagerStorage.sol";
import {IKeeperRewardDistributorV3} from "../KeeperRewardDistributor/IKeeperRewardDistributor.sol";
import {IPositionManagerEvents} from "./IPositionManagerEvents.sol";
interface IPositionManagerExtension is IPositionManagerStorageV2, IPositionManagerEvents {
/**
* @param token0 The address of the first token in the pair.
* @param token1 The address of the second token in the pair.
* @param amountInToken0 The maximum amount of token0 allowed in the position.
* @param amountInToken1 The maximum amount of token1 allowed in the position.
*/
struct MaxPositionSizeParams {
address token0;
address token1;
uint256 amountInToken0;
uint256 amountInToken1;
}
/**
* @param assetA The address of the first asset.
* @param assetB The address of the second asset.
* @param percent The new value for the oracle tolerable limit between two assets. Measured in WAD (1 WAD = 100%).
*/
struct OracleTolerableLimitsParams {
address assetA;
address assetB;
uint256 percent;
}
/**
* @notice Sets the maximum position size for a pair of tokens.
* @dev Only callable by the SMALL_TIMELOCK_ADMIN role.
* @param _token0 The address of the first token in the pair.
* @param _token1 The address of the second token in the pair.
* @param _amountInToken0 The maximum amount of token0 allowed in the position.
* @param _amountInToken1 The maximum amount of token1 allowed in the position.
*/
function setMaxPositionSize(
address _token0,
address _token1,
uint256 _amountInToken0,
uint256 _amountInToken1
) external;
/**
* @notice Same as the setMaxPositionSize but for a batch of sizes
* @dev Only callable by the SMALL_TIMELOCK_ADMIN role.
* @param _params The array of MaxPositionSizeParams structs
*/
function setMaxPositionSizes(MaxPositionSizeParams[] calldata _params) external;
/**
* @notice Sets the default oracle tolerable limit for the protocol.
* @dev Only callable by the MEDIUM_TIMELOCK_ADMIN role.
* @param _percent The new value for the default oracle tolerable limit. Measured in WAD (1 WAD = 100%).
*/
function setDefaultOracleTolerableLimit(uint256 _percent) external;
/**
* @notice Sets the oracle tolerable limit between two assets.
* @dev Only callable by the SMALL_TIMELOCK_ADMIN role.
* @param _assetA The address of the first asset.
* @param _assetB The address of the second asset.
* @param _percent The new value for the oracle tolerable limit between two assets. Measured in WAD (1 WAD = 100%).
*/
function setOracleTolerableLimit(address _assetA, address _assetB, uint256 _percent) external;
/**
* @notice Same as the setOracleTolerableLimit but for a batch of limits
* @dev Only callable by the SMALL_TIMELOCK_ADMIN role.
* @param _limitParams The array of the OracleTolerableLimitsParams sctructs
*/
function setOracleTolerableLimits(OracleTolerableLimitsParams[] calldata _limitParams) external;
/**
* @notice Function to set oracleTolerableLimitMultiplier.
* @dev Only callable by the MEDIUM_TIMELOCK_ADMIN role.
* @param newMultiplier New multiplier in WAD format.
*/
function setOracleTolerableLimitMultiplier(uint256 newMultiplier) external;
/**
* @notice Sets the security buffer value.
* @dev Only callable by the MEDIUM_TIMELOCK_ADMIN role.
* 0 <= newSecurityBuffer < 1.
* Buffer security parameter is used in calculating the liquidation conditions
* https://docs.google.com/document/d/1kR8eaqV4289MAbLKgIfKsZ2NgjFpeC0vpVL7jVUTvho/edit#bookmark=id.i9v508hvrv42
* @param newSecurityBuffer The new value of the security buffer in WAD format.
*/
function setSecurityBuffer(uint256 newSecurityBuffer) external;
/**
* @notice Sets the maintenance buffer value.
* @dev Only callable by the MEDIUM_TIMELOCK_ADMIN role.
* The new maintenance buffer value should be greater than zero and less than one.
* Maintenance buffer is used in calculating the maximum leverage
* https://docs.google.com/document/d/1kR8eaqV4289MAbLKgIfKsZ2NgjFpeC0vpVL7jVUTvho/edit#bookmark=id.87oc1j1s9z21
* @param newMaintenanceBuffer The new value of the maintenance buffer in WAD format.
*/
function setMaintenanceBuffer(uint256 newMaintenanceBuffer) external;
/**
* @notice Sets the address of the SpotTradingRewardDistributor contract.
* @dev Only callable by the BIG_TIMELOCK_ADMIN role.
* @param _spotTradingRewardDistributor The address of the SpotTradingRewardDistributor contract.
*/
function setSpotTradingRewardDistributor(address _spotTradingRewardDistributor) external;
/**
* @notice Sets the KeeperRewardDistributor contract.
* @dev Only callable by the BIG_TIMELOCK_ADMIN role.
* @param _keeperRewardDistributor The instance of the KeeperRewardDistributor contract.
*/
function setKeeperRewardDistributor(IKeeperRewardDistributorV3 _keeperRewardDistributor) external;
/**
* @notice Allows the trader to partially close a position.
* @param _positionId The ID of the position to be partially closed.
* @param _amount The amount of the position asset to be closed from the position.
* @param _depositReceiver The address where the remaining deposit will be sent.
* @param _megaRoutes The routing information for swapping assets.
* @param _amountOutMin The minimum amount to be received after swapping, measured in the same decimal format as the position's asset.
*/
function partiallyClosePosition(
uint256 _positionId,
uint256 _amount,
address _depositReceiver,
PrimexPricingLibrary.MegaRoute[] calldata _megaRoutes,
uint256 _amountOutMin,
bytes calldata _positionSoldAssetOracleData,
bytes calldata _nativePositionAssetOracleData,
bytes calldata _nativeSoldAssetOracleData,
bytes calldata _pmxSoldAssetOracleData,
bytes[][] calldata _pullOracleData,
uint256[] calldata _pullOracleTypes
) external payable;
/**
* @notice Opens a position based on the provided order parameters.
* @dev Only callable by the LOM_ROLE role.
* @param _params The parameters for opening a position.
* @return The total borrowed amount, position amount, position ID, and entry price of the new position.
*/
function openPositionByOrder(
LimitOrderLibrary.OpenPositionByOrderParams calldata _params
) external returns (uint256, uint256, uint256, uint256, uint256);
/**
* @notice Opens margin position.
* @dev Locks trader's collateral in TraderBalanceVault. Takes loan from bucket for deal.
* Makes swap bucket borrowedAsset amount on '_dex'. Updates rates and indexes in the '_bucket'.
* Mints debtToken for trader (msg.sender)
* @param _params The parameters required to open a position.
*/
function openPosition(PositionLibrary.OpenPositionParams calldata _params) external payable;
/**
* @notice Decreases the deposit amount for a given position.
* @param _positionId The ID of the position.
* @param _amount The amount to decrease the deposit by.
*/
function decreaseDeposit(
uint256 _positionId,
uint256 _amount,
bytes calldata _positionSoldAssetOracleData,
bytes calldata _nativeSoldAssetOracleData,
bytes[][] calldata _pullOracleData,
uint256[] calldata _pullOracleTypes
) external payable;
/**
* @notice Updates the position with the given position ID by setting new close conditions.
* @param _positionId The ID of the position to update.
* @param _closeConditions An array of close conditions for the position.
* @dev The caller of this function must be the trader who owns the position.
* @dev Emits an `UpdatePositionConditions` event upon successful update.
*/
function updatePositionConditions(
uint256 _positionId,
LimitOrderLibrary.Condition[] calldata _closeConditions
) external;
/**
* @notice Returns the oracle tolerable limit for the given asset pair.
* @param assetA The address of the first asset in the pair.
* @param assetB The address of the second asset in the pair.
* @return The oracle tolerable limit in WAD format (1 WAD = 100%) for the asset pair.
*/
function getOracleTolerableLimit(address assetA, address assetB) external view returns (uint256);
}// (c) 2024 Primex.finance
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.18;
import {IAccessControl} from "@openzeppelin/contracts/access/IAccessControl.sol";
import {PositionLibrary} from "../libraries/PositionLibrary.sol";
import {LimitOrderLibrary} from "../libraries/LimitOrderLibrary.sol";
import {IPrimexDNSV3} from "../PrimexDNS/IPrimexDNS.sol";
import {IPriceOracleV2} from "../PriceOracle/IPriceOracle.sol";
import {ITraderBalanceVault} from "../TraderBalanceVault/ITraderBalanceVault.sol";
import {IKeeperRewardDistributorV3} from "../KeeperRewardDistributor/IKeeperRewardDistributor.sol";
import {ISpotTradingRewardDistributorV2} from "../SpotTradingRewardDistributor/ISpotTradingRewardDistributor.sol";
interface IPositionManagerStorage {
function maxPositionSize(address, address) external returns (uint256);
function defaultOracleTolerableLimit() external returns (uint256);
function securityBuffer() external view returns (uint256);
function maintenanceBuffer() external view returns (uint256);
function positionsId() external view returns (uint256);
function traderPositionIds(address _trader, uint256 _index) external view returns (uint256);
function bucketPositionIds(address _bucket, uint256 _index) external view returns (uint256);
function registry() external view returns (IAccessControl);
function traderBalanceVault() external view returns (ITraderBalanceVault);
function primexDNS() external view returns (IPrimexDNSV3);
function priceOracle() external view returns (IPriceOracleV2);
function keeperRewardDistributor() external view returns (IKeeperRewardDistributorV3);
function spotTradingRewardDistributor() external view returns (ISpotTradingRewardDistributorV2);
function minPositionSize() external view returns (uint256);
function minPositionAsset() external view returns (address);
}
interface IPositionManagerStorageV2 {
function positionManagerExtension() external view returns (address);
}// (c) 2024 Primex.finance
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.26;
import {IAccessControl} from "@openzeppelin/contracts/access/IAccessControl.sol";
import {ERC165Upgradeable} from "@openzeppelin/contracts-upgradeable/utils/introspection/ERC165Upgradeable.sol";
import {ReentrancyGuardUpgradeable} from "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol";
import {PausableUpgradeable} from "@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol";
import {PositionLibrary} from "../libraries/PositionLibrary.sol";
import {LimitOrderLibrary} from "../libraries/LimitOrderLibrary.sol";
import "../libraries/Errors.sol";
import {IPositionManagerStorage, IPositionManagerStorageV2} from "./IPositionManagerStorage.sol";
import {IKeeperRewardDistributorV3} from "../KeeperRewardDistributor/IKeeperRewardDistributor.sol";
import {ITraderBalanceVault} from "../TraderBalanceVault/ITraderBalanceVault.sol";
import {ISpotTradingRewardDistributorV2} from "../SpotTradingRewardDistributor/ISpotTradingRewardDistributor.sol";
import {IPrimexDNSV3} from "../PrimexDNS/IPrimexDNS.sol";
import {IPriceOracleV2} from "../PriceOracle/IPriceOracle.sol";
import {IWhiteBlackList} from "../WhiteBlackList/WhiteBlackList/IWhiteBlackList.sol";
abstract contract PositionManagerStorage is
IPositionManagerStorage,
ReentrancyGuardUpgradeable,
PausableUpgradeable,
ERC165Upgradeable
{
// Mapping from asset sell to mapping of asset buy with max amount in asset buy
mapping(address => mapping(address => uint256)) public override maxPositionSize;
//The oracleTolerableLimit is the percentage by which a deviation from the oracle price is allowed.
//This means that the dex amount out must be greater than oracle amount out * (100% - oracleTolerableLimit)
//Specified in the WAD format: 1e17 = 10%, 1e18 = 100% and so on
//The defaultOracleTolerableLimit this is the oracleTolerableLimit that is returned when there is
//no set the oracleTolerableLimit for a specific pair of asset
uint256 public override defaultOracleTolerableLimit;
// Buffer security parameter, which characterizes additional price drop, occurs due to some unexpected events
// Specified in the WAD format: 1e17 = 0.1, 5e17 = 0.5 and so on
uint256 public override securityBuffer;
// Additional parameter is needed to avoid immediate liquidation when Trader choses maximal leverage.
// Specified in the WAD format, 0 < maintenanceBuffer < 1e18
uint256 public override maintenanceBuffer;
// Mapping from asset A to mapping of asset B with the oracleTolerableLimit
mapping(address => mapping(address => uint256)) internal oracleTolerableLimits;
uint256 public oracleTolerableLimitMultiplier;
PositionLibrary.Position[] internal positions;
uint256 public override positionsId;
// mapping from trader address to the position ids array
mapping(address => uint256[]) public override traderPositionIds;
// mapping from bucket address to the position ids array
mapping(address => uint256[]) public override bucketPositionIds;
// mapping from position to close conditions
mapping(uint256 => LimitOrderLibrary.Condition[]) internal closeConditions;
IAccessControl public override registry;
ITraderBalanceVault public override traderBalanceVault;
IPrimexDNSV3 public override primexDNS;
IPriceOracleV2 public override priceOracle;
IKeeperRewardDistributorV3 public override keeperRewardDistributor;
ISpotTradingRewardDistributorV2 public override spotTradingRewardDistributor;
// minimum position size allowed
uint256 public override minPositionSize;
// ERC20 token for minimum position size
address public override minPositionAsset;
// mapping from positionId to the index in the positions array
mapping(uint256 => uint256) internal positionIndexes;
// mapping from positionId to the index in the traderPositionIds[trader] array
//NOTE: positionId is unique for all traders hence we can put everything in one mapping
mapping(uint256 => uint256) internal traderPositionIndexes;
// mapping from positionId to the index in the bucketPositionIds[bucket] array
mapping(uint256 => uint256) internal bucketPositionIndexes;
IWhiteBlackList internal whiteBlackList;
}
abstract contract PositionManagerStorageV2 is IPositionManagerStorageV2, PositionManagerStorage {
address public override positionManagerExtension;
}// (c) 2024 Primex.finance
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.18;
import {IPriceOracleStorage, IPriceOracleStorageV3} from "./IPriceOracleStorage.sol";
interface IPriceOracleV2 is IPriceOracleStorageV3 {
event ChainlinkPriceFeedUpdated(address indexed token, address indexed priceFeed);
event PairPriceDropChanged(address indexed assetA, address indexed assetB, uint256 pairPriceDrop);
event PriceFeedUpdated(address indexed assetA, address indexed assetB, address indexed priceFeed);
event PriceDropFeedUpdated(address indexed assetA, address indexed assetB, address indexed priceDropFeed);
event GasPriceFeedChanged(address priceFeed);
event PythPairIdUpdated(address indexed token, bytes32 indexed priceFeedId);
event Univ3OracleUpdated(uint256 indexed oracleType, address indexed oracle);
event TimeToleranceUpdated(uint256 timeTolerance);
event SupraDataFeedUpdated(address indexed tokenA, address indexed tokenB, uint256 id);
event Univ3TrustedPairUpdated(
uint256 indexed oracleType,
address indexed tokenA,
address indexed tokenB,
bool isTrusted
);
struct UpdateUniv3TrustedPairParams {
uint256 oracleType;
address tokenA;
address tokenB;
bool isTrusted;
}
enum UpdatePullOracle {
Pyth,
Supra
}
struct UpdateSupraDataFeedParams {
address tokenA;
address tokenB;
SupraDataFeedId feedData;
}
/**
* @param assetA The address of the first asset in the pair.
* @param assetB The address of the second asset in the pair.
* @param priceDropFeed The chain link priceDrop feed address for the pair assetA/assetB
*/
struct UpdatePriceDropFeedsParams {
address assetA;
address assetB;
address priceDropFeed;
}
/**
* @param _registry The address of PrimexRegistry contract
* @param _eth Weth address if eth isn't native token of network. Otherwise set to zero address.
* @param _usdt Address of the USDT token
* @param _treasury Address of the Treasury
*/
function initialize(address _registry, address _eth, address _usdt, address _treasury) external;
/**
* @notice Function to set (change) the pair priceDrop of the trading assets
* @dev Only callable by the SMALL_TIMELOCK_ADMIN.
* @param _assetA The address of position asset
* @param _assetB The address of borrowed asset
* @param _pairPriceDrop The pair priceDrop (in wad)
*/
function setPairPriceDrop(address _assetA, address _assetB, uint256 _pairPriceDrop) external;
/**
* @notice Increases the priceDrop of a pair of assets in the system.
* @dev Only callable by the EMERGENCY_ADMIN role.
* The _pairPriceDrop value must be greater than the current priceDrop value for the pair
* and less than the maximum allowed priceDrop (WadRayMath.WAD / 2).
* @param _assetA The address of position asset
* @param _assetB The address of borrowed asset
* @param _pairPriceDrop The new priceDrop value for the pair (in wad)
*/
function increasePairPriceDrop(address _assetA, address _assetB, uint256 _pairPriceDrop) external;
/**
* @notice Sets the gas price feed contract address.
* @dev Only callable by the MEDIUM_TIMELOCK_ADMIN role.
* @param priceFeed The address of the gas price feed contract.
*/
function setGasPriceFeed(address priceFeed) external;
/**
* @notice Updates the priceDrop feed for a specific pair of assets.
* @dev Add or update priceDrop feed for assets pair.
* Only callable by the MEDIUM_TIMELOCK_ADMIN role.
* @param _updateParams The array of the UpdatePriceDropFeedsParams structs
*/
function updatePriceDropFeeds(UpdatePriceDropFeedsParams[] calldata _updateParams) external;
/**
* @notice Updates the priceDrop feed for a specific pair of assets.
* @dev Add or update priceDrop feed for assets pair.
* Only callable by the MEDIUM_TIMELOCK_ADMIN role.
* @param assetA The address of the first asset in the pair.
* @param assetB The address of the second asset in the pair.
* @param priceDropFeed The chain link priceDrop feed address for the pair assetA/assetB
*/
function updatePriceDropFeed(address assetA, address assetB, address priceDropFeed) external;
/**
* @notice Retrieves the current gas price from the specified gas price feed.
* @return The current gas price.
*/
function getGasPrice() external view returns (int256);
/**
* @notice For a given asset pair retrieves the priceDrop rate which is the higher
* of the oracle pair priceDrop and the historical pair priceDrop.
* @param _assetA The address of asset A.
* @param _assetB The address of asset B.
* @return The priceDrop rate.
*/
function getPairPriceDrop(address _assetA, address _assetB) external view returns (uint256);
/**
* @notice Retrieves the priceDrop rate between two assets based on the oracle pair priceDrop.
* @param assetA The address of the first asset.
* @param assetB The address of the second asset.
* @return The priceDrop rate as a uint256 value.
*/
function getOraclePriceDrop(address assetA, address assetB) external view returns (uint256);
/**
* @notice Retreives a priceDrop feed address from the oraclePriceDropFeeds mapping
* @param assetA The address of the first asset in the pair.
* @param assetB The address of the second asset in the pair.
* @return priceDropFeed The address of the priceDrop feed associated with the asset pair.
*/
function getOraclePriceDropFeed(address assetA, address assetB) external view returns (address);
/**
* @notice Calculates exchange rate of one token to another according to the specific oracle route
* @param assetA The address of the first asset in the pair.
* @param assetB The address of the second asset in the pair.
* @param oracleData The list of oracles to use for price calculations
* @return exchangeRate for assetA/assetB in 10**18 decimality
*/
function getExchangeRate(
address assetA,
address assetB,
bytes calldata oracleData
) external payable returns (uint256);
/**
* @notice Sets or updates the Chainlink price feed for the list of tokens to usd.
* @dev Only callable by the SMALL_TIMELOCK_ADMIN role.
* @param _tokens Array of token addresses.
* @param _feeds Array of price feeds.
*/
function updateChainlinkPriceFeedsUsd(address[] calldata _tokens, address[] calldata _feeds) external;
/**
* @notice Sets or updates the Pyth pair ids for the list of tokens.
* @dev Only callable by the SMALL_TIMELOCK_ADMIN role.
* @param _tokens Array of token addresses.
* @param _priceFeedIds Array of pair ids.
*/
function updatePythPairId(address[] calldata _tokens, bytes32[] calldata _priceFeedIds) external;
/**
* @notice Sets or updates the Supra price feeds for the list of tokens.
* @dev Only callable by the SMALL_TIMELOCK_ADMIN role.
* @param _params Array of token pairs and Supra ids.
*/
function updateSupraDataFeed(UpdateSupraDataFeedParams[] calldata _params) external;
/**
* @notice Sets Uni v3-based TWAP price oracle contracts.
* @dev Only callable by the SMALL_TIMELOCK_ADMIN role.
* @param _oracleTypes Array of ids of TWAP contracts.
* @param _oracles Array of TWAP contract addresses.
*/
function updateUniv3TypeOracle(uint256[] calldata _oracleTypes, address[] calldata _oracles) external;
/**
* @notice Sets or updates the Supra price feeds for the list of tokens.
* @dev Only callable by the SMALL_TIMELOCK_ADMIN role.
* @param _updateParams Array of token pairs, their DEXs and new trusted status.
*/
function updateUniv3TrustedPair(UpdateUniv3TrustedPairParams[] calldata _updateParams) external;
/**
* @notice Sets the Pyth address
* @dev Only callable by the BIG_TIMELOCK_ADMIN role.
* @param _pyth the address of the Pyth oracle
*/
function setPyth(address _pyth) external;
/**
* @notice Sets the Supra pull oracle address
* @dev Only callable by the BIG_TIMELOCK_ADMIN role.
* @param _supraPullOracle the address of the Supra pull oracle
*/
function setSupraPullOracle(address _supraPullOracle) external;
/**
* @notice Sets the Supra storage address
* @dev Only callable by the BIG_TIMELOCK_ADMIN role.
* @param _supraStorageOracle the address of the Supra storage
*/
function setSupraStorageOracle(address _supraStorageOracle) external;
/**
* @notice Updates pull oracle data for passed oracle types
* @param _data An array of update data for passed oracles
* @param _pullOracleTypes An array of oracle types (Must conform to the UpdatePullOracle struct)
*/
function updatePullOracle(bytes[][] calldata _data, uint256[] calldata _pullOracleTypes) external payable;
/**
* @notice Sets the time tolerance
* @dev Only callable by the SMALL_TIMELOCK_ADMIN role.
* @param _timeTolerance Time tolerance in seconds
*/
function setTimeTolerance(uint256 _timeTolerance) external;
/**
* @notice Sets the usdt address
* @dev Only callable by the BIG_TIMELOCK_ADMIN role.
* @param _usdt the address of the USDT
*/
function setUSDT(address _usdt) external;
/**
* @notice Sets the treasury address
* @dev Only callable by the BIG_TIMELOCK_ADMIN role.
* @param _treasury the address of the treasury
*/
function setTreasury(address _treasury) external;
}
interface IPriceOracle is IPriceOracleStorage {
event PairPriceDropChanged(address indexed assetA, address indexed assetB, uint256 pairPriceDrop);
event PriceFeedUpdated(address indexed assetA, address indexed assetB, address indexed priceFeed);
event PriceDropFeedUpdated(address indexed assetA, address indexed assetB, address indexed priceDropFeed);
event GasPriceFeedChanged(address priceFeed);
/**
* @param _registry The address of PrimexRegistry contract
* @param _eth Weth address if eth isn't native token of network. Otherwise set to zero address.
*/
function initialize(address _registry, address _eth) external;
/**
* @notice Function to set (change) the pair priceDrop of the trading assets
* @dev Only callable by the SMALL_TIMELOCK_ADMIN.
* @param _assetA The address of position asset
* @param _assetB The address of borrowed asset
* @param _pairPriceDrop The pair priceDrop (in wad)
*/
function setPairPriceDrop(address _assetA, address _assetB, uint256 _pairPriceDrop) external;
/**
* @notice Increases the priceDrop of a pair of assets in the system.
* @dev Only callable by the EMERGENCY_ADMIN role.
* The _pairPriceDrop value must be greater than the current priceDrop value for the pair
* and less than the maximum allowed priceDrop (WadRayMath.WAD / 2).
* @param _assetA The address of position asset
* @param _assetB The address of borrowed asset
* @param _pairPriceDrop The new priceDrop value for the pair (in wad)
*/
function increasePairPriceDrop(address _assetA, address _assetB, uint256 _pairPriceDrop) external;
/**
* @notice Add or update price feed for assets pair. For only the admin role.
* @param assetA The first currency within the currency pair quotation (the base currency).
* @param assetB The second currency within the currency pair quotation (the quote currency).
* @param priceFeed The chain link price feed address for the pair assetA/assetB
*/
function updatePriceFeed(address assetA, address assetB, address priceFeed) external;
/**
* @notice Sets the gas price feed contract address.
* @dev Only callable by the MEDIUM_TIMELOCK_ADMIN role.
* @param priceFeed The address of the gas price feed contract.
*/
function setGasPriceFeed(address priceFeed) external;
/**
* @notice Updates the priceDrop feed for a specific pair of assets.
* @dev Add or update priceDrop feed for assets pair.
* Only callable by the MEDIUM_TIMELOCK_ADMIN role.
* @param assetA The address of the first asset in the pair.
* @param assetB The address of the second asset in the pair.
* @param priceDropFeed The chain link priceDrop feed address for the pair assetA/assetB
*/
function updatePriceDropFeed(address assetA, address assetB, address priceDropFeed) external;
/**
* @notice Requests two priceFeeds - assetA/ETH and assetB/ETH (or assetA/USD and assetB/USD).
* @dev If there is no price feed found, the code will return a message that no price feed found.
* @param baseAsset The first currency within the currency pair quotation (the base currency).
* @param quoteAsset The second currency within the currency pair quotation (the quote currency).
* @return A tuple of basePriceFeed and quotePriceFeed. The addresses of the price feed for the base asset and quote asset respectively.
*/
function getPriceFeedsPair(address baseAsset, address quoteAsset) external view returns (address, address);
/**
* @notice Requests priceFeed for the actual exchange rate for an assetA/assetB pair.
* @dev If no price feed for the pair found, USD and ETH are used as intermediate tokens.
* A price for assetA/assetB can be derived if two data feeds exist:
* assetA/ETH and assetB/ETH (or assetA/USD and assetB/USD).
* If there is no price feed found, the code will return a message that no price feed found.
* @param assetA The first currency within the currency pair quotation (the base currency).
* @param assetB The second currency within the currency pair quotation (the quote currency).
* @return exchangeRate for assetA/assetB in 10**18 decimality which will be recalucaled in PrimexPricingLibrary.
* @return direction of a pair as it stored in chainLinkPriceFeeds (i.e. returns 'true' for assetA/assetB, and 'false' for assetB/assetA).
* Throws if priceFeed wasn't found or priceFeed hasn't answer is 0.
*/
function getExchangeRate(address assetA, address assetB) external view returns (uint256, bool);
/**
* @notice Retrieves the direct price feed for the given asset pair.
* @param assetA The address of the first asset.
* @param assetB The address of the second asset.
* @return priceFeed The address of the direct price feed.
*/
function getDirectPriceFeed(address assetA, address assetB) external view returns (address);
/**
* @notice Retrieves the current gas price from the specified gas price feed.
* @return The current gas price.
*/
function getGasPrice() external view returns (int256);
/**
* @notice For a given asset pair retrieves the priceDrop rate which is the higher
* of the oracle pair priceDrop and the historical pair priceDrop.
* @param _assetA The address of asset A.
* @param _assetB The address of asset B.
* @return The priceDrop rate.
*/
function getPairPriceDrop(address _assetA, address _assetB) external view returns (uint256);
/**
* @notice Retrieves the priceDrop rate between two assets based on the oracle pair priceDrop.
* @param assetA The address of the first asset.
* @param assetB The address of the second asset.
* @return The priceDrop rate as a uint256 value.
*/
function getOraclePriceDrop(address assetA, address assetB) external view returns (uint256);
/**
* @notice Retreives a priceDrop feed address from the oraclePriceDropFeeds mapping
* @param assetA The address of the first asset in the pair.
* @param assetB The address of the second asset in the pair.
* @return priceDropFeed The address of the priceDrop feed associated with the asset pair.
*/
function getOraclePriceDropFeed(address assetA, address assetB) external view returns (address);
}// (c) 2024 Primex.finance
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.18;
import {IPyth} from "@pythnetwork/pyth-sdk-solidity/IPyth.sol";
import {ISupraOraclePull} from "../interfaces/ISupraOraclePull.sol";
import {ISupraSValueFeed} from "../interfaces/ISupraSValueFeed.sol";
interface IPriceOracleStorage {
function registry() external view returns (address);
function eth() external view returns (address);
function gasPriceFeed() external view returns (address);
function pairPriceDrops(address, address) external view returns (uint256);
}
interface IPriceOracleStorageV2 is IPriceOracleStorage {
enum OracleType {
Pyth,
Chainlink,
Uniswapv3,
Supra
}
struct OracleRoute {
address tokenTo;
OracleType oracleType;
bytes oracleData;
}
function pyth() external view returns (IPyth);
function timeTolerance() external view returns (uint256);
function chainlinkPriceFeedsUsd(address) external view returns (address);
function pythPairIds(address) external view returns (bytes32);
function univ3TypeOracles(uint256) external view returns (address);
function univ3TrustedPairs(uint256, address, address) external view returns (bool);
}
interface IPriceOracleStorageV3 is IPriceOracleStorageV2 {
struct SupraDataFeedId {
uint256 id;
bool initialize;
}
function supraPullOracle() external view returns (ISupraOraclePull);
function supraStorageOracle() external view returns (ISupraSValueFeed);
function supraDataFeedID(address, address) external view returns (uint256, bool);
function usdt() external view returns (address);
function treasury() external view returns (address);
}// (c) 2024 Primex.finance
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.18;
import {IPrimexDNSStorage, IPrimexDNSStorageV3} from "./IPrimexDNSStorage.sol";
interface IPrimexDNSV3 is IPrimexDNSStorageV3 {
event AddNewBucket(BucketData newBucketData);
event BucketDeprecated(address bucketAddress, uint256 delistingTime);
event AddNewDex(DexData newDexData);
event ConditionalManagerChanged(uint256 indexed cmType, address indexed cmAddress);
event PMXchanged(address indexed pmx);
event AavePoolChanged(address indexed aavePool);
event BucketActivated(address indexed bucketAddress);
event BucketFrozen(address indexed bucketAddress);
event DexAdapterChanged(address indexed newAdapterAddress);
event DexActivated(address indexed routerAddress);
event DexFrozen(address indexed routerAddress);
event ChangeProtocolFeeRate(FeeRateType indexed feeRateType, uint256 indexed feeRate);
event ChangeAverageGasPerAction(TradingOrderType indexed tradingOrderType, uint256 indexed averageGasPerAction);
event ChangeMaxProtocolFee(uint256 indexed maxProtocolFee);
event ChangeProtocolFeeCoefficient(uint256 indexed protocolFeeCoefficient);
event ChangeLiquidationGasAmount(uint256 indexed liquidationGasAmount);
event ChangePmxDiscountMultiplier(uint256 indexed pmxDiscountMultiplier);
event ChangeAdditionalGasSpent(uint256 indexed additionalGasSpent);
event ChangeGasPriceBuffer(uint256 indexed gasPriceBuffer);
event ChangeMinFeeRestrictions(CallingMethod indexed callingMethod, MinFeeRestrictions minFeeRestrictions);
event ChangeLeverageTolerance(uint256 leverageTolerance);
/**
* @param feeRateType The order type for which the rate is set
* @param feeRate Setting rate in WAD format (1 WAD = 100%)
*/
struct FeeRateParams {
FeeRateType feeRateType;
uint256 feeRate;
}
struct AverageGasPerActionParams {
TradingOrderType tradingOrderType;
uint256 averageGasPerAction;
}
/**
* @dev Params for initialize() function
* @param registry The address of the PrimexRegistry contract.
* @param pmx The address of the PMX token contract.
* @param treasury The address of the Treasury contract.
* @param delistingDelay The time (in seconds) between deprecation and delisting of a bucket.
* @param adminWithdrawalDelay The time (in seconds) between delisting of a bucket and an adminDeadline.
* @param feeRateParams An array of structs to set protocol fee rate on the corresponding
* @param averageGasPerActionParams An array of structs to set average amount of gas spent by Keeper on the corresponding action
* @param maxProtocolFee MaxProtocolFee that can be charged. Measured in NATIVE_CURRENCY
* @param liquidationGasAmount Average gas amount spent for a single liquidation, measured in wei.
* @param protocolFeeCoefficient Additional coefficient to calculate minProtocolFee, measured in wei.
* @param additionalGasSpent Gas that will be additionally spend after gasSpent calculation.
* @param pmxDiscountMultiplier Multiplier for PMX discount calculation
* @param gasPriceBuffer Multiplier which protects position from immediate liquidation after gas price changed
*/
struct InitParams {
address registry;
address pmx;
address treasury;
uint256 delistingDelay;
uint256 adminWithdrawalDelay;
FeeRateParams[] feeRateParams;
AverageGasPerActionParams[] averageGasPerActionParams;
uint256 maxProtocolFee;
uint256 liquidationGasAmount;
uint256 protocolFeeCoefficient;
uint256 additionalGasSpent;
uint256 pmxDiscountMultiplier;
uint256 gasPriceBuffer;
uint256 leverageTolerance;
}
/**
* @notice Initializes the contract with the specified parameters.
*/
function initialize(InitParams calldata _params) external;
/**
* @notice Deprecates a bucket.
* @dev This function is used to deprecate a bucket by changing its current status to "Deprecated".
* Only callable by the BIG_TIMELOCK_ADMIN role.
* @param _bucket The name of the bucket to deprecate.
* Emits a BucketDeprecated event with the bucket address and the delisting time.
*/
function deprecateBucket(string memory _bucket) external;
/**
* @notice This function is used to set the address of the Aave pool contract.
* @dev Only callable by the BIG_TIMELOCK_ADMIN role.
* @param _aavePool The address of the Aave pool contract to be set.
*/
function setAavePool(address _aavePool) external;
/**
* @notice Sets the address of the PMX token contract.
* @dev Only callable by the BIG_TIMELOCK_ADMIN role.
* @param _pmx The address of the PMX token contract.
*/
function setPMX(address _pmx) external;
/**
* @notice Activates a bucket by changing its status from inactive to active.
* @dev Only callable by the SMALL_TIMELOCK_ADMIN role.
* @param _bucket The bucket to activate.
*/
function activateBucket(string memory _bucket) external;
/**
* @notice Freezes a bucket, preventing further operations on it,
* by changing its status from active to inactive.
* @dev Only callable by the EMERGENCY_ADMIN role.
* @param _bucket The bucket to be frozen.
*/
function freezeBucket(string memory _bucket) external;
/**
* @notice Adds a new bucket.
* @dev Only callable by the SMALL_TIMELOCK_ADMIN role.
* @param _newBucket The address of the new bucket to be added.
* @param _pmxRewardAmount The amount of PMX tokens to be rewarded from the bucket.
* Emits a AddNewBucket event with the struct BucketData of the newly added bucket.
*/
function addBucket(address _newBucket, uint256 _pmxRewardAmount) external;
/**
* @notice Activates a DEX by changing flag isActive on to true.
* @dev Only callable by the MEDIUM_TIMELOCK_ADMIN role.
* @param _dex The name of the DEX to activate.
*/
function activateDEX(string memory _dex) external;
/**
* @notice Freezes a DEX by changing flag isActive to false.
* @dev Only callable by the MEDIUM_TIMELOCK_ADMIN role.
* @param _dex The name of the DEX to be frozen.
*/
function freezeDEX(string memory _dex) external;
/**
* @notice Adds a new DEX to the protocol.
* @dev Only callable by the MEDIUM_TIMELOCK_ADMIN role.
* @param _name The name of the DEX.
* @param _routerAddress The address of the DEX router.
*/
function addDEX(string memory _name, address _routerAddress) external;
/**
* @notice Sets the address of the DEX adapter.
* @dev Only callable by the BIG_TIMELOCK_ADMIN role.
* @param newAdapterAddress The address of the new DEX adapter.
*/
function setDexAdapter(address newAdapterAddress) external;
/**
* @notice Set min protocol fee restrictions for different calling method.
* @dev Only callable by the MEDIUM_TIMELOCK_ADMIN role.
*/
function setMinFeeRestrictions(
CallingMethod _callingMethod,
MinFeeRestrictions calldata _minFeeRestrictions
) external;
/**
* @dev The function to specify the address of conditional manager of some type
* 1 => LimitPriceCOM
* 2 => TakeProfitStopLossCCM
* @dev Only callable by the BIG_TIMELOCK_ADMIN role.
* @param _address Address to be set for a conditional manager
* @param _cmType The type of a conditional manager
*/
function setConditionalManager(uint256 _cmType, address _address) external;
/**
* @notice Retrieves the address of a bucket by its name.
* @param _name The name of the bucket.
* @return The address of the bucket.
*/
function getBucketAddress(string memory _name) external view returns (address);
/**
* @notice Retrieves the address of the DEX router based on the given DEX name.
* @param _name The name of the DEX.
* @return The address of the DEX router.
*/
function getDexAddress(string memory _name) external view returns (address);
/**
* @notice Retrieves the names of Dexes registered in the protocol.
* @return An array of strings containing the names of all Dexes.
*/
function getAllDexes() external view returns (string[] memory);
/**
* @notice Set the protocol fee rate for one type of order.
* @dev Only callable by the BIG_TIMELOCK_ADMIN role.
*/
function setProtocolFeeRate(FeeRateParams calldata _feeRateType) external;
/**
* @notice Set average gas amount of gas spent by Keeper on the corresponding action.
* @dev Only callable by the MEDIUM_TIMELOCK_ADMIN role.
*/
function setAverageGasPerAction(AverageGasPerActionParams calldata _averageGasPerActionParams) external;
/**
* @notice Set the max protocol fee.
* @dev Only callable by the BIG_TIMELOCK_ADMIN role.
* @param _maxProtocolFee The new max protocol fee.
*/
function setMaxProtocolFee(uint256 _maxProtocolFee) external;
/**
* @notice Set protocol fee coefficient. Used to calculate the minProtocol fee
* @dev Only callable by the BIG_TIMELOCK_ADMIN role.
*/
function setProtocolFeeCoefficient(uint256 _maxProtocolFee) external;
/**
* @notice Set liquidation gas amount (average gas amount spent for a single liquidation).
* @dev Only callable by the MEDIUM_TIMELOCK_ADMIN role.
*/
function setLiquidationGasAmount(uint256 _maxProtocolFee) external;
/**
* @notice Set pmxDiscountMultiplier.
* @dev Only callable by the SMALL_TIMELOCK_ADMIN role.
*/
function setPmxDiscountMultiplier(uint256 _pmxDiscountMultiplier) external;
/**
* @notice Set new additionalGas. Used to calculate the minProtocol fee
* @dev Only callable by the MEDIUM_TIMELOCK_ADMIN role.
*/
function setAdditionalGasSpent(uint256 _additionalGasSpent) external;
/**
* @notice Set new gasPriceBuffer.
* @dev Only callable by the MEDIUM_TIMELOCK_ADMIN role.
*/
function setGasPriceBuffer(uint256 _gasPriceBuffer) external;
/**
* @notice Set new leverageTolerance.
* @dev Only callable by the MEDIUM_TIMELOCK_ADMIN role.
*/
function setLeverageTolerance(uint256 _leverageTolerance) external;
/**
* @notice Retrieves pmx, treasury, feeRateType, maxProtocolFee, pmxDiscountMultiplier
*/
function getPrimexDNSParams(
FeeRateType _feeRateType
) external view returns (address, address, uint256, uint256, uint256);
/**
* @notice Retrieves liquidationGasAmount, protocolFeeCoefficient, additionalGasSpent, maxGasAmount and baseLength
*/
function getParamsForMinProtocolFee(
CallingMethod _callingMethod
) external view returns (uint256, uint256, uint256, uint256, uint256);
/**
* @notice Retrieves baseLength, averageGasPerAction, protocolFeeCoefficient and gasPriceBuffer
*/
function getParamsForMinPositionSize(
TradingOrderType _tradingOrderType
) external view returns (uint256, uint256, uint256, uint256);
/**
* @notice Retrieves baseLength for L2 chain payment model depending from tradingOrderType
*/
function getL1BaseLengthForTradingOrderType(TradingOrderType _tradingOrderType) external view returns (uint256);
}
interface IPrimexDNS is IPrimexDNSStorage {
event AddNewBucket(BucketData newBucketData);
event BucketDeprecated(address bucketAddress, uint256 delistingTime);
event AddNewDex(DexData newDexData);
event ChangeFeeRate(OrderType orderType, address token, uint256 rate);
event ConditionalManagerChanged(uint256 indexed cmType, address indexed cmAddress);
event PMXchanged(address indexed pmx);
event AavePoolChanged(address indexed aavePool);
event BucketActivated(address indexed bucketAddress);
event BucketFrozen(address indexed bucketAddress);
event DexAdapterChanged(address indexed newAdapterAddress);
event DexActivated(address indexed routerAddress);
event DexFrozen(address indexed routerAddress);
/**
* @param orderType The order type for which the rate is set
* @param feeToken The token address for which the rate is set
* @param rate Setting rate in WAD format (1 WAD = 100%)
*/
struct FeeRateParams {
OrderType orderType;
address feeToken;
uint256 rate;
}
/**
* @notice Initializes the contract with the specified parameters.
* @param _registry The address of the PrimexRegistry contract.
* @param _pmx The address of the PMX token contract.
* @param _treasury The address of the Treasury contract.
* @param _delistingDelay The time (in seconds) between deprecation and delisting of a bucket.
* @param _adminWithdrawalDelay The time (in seconds) between delisting of a bucket and an adminDeadline.
* @param _feeRateParams Initial fee params
*/
function initialize(
address _registry,
address _pmx,
address _treasury,
uint256 _delistingDelay,
uint256 _adminWithdrawalDelay,
FeeRateParams[] calldata _feeRateParams
) external;
/**
* @notice Deprecates a bucket.
* @dev This function is used to deprecate a bucket by changing its current status to "Deprecated".
* Only callable by the BIG_TIMELOCK_ADMIN role.
* @param _bucket The name of the bucket to deprecate.
* Emits a BucketDeprecated event with the bucket address and the delisting time.
*/
function deprecateBucket(string memory _bucket) external;
/**
* @notice This function is used to set the address of the Aave pool contract.
* @dev Only callable by the BIG_TIMELOCK_ADMIN role.
* @param _aavePool The address of the Aave pool contract to be set.
*/
function setAavePool(address _aavePool) external;
/**
* @notice Sets the protocol rate in PMX.
* @dev Only callable by the BIG_TIMELOCK_ADMIN role.
*/
function setFeeRate(FeeRateParams calldata _feeRateParams) external;
/**
* @notice Sets the address of the PMX token contract.
* @dev Only callable by the BIG_TIMELOCK_ADMIN role.
* @param _pmx The address of the PMX token contract.
*/
function setPMX(address _pmx) external;
/**
* @notice Activates a bucket by changing its status from inactive to active.
* @dev Only callable by the SMALL_TIMELOCK_ADMIN role.
* @param _bucket The bucket to activate.
*/
function activateBucket(string memory _bucket) external;
/**
* @notice Freezes a bucket, preventing further operations on it,
* by changing its status from active to inactive.
* @dev Only callable by the EMERGENCY_ADMIN role.
* @param _bucket The bucket to be frozen.
*/
function freezeBucket(string memory _bucket) external;
/**
* @notice Adds a new bucket.
* @dev Only callable by the BIG_TIMELOCK_ADMIN role.
* @param _newBucket The address of the new bucket to be added.
* @param _pmxRewardAmount The amount of PMX tokens to be rewarded from the bucket.
* Emits a AddNewBucket event with the struct BucketData of the newly added bucket.
*/
function addBucket(address _newBucket, uint256 _pmxRewardAmount) external;
/**
* @notice Activates a DEX by changing flag isActive on to true.
* @dev Only callable by the MEDIUM_TIMELOCK_ADMIN role.
* @param _dex The name of the DEX to activate.
*/
function activateDEX(string memory _dex) external;
/**
* @notice Freezes a DEX by changing flag isActive to false.
* @dev Only callable by the MEDIUM_TIMELOCK_ADMIN role.
* @param _dex The name of the DEX to be frozen.
*/
function freezeDEX(string memory _dex) external;
/**
* @notice Adds a new DEX to the protocol.
* @dev Only callable by the MEDIUM_TIMELOCK_ADMIN role.
* @param _name The name of the DEX.
* @param _routerAddress The address of the DEX router.
*/
function addDEX(string memory _name, address _routerAddress) external;
/**
* @notice Sets the address of the DEX adapter.
* @dev Only callable by the BIG_TIMELOCK_ADMIN role.
* @param newAdapterAddress The address of the new DEX adapter.
*/
function setDexAdapter(address newAdapterAddress) external;
/**
* @dev The function to specify the address of conditional manager of some type
* 1 => LimitPriceCOM
* 2 => TakeProfitStopLossCCM
* @param _address Address to be set for a conditional manager
* @param _cmType The type of a conditional manager
*/
function setConditionalManager(uint256 _cmType, address _address) external;
/**
* @notice Retrieves the address of a bucket by its name.
* @param _name The name of the bucket.
* @return The address of the bucket.
*/
function getBucketAddress(string memory _name) external view returns (address);
/**
* @notice Retrieves the address of the DEX router based on the given DEX name.
* @param _name The name of the DEX.
* @return The address of the DEX router.
*/
function getDexAddress(string memory _name) external view returns (address);
/**
* @notice Retrieves the names of Dexes registered in the protocol.
* @return An array of strings containing the names of all Dexes.
*/
function getAllDexes() external view returns (string[] memory);
}// (c) 2024 Primex.finance
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.18;
interface IPrimexDNSStorage {
enum Status {
Inactive,
Active,
Deprecated
}
enum OrderType {
MARKET_ORDER,
LIMIT_ORDER,
SWAP_MARKET_ORDER,
SWAP_LIMIT_ORDER
}
struct BucketData {
address bucketAddress;
Status currentStatus;
uint256 delistingDeadline;
// The deadline is for the admin to call Bucket.withdrawAfterDelisting().
uint256 adminDeadline;
}
struct DexData {
address routerAddress;
bool isActive;
}
struct AdapterData {
string[] dexes;
bool isAdded;
}
function registry() external view returns (address);
function delistingDelay() external view returns (uint256);
function adminWithdrawalDelay() external view returns (uint256);
function buckets(string memory) external view returns (address, Status, uint256, uint256);
function dexes(string memory) external view returns (address, bool);
function cmTypeToAddress(uint256 cmType) external view returns (address);
function dexAdapter() external view returns (address);
function pmx() external view returns (address);
function treasury() external view returns (address);
function aavePool() external view returns (address);
function feeRates(OrderType _orderType, address _token) external view returns (uint256);
}
interface IPrimexDNSStorageV2 is IPrimexDNSStorage {
struct FeeRestrictions {
uint256 minProtocolFee;
uint256 maxProtocolFee;
}
function feeRestrictions(
OrderType _orderType
) external view returns (uint256 minProtocolFee, uint256 maxProtocolFee);
}
interface IPrimexDNSStorageV3 is IPrimexDNSStorageV2 {
enum FeeRateType {
MarginPositionClosedByTrader,
SpotPositionClosedByTrader,
MarginPositionClosedByKeeper,
SpotPositionClosedByKeeper,
MarginLimitOrderExecuted,
SpotLimitOrderExecuted,
SwapLimitOrderExecuted,
SwapMarketOrder
}
enum TradingOrderType {
MarginMarketOrder,
SpotMarketOrder,
MarginLimitOrder,
MarginLimitOrderDepositInThirdAsset,
SpotLimitOrder,
SwapLimitOrder
}
enum CallingMethod {
OpenPositionByOrder,
ClosePositionByCondition
}
struct MinFeeRestrictions {
uint256 maxGasAmount;
uint256 baseLength;
}
function protocolFeeRates(FeeRateType _feeRateType) external view returns (uint256);
function averageGasPerAction(TradingOrderType _tradingOrderType) external view returns (uint256);
function minFeeRestrictions(
CallingMethod _callingMethod
) external view returns (uint256 maxGasAmount, uint256 baseLength);
function maxProtocolFee() external view returns (uint256);
function protocolFeeCoefficient() external view returns (uint256);
function liquidationGasAmount() external view returns (uint256);
function additionalGasSpent() external view returns (uint256);
function pmxDiscountMultiplier() external view returns (uint256);
function gasPriceBuffer() external view returns (uint256);
function leverageTolerance() external view returns (uint256);
}// (c) 2024 Primex.finance
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.18;
import {IERC20Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol";
import {IAccessControl} from "@openzeppelin/contracts/access/IAccessControl.sol";
import {IERC165Upgradeable} from "@openzeppelin/contracts-upgradeable/utils/introspection/IERC165Upgradeable.sol";
import {IPTokenStorage, IBucket, IBucketV3, IFeeExecutor, IERC20MetadataUpgradeable, IActivityRewardDistributor} from "./IPTokenStorage.sol";
interface IPToken is IPTokenStorage {
/**
* @dev Emitted after the mint action
* @param from The address performing the mint
* @param value The amount being
*/
event Mint(address indexed from, uint256 value);
/**
* @dev Emitted after pTokens are burned
* @param from The owner of the aTokens, getting them burned
* @param value The amount being burned
*/
event Burn(address indexed from, uint256 value);
/**
* @dev Emitted during the transfer action
* @param from The user whose tokens are being transferred
* @param to The recipient
* @param amount The amount being transferred
* @param index The new liquidity index of the reserve
*/
event BalanceTransfer(address indexed from, address indexed to, uint256 amount, uint256 index);
event LockDeposit(address indexed user, uint256 indexed id, uint256 deadline, uint256 amount);
event UnlockDeposit(address indexed user, uint256 indexed id);
/**
* @dev contract initializer
* @param _name The name of the ERC20 token.
* @param _symbol The symbol of the ERC20 token.
* @param _decimals The number of decimals for the ERC20 token.
* @param _bucketsFactory Address of the buckets factory that will call the setBucket fucntion
*/
function initialize(string memory _name, string memory _symbol, uint8 _decimals, address _bucketsFactory) external;
/**
* @dev Sets the bucket for the contract.
* @param _bucket The address of the bucket to set.
*/
function setBucket(IBucket _bucket) external;
/**
* @dev Sets the InterestIncreaser for current PToken.
* @param _interestIncreaser The interest increaser address.
*/
function setInterestIncreaser(IFeeExecutor _interestIncreaser) external;
/**
* @dev Sets the lender reward distributor contract address.
* @param _lenderRewardDistributor The address of the lender reward distributor contract.
*/
function setLenderRewardDistributor(IActivityRewardDistributor _lenderRewardDistributor) external;
/**
* @notice Locks a deposit for a specified user.
* @param _user The address of the user for whom the deposit is being locked.
* @param _amount The amount to be locked as a deposit.
* @param _duration The duration for which the deposit will be locked.
* @dev This function can only be called externally and overrides the corresponding function in the parent contract.
* @dev The user must not be blacklisted.
*/
function lockDeposit(address _user, uint256 _amount, uint256 _duration) external;
/**
* @dev Unlocks a specific deposit.
* @param _depositId The ID of the deposit to be unlocked.
*/
function unlockDeposit(uint256 _depositId) external;
/**
* @dev Mints `amount` pTokens to `user`
* @param _user The address receiving the minted tokens
* @param _amount The amount of tokens getting minted
* @param _index The current liquidityIndex
* @return Minted amount of PTokens
*/
function mint(address _user, uint256 _amount, uint256 _index) external returns (uint256);
/**
* @dev Mints pTokens to the reserve address
* Compared to the normal mint, we don't revert when the amountScaled is equal to the zero. Additional checks were also removed
* Only callable by the Bucket
* @param _reserve The address of the reserve
* @param _amount The amount of tokens getting minted
* @param _index The current liquidityIndex
*/
function mintToReserve(address _reserve, uint256 _amount, uint256 _index) external;
/**
* @dev Burns pTokens from `user` and sends the equivalent amount of underlying to `receiverOfUnderlying`
* @param _user The owner of the pTokens, getting them burned
* @param _amount The amount of underlying token being returned to receiver
* @param _index The current liquidityIndex
* @return Burned amount of PTokens
*/
function burn(address _user, uint256 _amount, uint256 _index) external returns (uint256);
/**
* @dev Returns the scaled balance of the user.
* @param _user The owner of pToken
* @return The scaled balances of the user
*/
function scaledBalanceOf(address _user) external view returns (uint256);
/**
* @dev Returns available balance of the user.
* @param _user The owner of pToken
* @return The available balance of the user
*/
function availableBalanceOf(address _user) external view returns (uint256);
/**
* @dev Returns locked deposits and balance of user
* @param _user The owner of locked deposits
* @return Structure with deposits and total locked balance of user
*/
function getUserLockedBalance(address _user) external view returns (LockedBalance memory);
/**
* @dev Returns the scaled total supply of pToken.
* @return The scaled total supply of the pToken.
*/
function scaledTotalSupply() external view returns (uint256);
/**
* @dev Function to get a deposit index in user's deposit array.
* @param id Deposit id.
* @return index Deposit index in user's 'deposit' array.
*/
function getDepositIndexById(uint256 id) external returns (uint256 index);
}// (c) 2024 Primex.finance
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.18;
import {IERC20MetadataUpgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/IERC20MetadataUpgradeable.sol";
import {IBucket, IBucketV3} from "../Bucket/IBucket.sol";
import {IFeeExecutor} from "../BonusExecutor/IFeeExecutor.sol";
import {IActivityRewardDistributor} from "../ActivityRewardDistributor/IActivityRewardDistributor.sol";
interface IPTokenStorage is IERC20MetadataUpgradeable {
struct Deposit {
uint256 lockedBalance;
uint256 deadline;
uint256 id;
}
struct LockedBalance {
uint256 totalLockedBalance;
Deposit[] deposits;
}
function bucket() external view returns (IBucketV3);
function interestIncreaser() external view returns (IFeeExecutor);
function lenderRewardDistributor() external view returns (IActivityRewardDistributor);
}// (c) 2024 Primex.finance
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.18;
import {IBucketV3} from "../Bucket/IBucket.sol";
import {IPrimexDNSV3} from "../PrimexDNS/IPrimexDNS.sol";
import {IReserveStorage} from "./IReserveStorage.sol";
import {IPausable} from "../interfaces/IPausable.sol";
interface IReserve is IReserveStorage, IPausable {
event BurnAmountCalculated(uint256 burnAmount);
event TransferRestrictionsChanged(address indexed pToken, TransferRestrictions newTransferRestrictions);
/**
* @dev contract initializer
* @param dns The address of PrimexDNS contract
* @param registry The address of Registry contract
*/
function initialize(IPrimexDNSV3 dns, address registry) external;
/**
* @dev Burns the permanent loss amount (presented in pTokens) from the Reserve for a particular bucket
* @param bucket The address of a bucket
* Emits BurnAmountCalculated(burnAmount) event
*/
function paybackPermanentLoss(IBucketV3 bucket) external;
/**
* @dev Transfers some bonus in pTokens to receiver from Reserve
* Can be called by executor only
* @param _bucketName The bucket where the msg.sender should be a fee decreaser (for debtToken) or
* interest increaser (for pToken)
* @param _to The receiver of bonus pTokens
* @param _amount The amount of bonus pTokens to transfer
*/
function payBonus(string memory _bucketName, address _to, uint256 _amount) external;
/**
* @dev Function to transfer tokens to the Treasury. Only MEDIUM_TIMELOCK_ADMIN can call it.
* @param bucket The bucket from which to transfer pTokens
* @param amount The amount of pTokens to transfer
*/
function transferToTreasury(address bucket, uint256 amount) external;
/**
* @dev Function to set transfer restrictions for a token.
* @notice Only MEDIUM_TIMELOCK_ADMIN can call it.
* @param pToken pToken to set restrictions for
* @param transferRestrictions Min amount to be left in the Reserve
*/
function setTransferRestrictions(address pToken, TransferRestrictions calldata transferRestrictions) external;
}// (c) 2024 Primex.finance
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.18;
interface IReserveStorage {
struct TransferRestrictions {
uint256 minAmountToBeLeft;
uint256 minPercentOfTotalSupplyToBeLeft;
}
event TransferFromReserve(address pToken, address to, uint256 amount);
function transferRestrictions(address pToken) external view returns (uint256, uint256);
}// (c) 2024 Primex.finance
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.18;
import {ISpotTradingRewardDistributorStorage} from "./ISpotTradingRewardDistributorStorage.sol";
import {IPausable} from "../interfaces/IPausable.sol";
interface ISpotTradingRewardDistributorV2 is ISpotTradingRewardDistributorStorage, IPausable {
event SpotTradingClaimReward(address indexed trader, uint256 amount);
event RewardPerPeriodDecreased(uint256 indexed rewardPerPeriod);
event TopUpUndistributedPmxBalance(uint256 indexed amount);
event RewardPerPeriodChanged(uint256 indexed rewardPerPeriod);
event PmxWithdrawn(uint256 indexed amount);
/**
* @dev contract initializer
* @param registry The address of Registry contract
* @param periodDuration The duration of a reward period
* @param priceOracle The address of PriceOracle contract
* @param pmx The address of PMX token
* @param traderBalanceVault The address of TraderBalanceVault contract
* @param treasury The address of Treasury contract
*/
function initialize(
address registry,
uint256 periodDuration,
address priceOracle,
address pmx,
address payable traderBalanceVault,
address treasury
) external;
/**
* @dev Function to update spot trader activity. Only PM_ROLE can call it.
* @param trader Address of a trader
* @param positionAsset Address of a position asset
* @param positionAmount Amount of a position asset
*/
function updateTraderActivity(
address trader,
address positionAsset,
uint256 positionAmount,
bytes calldata positionUsdOracleDataoracleData
) external;
/**
* @dev Function to claim reward for spot trading activity.
* Transfer rewards on the balance in traderBalanceVault
* Emits SpotTradingClaimReward(address trader, uint256 amount)
*/
function claimReward() external;
/**
* @dev Function to set new reward per period. Only MEDIUM_TIMELOCK_ADMIN can call it.
* @param rewardPerPeriod New value for reward per period
*/
function setRewardPerPeriod(uint256 rewardPerPeriod) external;
/**
* @dev Function to decrease reward per period. Only EMERGENCY_ADMIN can call it.
* @param _rewardPerPeriod New value for reward per period, must be less than the current value
*/
function decreaseRewardPerPeriod(uint256 _rewardPerPeriod) external;
/**
* @dev Function to topUp the contract PMX balance
* @param amount PMX amount to add to the contract balance
*/
function topUpUndistributedPmxBalance(uint256 amount) external;
/**
* @dev Function to withdraw PMX from the contract to treasury
* @dev Only BIG_TIMELOCK_ADMIN can call it.
* @param amount Amount of PMX to withdraw from the contract
*/
function withdrawPmx(uint256 amount) external;
/**
* @dev Function to get SpotTraderActivity
* @param periodNumber Period number
* @param traderAddress Address of a trader
* @return A struct with activity and hasClaimed members
*/
function getSpotTraderActivity(uint256 periodNumber, address traderAddress) external view returns (uint256);
/**
* @dev Get information for the period corresponding to the given timestamp
* @param timestamp The timestamp to get information about
* @return totalReward Total reward for the corresponding period
* @return totalActivity Total activity for the corresponding period
*/
function getPeriodInfo(uint256 timestamp) external view returns (uint256, uint256);
/**
* @dev Function to get an array of period numbers when trader had any activity
* @param trader Address of a trader
* @return An array of period numbers with trader activity
*/
function getPeriodsWithTraderActivity(address trader) external view returns (uint256[] memory);
/**
* @dev Function to calculate trader's reward for her activities during periods
* @param trader Address of a trader
* @return reward Amount of reward
* @return currentPeriod The current period
*/
function calculateReward(address trader) external view returns (uint256 reward, uint256 currentPeriod);
}// (c) 2024 Primex.finance
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.18;
interface ISpotTradingRewardDistributorStorage {
struct PeriodInfo {
uint256 totalReward;
// map trader address to her activity
mapping(address => uint256) traderActivity;
uint256 totalActivity;
}
function registry() external view returns (address);
function dns() external view returns (address);
function periodDuration() external view returns (uint256);
function initialPeriodTimestamp() external view returns (uint256);
function rewardPerPeriod() external view returns (uint256);
function pmx() external view returns (address);
function priceOracle() external view returns (address);
function treasury() external view returns (address);
function traderBalanceVault() external view returns (address payable);
function undistributedPMX() external view returns (uint256);
function periods(uint256 periodNumber) external view returns (uint256, uint256);
}// (c) 2024 Primex.finance
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.18;
import {PrimexPricingLibrary} from "../libraries/PrimexPricingLibrary.sol";
import {IPrimexDNSV3, IPrimexDNSStorageV3} from "../PrimexDNS/IPrimexDNS.sol";
import {IPausable} from "../interfaces/IPausable.sol";
interface ISwapManager is IPausable {
event SpotSwap(
address indexed trader,
address indexed receiver,
address tokenA,
address tokenB,
uint256 amountSold,
uint256 amountBought
);
event PaidProtocolFee(
address indexed trader,
address indexed boughtAsset,
IPrimexDNSStorageV3.FeeRateType indexed feeRateType,
uint256 feeInPositionAsset,
uint256 feeInPmx
);
/**
* @param tokenA The address of the asset to be swapped from.
* @param tokenB The address of the asset to be received in the swap.
* @param amountTokenA The amount of tokenA to be swapped.
* @param amountOutMin The minimum amount of tokenB expected to receive.
* @param routes An array of PrimexPricingLibrary.Route structs representing the routes for the swap.
* @param receiver The address where the swapped tokens will be received.
* @param deadline The deadline for the swap transaction.
* @param isSwapFromWallet A flag indicating whether the swap is perfomed from a wallet or a protocol balance.
* @param isSwapToWallet A flag indicating whether the swapped tokens will be sent to a wallet or a protocol balance.
* @param isSwapFeeInPmx A flag indicating whether the swap fee is paid in PMX or in native token.
*/
struct SwapParams {
address tokenA;
address tokenB;
uint256 amountTokenA;
uint256 amountOutMin;
PrimexPricingLibrary.MegaRoute[] megaRoutes;
address receiver;
uint256 deadline;
bool isSwapFromWallet;
bool isSwapToWallet;
bool isSwapFeeInPmx;
bytes tokenAtokenBOracleData;
bytes pmxPositionAssetOracleData;
bytes nativePositionAssetOracleData;
bytes[][] pullOracleData;
uint256[] pullOracleTypes;
}
/**
* @param depositAsset The address of the deposited asset.
* @param positionAsset The address of the position asset.
* @param depositAmount Amount of tokens in a deposit asset.
* @param megaRoutes An array of PrimexPricingLibrary.Route structs representing the routes for the swap.
* @param trader The trader address, who has created the order.
* @param deadline The deadline for the swap transaction.
* @param feeToken An asset in which the fee will be paid. At this point it could be the pmx, the epmx or a positionAsset
* @param keeperRewardDistributor The address of KeeperRewardDistributor contract.
* @param gasSpent Gas spent on executing transaction.
*/
struct SwapInLimitOrderParams {
address depositAsset;
address positionAsset;
uint256 depositAmount;
PrimexPricingLibrary.MegaRoute[] megaRoutes;
address trader;
uint256 deadline;
address feeToken;
address keeperRewardDistributor;
uint256 gasSpent;
bytes depositPositionAssetOracleData;
bytes pmxPositionAssetOracleData;
bytes nativePositionAssetOracleData;
}
/**
* @notice Initializes the contract with the specified parameters.
* @param _registry The address of the PrimexRegistry contract.
*/
function initialize(address _registry) external;
/**
* @notice Re-initializes the contract with the specified parameters.
* @dev Only BIG_TIMELOCK_ADMIN can call it.
* @param _primexDNS The address of the PrimexDNS contract.
* @param _traderBalanceVault The address of the TraderBalanceVault contract.
* @param _priceOracle The address of the PriceOracle contract.
* @param _whiteBlackList The address of the WhiteBlackList contract.
*/
function initializeAfterUpgrade(
address _primexDNS,
address payable _traderBalanceVault,
address _priceOracle,
address _whiteBlackList
) external;
/**
* @notice Executes a swap on dexes defined in routes
* @param params The SwapParams struct containing the details of the swap transaction.
* @param maximumOracleTolerableLimit The maximum tolerable limit in WAD format (1 WAD = 100%)
* @param needOracleTolerableLimitCheck Flag indicating whether to perform an oracle tolerable limit check.
* @return The resulting amount after the swap.
*/
function swap(
SwapParams calldata params,
uint256 maximumOracleTolerableLimit,
bool needOracleTolerableLimitCheck
) external payable returns (uint256);
/**
* @notice Executes a swap on dexes defined in routes
* @dev Only callable by the LOM_ROLE role.
* @param params The SwapInLimitOrderParams struct containing the details of the swap transaction.
* @param maximumOracleTolerableLimit The maximum tolerable limit in WAD format (1 WAD = 100%)
* @return The resulting amount after the swap and feeInPositionAsset.
*/
function swapInLimitOrder(
SwapInLimitOrderParams calldata params,
uint256 maximumOracleTolerableLimit
) external returns (uint256, uint256);
}// (c) 2024 Primex.finance
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.18;
import {ITraderBalanceVaultStorage} from "./ITraderBalanceVaultStorage.sol";
import {IPausable} from "../interfaces/IPausable.sol";
interface ITraderBalanceVault is ITraderBalanceVaultStorage, IPausable {
/**
* Types of way to open a position or order
*/
enum OpenType {
OPEN_BY_ORDER,
OPEN,
CREATE_LIMIT_ORDER
}
/**
* @param trader The trader, who opens margin deal
* @param depositReceiver the address to which the deposit is transferred when blocked.
* This happens because the trader's deposit is involved in the position
* @param borrowedAsset The token to lock for deal in a borrowed asset
* @param depositAsset The token is a deposit asset
* (it is blocked when creating a limit order
* For others, the operations is transferred to the account of the receiver of the deposit and is swapped )
* @param depositAmount Amount of tokens in a deposit asset
* @param depositInBorrowedAmount Amount of tokens to lock for deal in a borrowed asset
* @param openType Corresponds to the purpose of locking
*/
struct LockAssetParams {
address trader;
address depositReceiver;
address depositAsset;
uint256 depositAmount;
OpenType openType;
}
/**
* @param trader The trader who opened the position
* @param receiver The receiver of the rest of trader deposit.
* @param asset Borrowed asset of the position being closed (the need for accrual of profit).
* @param unlockAmount The amount of unlocked collateral for deal
* @param returnToTrader The returned to trader amount when position was closed.
*/
struct UnlockAssetParams {
address trader;
address receiver;
address asset;
uint256 amount;
}
/**
* @param traders An array of traders for which available balance should be increased
* @param amounts An array of amounts corresponding to traders' addresses that should be added to their available balances
* @param asset Asset address which amount will be increased
* @param length The amount of traders in an array
*/
struct BatchTopUpAvailableBalanceParams {
address[] traders;
uint256[] amounts;
address asset;
uint256 length;
}
event Deposit(address indexed depositer, address indexed asset, uint256 amount);
event Withdraw(address indexed withdrawer, address asset, uint256 amount);
/**
* @dev contract initializer
* @param _registry The address of Registry contract
* @param _whiteBlackList The address of WhiteBlackList contract
*/
function initialize(address _registry, address _whiteBlackList) external;
receive() external payable;
/**
* @dev Deposits trader collateral for margin deal
* @param _asset The collateral asset for deal
* @param _amount The amount of '_asset' to deposit
*/
function deposit(address _asset, uint256 _amount) external payable;
/**
* @dev Withdraws the rest of trader's deposit after closing deal
* @param _asset The collateral asset for withdraw
* @param _amount The amount of '_asset' to withdraw
*/
function withdraw(address _asset, uint256 _amount) external;
/**
* @dev Traders lock their collateral for the limit order.
* @param _trader The owner of collateral
* @param _asset The collateral asset for deal
* @param _amount The amount of '_asset' to deposit
*/
function increaseLockedBalance(address _trader, address _asset, uint256 _amount) external payable;
/**
* @dev Locks deposited trader's assets as collateral for orders.
* Decreases the available balance when opening position.
* Transfers deposited amount to the deposit receiver.
* @param _params parameters necessary to lock asset
*/
function useTraderAssets(LockAssetParams calldata _params) external;
/**
* @dev Unlocks trader's collateral when open position by order or update deposit.
* @param _params parameters necessary to unlock asset
*/
function unlockAsset(UnlockAssetParams calldata _params) external;
/**
* The function to increase available balance for several traders
* @param _params A struct containing BatchTopUpAvailableBalanceParams
*/
function batchTopUpAvailableBalance(BatchTopUpAvailableBalanceParams calldata _params) external;
/**
* Withdraws an asset amount from an asset holder to a receiver
* @param _from Withdraw from address
* @param _to Withdraw to address
* @param _asset Address of an asset
* @param _amount Amount of an asset
* @param fromLocked True if withdraw from locked balance
*/
function withdrawFrom(address _from, address _to, address _asset, uint256 _amount, bool fromLocked) external;
/**
* Increases available balance of a receiver in the protocol
* @param receiver The address of an asset receiver
* @param asset The asset address for which available balance will be increased
* @param amount The amount of an asset
*/
function topUpAvailableBalance(address receiver, address asset, uint256 amount) external payable;
}// (c) 2024 Primex.finance
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.18;
interface ITraderBalanceVaultStorage {
struct TraderBalance {
uint256 availableBalance;
uint256 lockedBalance;
}
function registry() external view returns (address);
/**
*
* @param trader Trader's address
* @param asset Asset address
* @return availableBalance availableBalance
* @return lockedBalance lockedBalance
*/
function balances(
address trader,
address asset
) external view returns (uint256 availableBalance, uint256 lockedBalance);
}// (c) 2024 Primex.finance
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.18;
interface IWhiteBlackList {
enum AccessType {
UNLISTED,
WHITELISTED,
BLACKLISTED
}
event WhitelistedAddressAdded(address indexed addr);
event WhitelistedAddressRemoved(address indexed addr);
event BlacklistedAddressAdded(address indexed addr);
event BlacklistedAddressRemoved(address indexed addr);
function addAddressToWhitelist(address _address) external;
function addAddressesToWhitelist(address[] calldata _addresses) external;
function removeAddressFromWhitelist(address _address) external;
function removeAddressesFromWhitelist(address[] calldata _addresses) external;
function addAddressToBlacklist(address _address) external;
function addAddressesToBlacklist(address[] calldata _addresses) external;
function removeAddressFromBlacklist(address _address) external;
function removeAddressesFromBlacklist(address[] calldata _addresses) external;
function getAccessType(address _address) external view returns (AccessType);
function isBlackListed(address _address) external view returns (bool);
function registry() external view returns (address);
}{
"optimizer": {
"enabled": true,
"runs": 200
},
"viaIR": true,
"evmVersion": "paris",
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"metadata": {
"useLiteralContent": true
},
"libraries": {
"contracts/libraries/PositionLibrary.sol": {
"PositionLibrary": "0x4a63105e7d0df8fc25250568fcb6f39738850496"
},
"contracts/libraries/PrimexPricingLibrary.sol": {
"PrimexPricingLibrary": "0x5a1235cd7f233b024273dcace98f966e0e7c7918"
}
}
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[],"name":"DS_MATH_ADD_OVERFLOW","type":"error"},{"inputs":[],"name":"DS_MATH_MUL_OVERFLOW","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"positionId","type":"uint256"},{"indexed":true,"internalType":"address","name":"trader","type":"address"},{"indexed":false,"internalType":"uint256","name":"depositDelta","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"scaledDebtAmount","type":"uint256"}],"name":"DecreaseDeposit","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint8","name":"version","type":"uint8"}],"name":"Initialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_keeperRewardDistributor","type":"address"}],"name":"KeeperRewardDistributorChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"maintenanceBuffer","type":"uint256"}],"name":"MaintenanceBufferChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"positionId","type":"uint256"},{"indexed":true,"internalType":"address","name":"trader","type":"address"},{"indexed":true,"internalType":"address","name":"openedBy","type":"address"},{"components":[{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"uint256","name":"scaledDebtAmount","type":"uint256"},{"internalType":"contract IBucketV3","name":"bucket","type":"address"},{"internalType":"address","name":"soldAsset","type":"address"},{"internalType":"uint256","name":"depositAmountInSoldAsset","type":"uint256"},{"internalType":"address","name":"positionAsset","type":"address"},{"internalType":"uint256","name":"positionAmount","type":"uint256"},{"internalType":"address","name":"trader","type":"address"},{"internalType":"uint256","name":"openBorrowIndex","type":"uint256"},{"internalType":"uint256","name":"createdAt","type":"uint256"},{"internalType":"uint256","name":"updatedConditionsAt","type":"uint256"},{"internalType":"bytes","name":"extraParams","type":"bytes"}],"indexed":false,"internalType":"struct PositionLibrary.Position","name":"position","type":"tuple"},{"indexed":false,"internalType":"uint256","name":"entryPrice","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"leverage","type":"uint256"},{"components":[{"internalType":"uint256","name":"managerType","type":"uint256"},{"internalType":"bytes","name":"params","type":"bytes"}],"indexed":false,"internalType":"struct LimitOrderLibrary.Condition[]","name":"closeConditions","type":"tuple[]"}],"name":"OpenPosition","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"newMultiplier","type":"uint256"}],"name":"OracleTolerableLimitMultiplierChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"positionId","type":"uint256"},{"indexed":true,"internalType":"address","name":"trader","type":"address"},{"indexed":false,"internalType":"address","name":"paymentAsset","type":"address"},{"indexed":true,"internalType":"enum IPrimexDNSStorageV3.FeeRateType","name":"feeRateType","type":"uint8"},{"indexed":false,"internalType":"uint256","name":"feeInPaymentAsset","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"feeInPmx","type":"uint256"}],"name":"PaidProtocolFee","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"positionId","type":"uint256"},{"indexed":true,"internalType":"address","name":"trader","type":"address"},{"indexed":false,"internalType":"address","name":"bucketAddress","type":"address"},{"indexed":false,"internalType":"address","name":"soldAsset","type":"address"},{"indexed":false,"internalType":"address","name":"positionAsset","type":"address"},{"indexed":false,"internalType":"uint256","name":"decreasePositionAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"depositedAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"scaledDebtAmount","type":"uint256"},{"indexed":false,"internalType":"int256","name":"profit","type":"int256"},{"indexed":false,"internalType":"uint256","name":"positionDebt","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amountOut","type":"uint256"}],"name":"PartialClosePosition","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Paused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"securityBuffer","type":"uint256"}],"name":"SecurityBufferChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"oracleTolerableLimit","type":"uint256"}],"name":"SetDefaultOracleTolerableLimit","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"token0","type":"address"},{"indexed":false,"internalType":"address","name":"token1","type":"address"},{"indexed":false,"internalType":"uint256","name":"amountInToken0","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amountInToken1","type":"uint256"}],"name":"SetMaxPositionSize","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"assetA","type":"address"},{"indexed":true,"internalType":"address","name":"assetB","type":"address"},{"indexed":false,"internalType":"uint256","name":"oracleTolerableLimit","type":"uint256"}],"name":"SetOracleTolerableLimit","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Unpaused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"positionId","type":"uint256"},{"indexed":true,"internalType":"address","name":"trader","type":"address"},{"components":[{"internalType":"uint256","name":"managerType","type":"uint256"},{"internalType":"bytes","name":"params","type":"bytes"}],"indexed":false,"internalType":"struct LimitOrderLibrary.Condition[]","name":"closeConditions","type":"tuple[]"}],"name":"UpdatePositionConditions","type":"event"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"bucketPositionIds","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_positionId","type":"uint256"},{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"bytes","name":"_positionSoldAssetOracleData","type":"bytes"},{"internalType":"bytes","name":"_nativeSoldAssetOracleData","type":"bytes"},{"internalType":"bytes[][]","name":"_pullOracleData","type":"bytes[][]"},{"internalType":"uint256[]","name":"_pullOracleTypes","type":"uint256[]"}],"name":"decreaseDeposit","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"defaultOracleTolerableLimit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"assetA","type":"address"},{"internalType":"address","name":"assetB","type":"address"}],"name":"getOracleTolerableLimit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"keeperRewardDistributor","outputs":[{"internalType":"contract IKeeperRewardDistributorV3","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maintenanceBuffer","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"maxPositionSize","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"minPositionAsset","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"minPositionSize","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"components":[{"internalType":"string","name":"bucket","type":"string"},{"internalType":"uint256","name":"borrowedAmount","type":"uint256"},{"components":[{"internalType":"uint256","name":"shares","type":"uint256"},{"components":[{"internalType":"address","name":"to","type":"address"},{"components":[{"internalType":"string","name":"dexName","type":"string"},{"internalType":"uint256","name":"shares","type":"uint256"},{"internalType":"bytes","name":"payload","type":"bytes"}],"internalType":"struct PrimexPricingLibrary.Path[]","name":"paths","type":"tuple[]"}],"internalType":"struct PrimexPricingLibrary.Route[]","name":"routes","type":"tuple[]"}],"internalType":"struct PrimexPricingLibrary.MegaRoute[]","name":"depositInThirdAssetMegaRoutes","type":"tuple[]"}],"internalType":"struct PositionLibrary.OpenPositionMarginParams","name":"marginParams","type":"tuple"},{"components":[{"internalType":"uint256","name":"shares","type":"uint256"},{"components":[{"internalType":"address","name":"to","type":"address"},{"components":[{"internalType":"string","name":"dexName","type":"string"},{"internalType":"uint256","name":"shares","type":"uint256"},{"internalType":"bytes","name":"payload","type":"bytes"}],"internalType":"struct PrimexPricingLibrary.Path[]","name":"paths","type":"tuple[]"}],"internalType":"struct PrimexPricingLibrary.Route[]","name":"routes","type":"tuple[]"}],"internalType":"struct PrimexPricingLibrary.MegaRoute[]","name":"firstAssetMegaRoutes","type":"tuple[]"},{"internalType":"address","name":"depositAsset","type":"address"},{"internalType":"uint256","name":"depositAmount","type":"uint256"},{"internalType":"address","name":"positionAsset","type":"address"},{"internalType":"uint256","name":"amountOutMin","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"bool","name":"takeDepositFromWallet","type":"bool"},{"internalType":"bool","name":"isProtocolFeeInPmx","type":"bool"},{"components":[{"internalType":"uint256","name":"managerType","type":"uint256"},{"internalType":"bytes","name":"params","type":"bytes"}],"internalType":"struct LimitOrderLibrary.Condition[]","name":"closeConditions","type":"tuple[]"},{"internalType":"bytes","name":"firstAssetOracleData","type":"bytes"},{"internalType":"bytes","name":"thirdAssetOracleData","type":"bytes"},{"internalType":"bytes","name":"depositSoldAssetOracleData","type":"bytes"},{"internalType":"bytes","name":"positionUsdOracleData","type":"bytes"},{"internalType":"bytes","name":"nativePositionAssetOracleData","type":"bytes"},{"internalType":"bytes","name":"pmxPositionAssetOracleData","type":"bytes"},{"internalType":"bytes","name":"nativeSoldAssetOracleData","type":"bytes"},{"internalType":"bytes[][]","name":"pullOracleData","type":"bytes[][]"},{"internalType":"uint256[]","name":"pullOracleTypes","type":"uint256[]"}],"internalType":"struct PositionLibrary.OpenPositionParams","name":"_params","type":"tuple"}],"name":"openPosition","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"sender","type":"address"},{"components":[{"internalType":"contract IBucketV3","name":"bucket","type":"address"},{"internalType":"address","name":"positionAsset","type":"address"},{"internalType":"address","name":"depositAsset","type":"address"},{"internalType":"uint256","name":"depositAmount","type":"uint256"},{"internalType":"address","name":"feeToken","type":"address"},{"internalType":"uint256","name":"protocolFee","type":"uint256"},{"internalType":"address","name":"trader","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"uint256","name":"leverage","type":"uint256"},{"internalType":"bool","name":"shouldOpenPosition","type":"bool"},{"internalType":"uint256","name":"createdAt","type":"uint256"},{"internalType":"uint256","name":"updatedConditionsAt","type":"uint256"},{"internalType":"bytes","name":"extraParams","type":"bytes"}],"internalType":"struct LimitOrderLibrary.LimitOrder","name":"order","type":"tuple"},{"components":[{"internalType":"uint256","name":"managerType","type":"uint256"},{"internalType":"bytes","name":"params","type":"bytes"}],"internalType":"struct LimitOrderLibrary.Condition[]","name":"closeConditions","type":"tuple[]"},{"components":[{"internalType":"uint256","name":"shares","type":"uint256"},{"components":[{"internalType":"address","name":"to","type":"address"},{"components":[{"internalType":"string","name":"dexName","type":"string"},{"internalType":"uint256","name":"shares","type":"uint256"},{"internalType":"bytes","name":"payload","type":"bytes"}],"internalType":"struct PrimexPricingLibrary.Path[]","name":"paths","type":"tuple[]"}],"internalType":"struct PrimexPricingLibrary.Route[]","name":"routes","type":"tuple[]"}],"internalType":"struct PrimexPricingLibrary.MegaRoute[]","name":"firstAssetMegaRoutes","type":"tuple[]"},{"components":[{"internalType":"uint256","name":"shares","type":"uint256"},{"components":[{"internalType":"address","name":"to","type":"address"},{"components":[{"internalType":"string","name":"dexName","type":"string"},{"internalType":"uint256","name":"shares","type":"uint256"},{"internalType":"bytes","name":"payload","type":"bytes"}],"internalType":"struct PrimexPricingLibrary.Path[]","name":"paths","type":"tuple[]"}],"internalType":"struct PrimexPricingLibrary.Route[]","name":"routes","type":"tuple[]"}],"internalType":"struct PrimexPricingLibrary.MegaRoute[]","name":"depositInThirdAssetMegaRoutes","type":"tuple[]"},{"internalType":"bytes","name":"firstAssetOracleData","type":"bytes"},{"internalType":"bytes","name":"thirdAssetOracleData","type":"bytes"},{"internalType":"bytes","name":"depositSoldAssetOracleData","type":"bytes"},{"internalType":"bytes","name":"positionUsdOracleData","type":"bytes"},{"internalType":"bytes","name":"nativePositionAssetOracleData","type":"bytes"},{"internalType":"bytes","name":"pmxPositionAssetOracleData","type":"bytes"},{"internalType":"bytes","name":"nativeSoldAssetOracleData","type":"bytes"},{"internalType":"uint256","name":"borrowedAmount","type":"uint256"}],"internalType":"struct LimitOrderLibrary.OpenPositionByOrderParams","name":"_params","type":"tuple"}],"name":"openPositionByOrder","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"oracleTolerableLimitMultiplier","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_positionId","type":"uint256"},{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"address","name":"_depositReceiver","type":"address"},{"components":[{"internalType":"uint256","name":"shares","type":"uint256"},{"components":[{"internalType":"address","name":"to","type":"address"},{"components":[{"internalType":"string","name":"dexName","type":"string"},{"internalType":"uint256","name":"shares","type":"uint256"},{"internalType":"bytes","name":"payload","type":"bytes"}],"internalType":"struct PrimexPricingLibrary.Path[]","name":"paths","type":"tuple[]"}],"internalType":"struct PrimexPricingLibrary.Route[]","name":"routes","type":"tuple[]"}],"internalType":"struct PrimexPricingLibrary.MegaRoute[]","name":"_megaRoutes","type":"tuple[]"},{"internalType":"uint256","name":"_amountOutMin","type":"uint256"},{"internalType":"bytes","name":"_positionSoldAssetOracleData","type":"bytes"},{"internalType":"bytes","name":"_nativePositionAssetOracleData","type":"bytes"},{"internalType":"bytes","name":"_nativeSoldAssetOracleData","type":"bytes"},{"internalType":"bytes","name":"_pmxSoldAssetOracleData","type":"bytes"},{"internalType":"bytes[][]","name":"_pullOracleData","type":"bytes[][]"},{"internalType":"uint256[]","name":"_pullOracleTypes","type":"uint256[]"}],"name":"partiallyClosePosition","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"positionManagerExtension","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"positionsId","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"priceOracle","outputs":[{"internalType":"contract IPriceOracleV2","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"primexDNS","outputs":[{"internalType":"contract IPrimexDNSV3","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"registry","outputs":[{"internalType":"contract IAccessControl","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"securityBuffer","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_percent","type":"uint256"}],"name":"setDefaultOracleTolerableLimit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IKeeperRewardDistributorV3","name":"_keeperRewardDistributor","type":"address"}],"name":"setKeeperRewardDistributor","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_newMaintenanceBuffer","type":"uint256"}],"name":"setMaintenanceBuffer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_token0","type":"address"},{"internalType":"address","name":"_token1","type":"address"},{"internalType":"uint256","name":"_amountInToken0","type":"uint256"},{"internalType":"uint256","name":"_amountInToken1","type":"uint256"}],"name":"setMaxPositionSize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"token0","type":"address"},{"internalType":"address","name":"token1","type":"address"},{"internalType":"uint256","name":"amountInToken0","type":"uint256"},{"internalType":"uint256","name":"amountInToken1","type":"uint256"}],"internalType":"struct IPositionManagerExtension.MaxPositionSizeParams[]","name":"_params","type":"tuple[]"}],"name":"setMaxPositionSizes","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_assetA","type":"address"},{"internalType":"address","name":"_assetB","type":"address"},{"internalType":"uint256","name":"_percent","type":"uint256"}],"name":"setOracleTolerableLimit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"newMultiplier","type":"uint256"}],"name":"setOracleTolerableLimitMultiplier","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"assetA","type":"address"},{"internalType":"address","name":"assetB","type":"address"},{"internalType":"uint256","name":"percent","type":"uint256"}],"internalType":"struct IPositionManagerExtension.OracleTolerableLimitsParams[]","name":"_limitParams","type":"tuple[]"}],"name":"setOracleTolerableLimits","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_newSecurityBuffer","type":"uint256"}],"name":"setSecurityBuffer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_spotTradingRewardDistributor","type":"address"}],"name":"setSpotTradingRewardDistributor","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"spotTradingRewardDistributor","outputs":[{"internalType":"contract ISpotTradingRewardDistributorV2","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes4","name":"_interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"traderBalanceVault","outputs":[{"internalType":"contract ITraderBalanceVault","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"traderPositionIds","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_positionId","type":"uint256"},{"components":[{"internalType":"uint256","name":"managerType","type":"uint256"},{"internalType":"bytes","name":"params","type":"bytes"}],"internalType":"struct LimitOrderLibrary.Condition[]","name":"_closeConditions","type":"tuple[]"}],"name":"updatePositionConditions","outputs":[],"stateMutability":"nonpayable","type":"function"}]Contract Creation Code
608080604052346015576153b4908161001b8239f35b600080fdfe610320604052600436101561001357600080fd5b600061024052610240513560e01c80623ec24b1461382f57806301ffc9a7146137d95780630bbabf29146137b95780631682f623146137475780631785884c1461371c578063212d55ec146136fc5780632630c12f146136d157806328c855121461364f5780632beb2e9f146135385780633bf7771f1461317b5780633f4a2d2714613150578063414d19e31461260a57806350f41322146125df57806357c647c6146125945780635c975abb1461256f57806362ad9e9e146117ce578063659be053146117a3578063777d24c01461178357806377c30daf146116915780637b103999146116665780638262b7c5146113fc5780638992c911146113dc5780639968861e146113b15780639c68a60814611391578063a41b4ea414611346578063a8905dab14611312578063ac55640014610648578063b35ac732146105ee578063b4b6ce601461057b578063bb8313c21461055b578063c542914c146103bf578063d64470e914610365578063e92d529a1461033a578063eaf36179146102af5763fa824a82146101a557600080fd5b34610292576020366003190112610292576004356001600160a01b03811690819003610292576101d36150e2565b6040516301ffc9a760e01b8152631e42d3e560e01b6004820152602081602481855afa9081156102a1576102405191610260575b501561024e5760d880546001600160a01b03191682179055610240517f3b8f7040d16ad94528a612b931acee6355587dda723c170b4d5949f59560579f9080a26102405180f35b60405163044aa57560e41b8152600490fd5b90506020813d602011610299575b8161027b60209383613b97565b810103126102925761028c90614252565b38610207565b6102405180fd5b3d915061026e565b6040513d61024051823e3d90fd5b34610292576020366003190112610292576004356102cb61507a565b670de0b6b3a764000081101580610329575b15610317578060ce557f2de46581b5873212cac819667b4965f685fa93f3341a7f60b1ab25321e06daa16102405161024051a26102405180f35b60405163970e254b60e01b8152600490fd5b50678ac7230489e8000081106102dd565b3461029257610240513660031901126102925760d8546040516001600160a01b039091168152602090f35b346102925760403660031901126102925761037e613a04565b6024359060018060a01b0316610240515260d1602052604061024051208054821015610292576020916103b091613aa6565b90549060031b1c604051908152f35b34610292576020366003190112610292576004356001600160401b03811161029257366023820112156102925780600401356001600160401b03811161029257602482019160243691606084020101116102925761041b614fcb565b61024051734a63105e7d0df8fc25250568fcb6f3973885049691905b818110610445576102405180f35b610458610453828487614fa8565b613ae4565b61046e6020610468848689614fa8565b01613ae4565b90604061047c848689614fa8565b0135853b15610292576040516393ed1f6560e01b81526102405160cd60048301526001600160a01b03938416602483015293909216604483015260648201529081608481875af480156102a157610540575b50806104e06104536001938588614fa8565b6104f0602061046884878a614fa8565b7f938d851f75d7ce6b1bbe32add24a326e1d035a93c033dcfff2bff0a48d4337cd6020604061052086898c614fa8565b013592604051938452868060a01b031693868060a01b031692a301610437565b6102405161054d91613b97565b6102405161029257846104ce565b34610292576102405136600319011261029257602060da54604051908152f35b346102925760203660031901126102925760043561059761507a565b670de0b6b3a76400008110156105dc578060cb557fc585f8bc75966d22aaa8e0939f33adbf1bf3f6c52b616ea3055221ed374631a06102405161024051a26102405180f35b604051631d9c9aaf60e31b8152600490fd5b3461029257604036600319011261029257610607613a04565b61060f613a1f565b9060018060a01b0316610240515260c9602052604061024051209060018060a01b03166000526020526020604060002054604051908152f35b34610292576020366003190112610292576004356001600160401b03811161029257803603906101a060031983011261029257610683615181565b60d454604051632474521560e21b81527f8f8b3dc194d940c9ce77bea0cf23855523a0d7b641973e714d9dc5f3528a573a600482015233602482015290602090829060449082906001600160a01b03165afa9081156102a15761024051916112d8575b50156112c6575a60d75460d65460405162ea49c360e81b81526060600480830191909152939590936001600160a01b0393841693928316929061072a908701613a35565b1660648501526024850135906101c2190181121561029257840160048101906101a0608486015281359060018060a01b0382168092036102925761097761095361093061090d6108ea6108c88c8c6108b66108a86108866108726109e39e6101a46109bf9f9e61099b9f61020489015260018060a01b036107ad60248301613a35565b166102248901526001600160a01b036107c860448301613a35565b1661024489015260648101356102648901526001600160a01b036107ee60848301613a35565b1661028489015260a48101356102a48901526001600160a01b0361081460c48301613a35565b166102c489015260e48101356102e489015261010481013561030489015261012481013561032489015261084b61014482016147a3565b15156103448901526101648101356103648901526101848101356103848901520190613bf8565b6101c06103a48701526103c4860191613bd7565b6108966044860186600401613c29565b8583036063190160a4870152906147b0565b926064810190600401613c29565b9160c4606319828603019101526145c3565b6108d860848e0160048f01613c29565b8d83036063190160e48f0152906145c3565b6108fa60a48d018d600401613bf8565b8c8303606319016101048e015290613bd7565b61091d60c48c018c600401613bf8565b8b8303606319016101248d015290613bd7565b61094060e48b018b600401613bf8565b8a8303606319016101448c015290613bd7565b6109646101048a018a600401613bf8565b898303606319016101648b015290613bd7565b610988610124890189600401613bf8565b888303606319016101848a015290613bd7565b6109ac610144880188600401613bf8565b878303606319016101a489015290613bd7565b6109d0610164870187600401613bf8565b868303606319016101c488015290613bd7565b916101848501356101e485015260248401526044830152818061024051920381734a63105e7d0df8fc25250568fcb6f397388504965af49283156102a1576102405191610240519461129d575b50604051610a3d81613b60565b610240805182528051602083015280516040830152805160608301525160809091015260d65460d75460d55460e08701516001600160a01b03938416949284169390911690156112945760408701515160a0860151610aa8916001600160a01b039182169116614f4c565b6101a08801511561128b57606086015160a0870151610ad3916001600160a01b039182169116614f4c565b606087015161024080516001600160a01b03928316905260c96020908152905160409081902060a08b0151841660009081529252908190205460d854915198919092169691949190610b2489613b7b565b8852602088015260408701526060860152608085015260a084015260c083015260e082015260405190637e5eae6360e11b82526101406004830152610b6d610144830184614af4565b90600319838303016024840152610cf1610c93610c7f610c5d610bb0610b9e8b516102c089526102c0890190614b9e565b60208c015188820360208a0152614b9e565b6040808c015160018060a01b03815116828a0152602081015160608a01520151608088015260608b015160a088015260808b015160c088015260a08b015160e088015260c08b0151151561010088015260e08b015115156101208801526101008b015115156101408801526101208b015115156101608801526101408b015161018088015260018060a01b036101608c0151166101a08801526101808b01518782036101c0890152615253565b6101a08a015115156101e08701526101c08a0151868203610200880152613dba565b6101e0890151858203610220870152613dba565b92610cdb610cc7610cb36102008b01968751858203610240870152613dba565b6102208b0151848203610260860152613dba565b6102408a0151838203610280850152613dba565b90610260890151906102a0818403910152613dba565b81516001600160a01b039081166044860152602083015181166064860152604083015181166084860152606083015160a4860152608083015160c486015260a083015160e486015260c083015161010486015260e0909201519091166101248401526102405190839081900381734a63105e7d0df8fc25250568fcb6f397388504965af49283156102a157610240519261024051946111f3575b5060d05480845260001981146110ee5760010160d05560cf54600160401b8110156111d95783610dc4826001610dca940160cf55613bb8565b90614d08565b60cf5460001981019081116110ee578351610240515260dc602052604061024051205560e0830160018060a01b03815116610240515260d1602052610e17604061024051208551906152ac565b60018060a01b03905116610240515260d1602052604061024051205460001981019081116110ee578351610240515260dd60205260406102405120556040830160018060a01b03815116610240515260d2602052610e7d604061024051208551906152ac565b60018060a01b03815116610240515260d2602052604061024051205460001981019081116110ee578451610240515260de602052604061024051205561018087015160018060a01b0360d65416734a63105e7d0df8fc25250568fcb6f397388504963b1561029257604051630d16a28560e01b81526080600482015291610f2490610f0b6084850189614af4565b60d3602486015284810360031901604486015290615253565b906064830152818061024051920381734a63105e7d0df8fc25250568fcb6f397388504965af480156102a1576111be575b50516001600160a01b031615806111aa575b611108575b505060cf546000198101915081116110ee57610f8a610f9091613bb8565b5061490b565b90815192610fa960c06104686024840184600401614f85565b610fb582600401613ae4565b6040848101805160608701516101808b0151935192996001600160a01b039586169690951694937f7eb8abd0eb2f629d9150adef2d047584ab0d0830368a1d5d5243955f73f884e79392839261100d92918c856149c2565b0390a461102860c06104688551936024810190600401614f85565b60018060a01b0360a08501511695608084015160088110156110d4578451602080870151604080516001600160a01b039c8d168152928301939093529181019190915260a0986110ab95606095909116917f68c1d3a775fd37da20ef77475b9074ee3b57e4d73d94e046e892720b5043ff72908690a40151608084015190614f9b565b9260c0830151925190519151926040519485526020850152604084015260608301526080820152f35b634e487b7160e01b61024051526021600452602461024051fd5b634e487b7160e01b61024051526011600452602461024051fd5b60d95460e09091015160a084015160c0949094015192516001600160a01b03948516949093918116921690813b1561029257604051948593630ba6e0bb60e31b855260048501526024840152604483015260806064830152818061117461024051956084830190613dba565b039161024051905af180156102a15761118f575b8080610f6c565b6102405161119c91613b97565b610240516102925783611188565b5060d9546001600160a01b03161515610f67565b610240516111cb91613b97565b610240516102925787610f55565b634e487b7160e01b61024051526041600452602461024051fd5b925092503d8061024051843e6112098184613b97565b82018281039060c082126102925783516001600160401b0381116102925760a091611235918601613e79565b91601f1901126102925760a06040519361124e85613b60565b6020810151855260408101516020860152606081015160408601526080810151606086015201516008811015610292576080840152919286610d8b565b61024051610ad3565b61024051610aa8565b9093506112bf91503d8061024051833e6112b78183613b97565b81019061425f565b9284610a30565b60405163036be76f60e61b8152600490fd5b90506020813d60201161130a575b816112f360209383613b97565b810103126102925761130490614252565b836106e6565b3d91506112e6565b3461029257604036600319011261029257602061133e611330613a04565b611338613a1f565b90614f4c565b604051908152f35b346102925760403660031901126102925761135f613a04565b6024359060018060a01b0316610240515260d2602052604061024051208054821015610292576020916103b091613aa6565b34610292576102405136600319011261029257602060ce54604051908152f35b3461029257610240513660031901126102925760d5546040516001600160a01b039091168152602090f35b34610292576102405136600319011261029257602060ca54604051908152f35b34610292576040366003190112610292576004356024356001600160401b0381116102925761142f903690600401613a76565b61143a92919261512b565b6114426151c5565b81610240515260dc60205261145d6040610240512054613bb8565b506007810180549192916001600160a01b0316330361165457604051602081019061149b8161148d868a86614ee6565b03601f198101835282613b97565b51902084610240515260d3602052604061024051206040516020810191816040810191602085528054809352606082019260608160051b840101916102405152602061024051209361024051905b82821061161957505050611506925003601f198101835282613b97565b5190200361151a575b600180556102405180f35b6115238361490b565b60d654734a63105e7d0df8fc25250568fcb6f39738850496916001600160a01b0390911690823b1561029257604051630d16a28560e01b81526080600482015292839161159290611578906084850190614af4565b60d36024850152838103600319016044850152878b6147b0565b9260648301528180610240519403915af480156102a1576115fe575b507f7eae323942fab89b6464d6efc98661e980ff52b318d132930eed2b31b0fbf38c92600a4291015560018060a01b03905416936115f160405192839283614ee6565b0390a3808080808061150f565b6102405161160b91613b97565b6102405161029257856115ae565b91935091602060026116446040600194605f198b820301875289548152818582015201848901614888565b96019201920185939194926114e9565b60405163014d683360e41b8152600490fd5b3461029257610240513660031901126102925760d4546040516001600160a01b039091168152602090f35b34610292576060366003190112610292576116aa613a04565b6116b2613a1f565b604435906116be614fcb565b734a63105e7d0df8fc25250568fcb6f39738850496803b15610292576040516393ed1f6560e01b81526102405160cd60048301526001600160a01b03868116602484015284166044830152606482018590529091829060849082905af480156102a157611768575b506040519182526001600160a01b039081169216907f938d851f75d7ce6b1bbe32add24a326e1d035a93c033dcfff2bff0a48d4337cd90602090a36102405180f35b6102405161177591613b97565b610240516102925783611726565b34610292576102405136600319011261029257602060cb54604051908152f35b3461029257610240513660031901126102925760e0546040516001600160a01b039091168152602090f35b610160366003190112610292576044356101008190526001600160a01b0381169003610292576064356001600160401b03811161029257611813903690600401613a76565b9060c05260a4356001600160401b03811161029257611836903690600401613a49565b9060a05260c4356001600160401b03811161029257611859903690600401613a49565b6101e0526101c05260e4356001600160401b03811161029257611880903690600401613a49565b6101605261018052610104356001600160401b038111610292576118a8903690600401613a49565b60e05261012052610124356001600160401b038111610292576118cf903690600401613a76565b90610144356001600160401b038111610292576118f0903690600401613a76565b906118f961512b565b6119016151c5565b60cf54151580612546575b1561253457600435610240515260dc602052611931610f8a6040610240512054613bb8565b61020081905260e001516001600160a01b031633036116545760c061020051015160243510156125225760d7546001600160a01b031690813b1561029257611995946040519586948593849363f197ce3560e01b8552610240519860048601613c81565b039134905af180156102a15761250f575b506040516101a08190526001600160401b03608082019081119111176111d95760806101a05101604052610240516101a051526102405160206101a05101526102405160406101a05101526102405160606101a05101526020610200510151151560606101a051015260c06102005101516102405150610240515061024051506102405150670de0b6b3a764000060243502906024358204670de0b6b3a7640000146024351517156110ee57670de0b6b3a76400008204602435036124fa57611a73600182901c83614f9b565b9182106124e557611a83916152e1565b6101a080518290525160600151156124db57611aa6906020610200510151615301565b60206101a0510152611ac460806102005101516101a0515190615301565b60406101a051015260405190611ad982613af8565b610240518252606060208084018290526101a05160408101516102205290810151910151156124d2576102005160a0810151606090910151611b27916001600160a01b039182169116614f4c565b60d65460d75460d5546101a0516060908101516102005160a0810151920151604051631dae49b960e21b81526001600160a01b039384166004820152908316602482015290151598909490939282169282169116602085604481855afa9485156102a157610240519561249e575b5060cb549560606101a051015115159760405161014052610140516102a06101405101106001600160401b036102a0610140510111176111d9576102a061014051016040526024356101405152610220516020610140510152604061014051015260018060a01b0361010051166060610140510152611c138b613f52565b611c206040519182613b97565b806080528b815260208101608052368c60051b60c05101116102925760c0515b8c60051b60c0510181106122885750611d079a9b50608061014051015260843560a061014051015260c061014051015260e061014051015261010061014051015261012061014051015261014080510152610240515061024051506102405150602095604051611cb08882613b97565b6102405181526101606101405101526101806101405101526101a06101405101526101c06101405101526101e06101405101526102405161020061014051015261024051610220610140510152369060a051613d60565b610240610140510152611d203660e05161012051613d60565b610260610140510152611d3a366101605161018051613d60565b61028061014051015260405190630b2aa09960e11b8252606060048301526101408280611ee5611d706064830161020051614af4565b600319838203016024840152845151815260208551015186820152604085510151604082015260018060a01b03606086510151166060820152611ece611eb9611e4a611e35611dd160808a5101516102a060808801526102a0870190614b9e565b60a08a51015160a087015260c08a51015160c087015260018060a01b0360e08b5101511660e087015260018060a01b036101008b5101511661010087015260018060a01b036101208b5101511661012087015289805101518682038b880152614ceb565b61016089510151858203610160870152613dba565b6101808851015115156101808501526101a0885101516101a08501526101c0885101516101c08501526101e08851015115156101e08501526102008851015161020085015260018060a01b03610220895101511661022085015261024088510151848203610240860152613dba565b61026087510151838203610260850152613dba565b906102808651015190610280818403910152613dba565b6102405160448301520381734a63105e7d0df8fc25250568fcb6f397388504965af49182156102a15761024051926121b5575b50611f2c60243560c061020051015161482b565b60c0610200510152611f4c60206101a0510151602061020051015161482b565b6020610200510152611f6c60406101a0510151608061020051015161482b565b6102008051608001919091525160400180516001600160a01b03166121ac5760015b6102005160c081015160a09091015160d75460d85460d6549394735a1235cd7f233b024273dcace98f966e0e7c79189490936001600160a01b03918216939282169282169116853b156102925760405196636727ed3d60e11b88526004880152602487015260448601526064850152608484015260068110156110d45760a483015260e060c4830152816102405191818061203360e482016101e0516101c051613bd7565b03915af480156102a157612199575b50600435610240515260dc825261206c6120626040610240512054613bb8565b6102005191614d08565b60018060a01b039051169060018060a01b0360606102005101511660018060a01b0360a0610200510151166080610200510151602061020051015190865192858801519460608901519660405198895288015260408701526024356060870152608086015260a085015260c084015260e083015261010082015233907fda47f84a849dfb28125ae28a0bf305b75e72bff27796fc4bca36e2f848b0a0e661012060043592a360c081015160e082015191906001600160a01b031660088310156110d4576101008201516101209290920151604080516001600160a01b0390931683526020830193909352918101919091523390600435907f68c1d3a775fd37da20ef77475b9074ee3b57e4d73d94e046e892720b5043ff729080606081015b0390a4600180556102405180f35b610240516121a691613b97565b83612042565b61024051611f8e565b909150610140813d8211612280575b816121d26101409383613b97565b81010312610292576040519061014082016001600160401b038111838210176111d95760405280518252828101518383015260408101516040830152606081015160608301526080810151600581101561029257608083015261223760a08201613e14565b60a083015261224860c08201613e14565b60c083015260e0810151906008821015610292576101209160e084015261010081015161010084015201516101208201529082611f18565b3d91506121c4565b80356001600160401b0381116102925760c0510160408136031261029257604051906122b382613af8565b803582526020810135906001600160401b038211610292570136601f820112156102925780356122e281613f52565b916122f06040519384613b97565b81835260208084019260051b820101903682116102925760208101925b828410612333575050505090602092918382015260805152816080510160805201611c40565b83356001600160401b0381116102925782016040601f198236030112610292576040519061236082613af8565b61236c60208201613a35565b825260408101356001600160401b03811161029257602091010136601f8201121561029257803561239c81613f52565b916123aa6040519384613b97565b81835260208084019260051b820101903682116102925760208101925b8284106123e757505050509181602093848094015281520193019261230d565b83356001600160401b038111610292578201906060601f198336030112610292576040519161241583613b29565b60208101356001600160401b0381116102925760209082010136601f820112156102925761244a903690602081359101613d60565b83526040810135602084015260608101356001600160401b0381116102925760209101019036601f830112156102925760209261248e849336908581359101613d60565b60408201528152019301926123c7565b9094506020813d6020116124ca575b816124ba60209383613b97565b810103126102925751938a611b95565b3d91506124ad565b61024051611b27565b5061024051611aa6565b630a77254f60e01b6102405152600461024051fd5b631550e8b760e01b6102405152600461024051fd5b6102405161251c91613b97565b826119a6565b60405163242e4c2b60e11b8152600490fd5b604051631eab2a3b60e31b8152600490fd5b50600435610240515260dc6020526125646040610240512054613bb8565b50546004351461190c565b34610292576102405136600319011261029257602060ff606554166040519015158152f35b34610292576020366003190112610292576125ad613a04565b6125b56150e2565b60018060a01b03166bffffffffffffffffffffffff60a01b60d954161760d9556102405161024051f35b3461029257610240513660031901126102925760d9546040516001600160a01b039091168152602090f35b6020366003190112610292576004356001600160401b03811161029257806004019061026060031982360301126102925761264361512b565b61264b615181565b6126536151c5565b60018060a01b0360d7541661022482019261266e8482613ddf565b9061024485019361267f8585613ddf565b9190813b15610292576126ae946040519586948593849363f197ce3560e01b8552610240519860048601613c81565b039134905af180156102a15761313d575b5060d65460d75460405163069102a360e01b815260606004820152734a63105e7d0df8fc25250568fcb6f397388504969690956001600160a01b039384169592909316939092612937906129309061291d6102046129156128f36128d18d6128b161289061286f8c61285c6128508c61283d6101246127ba61279b8b610260606461274a8b8061459b565b9201528c61278761277161275e8480613bf8565b60606102c4860152610324850191613bd7565b9260208101356102e48401526040810190613c29565b916103046102c319828603019101526145c3565b6127a86024860189613c29565b8d83036063190160848f0152906145c3565b926001600160a01b036127cf60448301613a35565b1660a48c0152606481013560c48c01526001600160a01b036127f360848301613a35565b1660e48c015260a48101356101048c015260c4810135828c015261281960e482016147a3565b15156101448c015261282e61010482016147a3565b15156101648c01520185613c29565b898303606319016101848b0152906147b0565b916101448d0190613bf8565b868303606319016101a488015290613bd7565b61287d6101648b018e613bf8565b858303606319016101c487015290613bd7565b61289e6101848a018d613bf8565b848303606319016101e486015290613bd7565b906128c06101a489018c613bf8565b918760631982860301910152613bd7565b8d6128e06101c488018b613bf8565b9161022460631982860301910152613bd7565b8c6129026101e487018a613bf8565b9161024460631982860301910152613bd7565b920185613bf8565b8a8303606319016102648c015290613bd7565b9382613c29565b60631988860301610284890152808552602085019460208260051b820101958361024051925b8484106130b25750505050505061298b929161297891613c29565b868303606319016102a488015290613c5d565b9160248401526044830152818061024051920381855af49081156102a15761024051906102405192613092575b506040516129c581613b60565b610240805182528051602083015280516040830152805160608301525160809091015260d65460d75460d55460e08501516001600160a01b039384169491841693919290911690156130885760408501515160a0830151612a32916001600160a01b039182169116614f4c565b935b6101a08601511561307e57606083015160a0840151612a5f916001600160a01b039182169116614f4c565b945b606084015161024080516001600160a01b03928316905260c960209081528151604080822060a08a01518616909252915290518190205460d854915194919092169290612aad85613b7b565b8452602084019485526040808501978852606085019889526080850191825260a085019283526102405160c0860190815260e086019490945251637e5eae6360e11b8152610140600482015297612b086101448a0188614af4565b976003198a8a030160248b01528a516102c08a526102c08a01612b2a91614b9e565b60208c0151908a810360208c0152612b4191614b9e565b60408c0151600160a01b6001900381511660408c0152602081015160608c01526040015160808b015260608c015160a08b015260808c015160c08b015260a08c015160e08b015260c08c015115156101008b015260e08c015115156101208b01526101008c015115156101408b01526101208c015115156101608b01526101408c01516101808b0152600160a01b600190036101608d0151166101a08b01526101808c0151908a81036101c08c0152612bf991615253565b6101a08c015115156101e08b01526101c08c0151908a81036102008c0152612c2091613dba565b6101e08c0151908a81036102208c0152612c3991613dba565b986102008c01998a5190828103610240840152612c5591613dba565b6102208d015190828103610260840152612c6e91613dba565b6102408d015190828103610280840152612c8791613dba565b6102608d015191808203906102a00152612ca091613dba565b86516001600160a01b0390811660448d01529751881660648c01529051871660848b0152905160a48a0152905160c4890152905160e4880152905161010487015260e0909101519091166101248501526102405190849081900381885af49485156102a15761024051936102405196612fe6575b5060d05480855260001981146110ee5760010160d05560cf54600160401b8110156111d95784610dc4826001612d4d940160cf55613bb8565b60cf5460001981019081116110ee578451610240515260dc602052604061024051205560e0840160018060a01b03815116610240515260d1602052612d9a604061024051208651906152ac565b60018060a01b03905116610240515260d1602052604061024051205460001981019081116110ee578451610240515260dd6020526040610240512055604084019060018060a01b03825116610240515260d2602052612e01604061024051208651906152ac565b60018060a01b03825116610240515260d2602052604061024051205460001981019081116110ee578551610240515260de602052604061024051205561018086015160018060a01b0360d6541690823b1561029257604051630d16a28560e01b815260806004820152928391612e7e90610f0b608485018b614af4565b9260648301528180610240519403915af480156102a157612fd3575b50516001600160a01b03161580612fbf575b612f25575b505060cf54600019810193915083116110ee577f7eb8abd0eb2f629d9150adef2d047584ab0d0830368a1d5d5243955f73f884e7612ef4610f8a61218b95613bb8565b9182519260018060a01b0360e0820151169586956101806060604086015195015191015190604051948594856149c2565b60d95460e09091015160a084015160c0949094015192516001600160a01b03948516949093918116921690813b1561029257604051948593630ba6e0bb60e31b8552600485015260248401526044830152608060648301528180612f9161024051956084830190613dba565b039161024051905af180156102a157612fac575b8080612eb1565b61024051612fb991613b97565b82612fa5565b5060d9546001600160a01b03161515612eac565b61024051612fe091613b97565b86612e9a565b935094503d8061024051853e612ffc8185613b97565b83018381039060c082126102925784516001600160401b0381116102925760a091613028918701613e79565b91601f1901126102925760a06040519461304186613b60565b6020810151865260408101516020870152606081015160408701526080810151606087015201516008811015610292576080850152929486612d14565b6102405194612a61565b6102405193612a34565b90506130ab91503d8061024051833e6112b78183613b97565b90836129b8565b909192939497601f198282030184526130cb8984613c29565b808352602083019060208160051b850101938361024051905b838210613107575050505050506020806001929a0194019401929493919061295d565b90919293949560208061312f600193601f19888203018a526131298b87613bf8565b90613bd7565b9801960194939201906130e4565b6102405161314a91613b97565b846126bf565b3461029257610240513660031901126102925760d6546040516001600160a01b039091168152602090f35b60c0366003190112610292576044356001600160401b038111610292576131a6903690600401613a49565b906064356001600160401b038111610292576131c6903690600401613a49565b9190926084356001600160401b038111610292576131e8903690600401613a76565b60a4959195356001600160401b0381116102925761320a903690600401613a76565b61321597919761512b565b61321d615181565b6132256151c5565b600435610240515260dc6020526132426040610240512054613bb8565b509760018060a01b0360d7541690813b156102925761327d946040519586948593849363f197ce3560e01b8552610240519860048601613c81565b039134905af180156102a157613525575b5060d65460d75460d55460058801546003890154604051631dae49b960e21b81526001600160a01b039283166004820181905291831660248201819052958316999383169892909416959294929391926020836044818a5afa9283156102a15761024051936134ef575b506133069060cb5492614f4c565b9160cc549360018060a01b0360d8541695604051998a6101608101106001600160401b036101608d0111176111d95761339a9261338a916101608d016040526024358d5260208d019e8f5260408d019b8c5260608d019d8e5260808d0195865260a08d0196875260c08d0197885260e08d019889526101008d01998a523691613d60565b976101208b019889523691613d60565b986101408901998a52734a63105e7d0df8fc25250568fcb6f397388504963b1561029257604080516319498bc360e21b8152600481018e90526024810191909152985160448a015299516001600160a01b0390811660648a01529651871660848901529851861660a4880152975160c4870152965160e48601529551610104850152945161012484015293511661014482015291516101606101648401528290819061344b906101a4830190613dba565b925181840360431901610184830152610240519361346891613dba565b0381734a63105e7d0df8fc25250568fcb6f397388504965af480156102a1576134dc575b5080547f21b98b9d6fb2ba49b9764bd2d465450aa78464b9f9ddf9846bca69ce7e0e969560406001808060a01b0360078601541694015481519060243582526020820152a3600180556102405180f35b610240516134e991613b97565b8161348c565b9092506020813d60201161351d575b8161350b60209383613b97565b810103126102925751916133066132f8565b3d91506134fe565b6102405161353291613b97565b8561328e565b3461029257608036600319011261029257613551613a04565b613559613a1f565b60643591604435613568614fcb565b734a63105e7d0df8fc25250568fcb6f3973885049693843b1561029257604051635323cdb560e11b81526102405160c960048301526001600160a01b0385811660248401528616604483015260648201849052608482018390529095869060a49082905af49182156102a1577fa73bd1411d7cefafbca3c39b9dfc6d263533671c6b50b796f2f4a31eb63a4711956136329361363c575b50604080516001600160a01b03958616815295909416602086015292840192909252606083019190915281906080820190565b0390a16102405180f35b6102405161364991613b97565b866135ff565b346102925760203660031901126102925760043561366b61507a565b801515806136c0575b156136ae578060cc557fbaff4968173e39aed42691fb55671c5a687a43b3b936a129ab68365a791d006d6102405161024051a26102405180f35b6040516333e75a7f60e11b8152600490fd5b50670de0b6b3a76400008110613674565b3461029257610240513660031901126102925760d7546040516001600160a01b039091168152602090f35b34610292576102405136600319011261029257602060cc54604051908152f35b3461029257610240513660031901126102925760db546040516001600160a01b039091168152602090f35b346102925760203660031901126102925760043561376361507a565b670de0b6b3a764000081116137a7578060ca557f9cc578c88f7a198c4ccced6601ea38cdc1fc2dbacf6b476b689ba507edbe94e66102405161024051a26102405180f35b60405163948b756d60e01b8152600490fd5b34610292576102405136600319011261029257602060d054604051908152f35b346102925760203660031901126102925760043563ffffffff60e01b81168091036102925760209063caaf0f5760e01b811490811561381e575b506040519015158152f35b6301ffc9a760e01b14905082613813565b34610292576020366003190112610292576004356001600160401b0381116102925736602382011215610292578060040135906001600160401b038211610292576024810190602436918460071b010111610292579061388d614fcb565b61024051734a63105e7d0df8fc25250568fcb6f3973885049692905b8281106138b7576102405180f35b6138c5610453828585613ad4565b906138d66020610468838787613ad4565b9160406138e4838787613ad4565b013560606138f3848888613ad4565b013593873b1561029257604051635323cdb560e11b81526102405160c960048301526001600160a01b03948516602483015291909316604484015260648301919091526084820193909352918260a481885af49182156102a1576001926139f1575b507fa73bd1411d7cefafbca3c39b9dfc6d263533671c6b50b796f2f4a31eb63a4711613985610453838787613ad4565b6139956020610468858989613ad4565b906139e860406139a6868a8a613ad4565b013560606139b5878b8b613ad4565b604080516001600160a01b0396871681529690951660208701529385019190915290910135606083015281906080820190565b0390a1016138a9565b610240516139fe91613b97565b85613955565b600435906001600160a01b0382168203613a1a57565b600080fd5b602435906001600160a01b0382168203613a1a57565b35906001600160a01b0382168203613a1a57565b9181601f84011215613a1a578235916001600160401b038311613a1a5760208381860195010111613a1a57565b9181601f84011215613a1a578235916001600160401b038311613a1a576020808501948460051b010111613a1a57565b8054821015613abe5760005260206000200190600090565b634e487b7160e01b600052603260045260246000fd5b9190811015613abe5760071b0190565b356001600160a01b0381168103613a1a5790565b604081019081106001600160401b03821117613b1357604052565b634e487b7160e01b600052604160045260246000fd5b606081019081106001600160401b03821117613b1357604052565b61018081019081106001600160401b03821117613b1357604052565b60a081019081106001600160401b03821117613b1357604052565b61010081019081106001600160401b03821117613b1357604052565b90601f801991011681019081106001600160401b03821117613b1357604052565b60cf54811015613abe5760cf600052600c602060002091020190600090565b908060209392818452848401376000828201840152601f01601f1916010190565b9035601e1982360301811215613a1a5701602081359101916001600160401b038211613a1a578136038313613a1a57565b9035601e1982360301811215613a1a5701602081359101916001600160401b038211613a1a578160051b36038313613a1a57565b81835290916001600160fb1b038311613a1a5760209260051b809284830137010190565b949391929094836040820160408352526060810160608560051b8301019487916000905b828210613cc65750505050613cc39495506020818503910152613c5d565b90565b90919296605f19858203018252613cdd888b613c29565b808352602083019060208160051b85010193836000905b838210613d1557505050505050602080600192990192019201909291613ca5565b909192939495602080613d37600193601f19888203018a526131298b87613bf8565b980196019493920190613cf4565b6001600160401b038111613b1357601f01601f191660200190565b929192613d6c82613d45565b91613d7a6040519384613b97565b829481845281830111613a1a578281602093846000960137010152565b60005b838110613daa5750506000910152565b8181015183820152602001613d9a565b90602091613dd381518092818552858086019101613d97565b601f01601f1916010190565b903590601e1981360301821215613a1a57018035906001600160401b038211613a1a57602001918160051b36038313613a1a57565b51906001600160a01b0382168203613a1a57565b90929192613e3581613d45565b91613e436040519384613b97565b829482845282820111613a1a576020613e5d930190613d97565b565b9080601f83011215613a1a578151613cc392602001613e28565b919061018083820312613a1a5760405190613e9382613b44565b83518252602080850151908301526040840151919384926001600160a01b0381168103613a1a576040840152613ecb60608201613e14565b606084015260808101516080840152613ee660a08201613e14565b60a084015260c081015160c0840152613f0160e08201613e14565b60e0840152610100810151610100840152610120810151610120840152610140810151610140840152610160810151916001600160401b038311613a1a5761016092613f4d9201613e5f565b910152565b6001600160401b038111613b135760051b60200190565b610280526103005261030051601f61028051011215613a1a5761028051516102c052613faa613f9a6102c051613f52565b6040516102e0526102e051613b97565b6102e051506102c0516102e0515260206102e051016103005160206102c05160051b61028051010111613a1a57602061028051016102a0525b60206102c05160051b6102805101016102a0511061400357506102e05190565b6102a051516001600160401b038111613a1a5761028051016040601f198261030051030112613a1a576040519061403982613af8565b602081015182526040810151906001600160401b038211613a1a5761030051603f828401011215613a1a57602082820101519061407582613f52565b926140836040519485613b97565b82845260208085019360051b8183850101010191610300518311613a1a57604081830101935b8385106140d157505050505090602092918382015281520160206102a051016102a052613fe3565b84516001600160401b038111613a1a57602084840101016040601f198261030051030112613a1a576040519061410682613af8565b61411260208201613e14565b82526040810151906001600160401b038211613a1a5761030051603f828401011215613a1a57602082820101519061414982613f52565b926141576040519485613b97565b82845260208085019360051b8183850101010191610300518311613a1a57604081830101935b83851061419e575050505050918160209384809401528152019401936140a9565b84516001600160401b038111613a1a57602084840101016060601f198261030051030112613a1a57604051916141d383613b29565b60208201516001600160401b038111613a1a5760209083010161030051601f82011215613a1a5761030051815161420c92602001613e28565b8352604082015160208401526060820151926001600160401b038411613a1a576142426020949385809561030051920101613e5f565b604082015281520194019361417d565b51908115158203613a1a57565b9190604083820312613a1a5782516001600160401b038111613a1a5781614287918501613e79565b926020810151906001600160401b038211613a1a5701808203916102c08312613a1a576040519261028084018481106001600160401b03821117613b135760405282516001600160401b038111613a1a57826142e4918501613f69565b84526020830151906001600160401b038211613a1a57614308836060938601613f69565b6020860152603f190112613a1a5760405161432281613b29565b61432e60408401613e14565b81526060830151602082015260808301516040820152604084015260a0820151606084015260c0820151608084015260e082015160a08401526143746101008301614252565b60c08401526143866101208301614252565b60e08401526143986101408301614252565b6101008401526143ab6101608301614252565b6101208401526101808201516101408401526143ca6101a08301613e14565b6101608401526101c08201516001600160401b038111613a1a57820181601f82011215613a1a578051906143fd82613f52565b9161440b6040519384613b97565b80835260208084019160051b83010191848311613a1a5760208101915b83831061453157505050506101808401526144466101e08301614252565b6101a08401526102008201516001600160401b038111613a1a578161446c918401613e5f565b6101c08401526102208201516001600160401b038111613a1a5781614492918401613e5f565b6101e08401526102408201516001600160401b038111613a1a57816144b8918401613e5f565b6102008401526102608201516001600160401b038111613a1a57816144de918401613e5f565b6102208401526102808201516001600160401b038111613a1a5781614504918401613e5f565b6102408401526102a08201516001600160401b038111613a1a576145289201613e5f565b61026082015290565b82516001600160401b038111613a1a578201906040828803601f190112613a1a576040519061455f82613af8565b602083015182526040830151916001600160401b038311613a1a5761458c89602080969581960101613e5f565b83820152815201920191614428565b9035605e1982360301811215613a1a570190565b9035603e1982360301811215613a1a570190565b91906102a0526020816102a0518152019060206102a05160051b82010192806102e0526000610280525b6102a05161028051106146005750505090565b909192601f198382030184526146196102e051836145af565b61462f6040830191803584526020810190613c29565b839192604060208396015252606081019160608460051b8301016103005280916000925b8584106146845750505050505060206103005193816102e051016102e05201916001610280510161028052906145ed565b605f1982610300510301855261469a81846145af565b61030051604001906146c7906001600160a01b036146b782613a35565b1661030051526020810190613c29565b80926040602061030051015252606061030051019060608360051b61030051010161026052806102c0526000915b83831061471a5750505050602080600192610260516103005201950193019293614653565b6020600191605f1961030051610260510301815261478c61473e6102c0518661459b565b61477861476261474e8380613bf8565b606061026051526060610260510191613bd7565b9185810135866102605101526040810190613bf8565b906102605183036040610260510152613bd7565b61026052816102c051016102c052019201916146f5565b35908115158203613a1a57565b90602083828152019260208260051b82010193836000925b8484106147d85750505050505090565b90919293949560208061481b600193601f19868203018852604061480c6147ff8d8a6145af565b8035845285810190613bf8565b91909281868201520191613bd7565b98019401940192949391906147c8565b9190820391821161483857565b634e487b7160e01b600052601160045260246000fd5b90600182811c9216801561487e575b602083101461486857565b634e487b7160e01b600052602260045260246000fd5b91607f169161485d565b600092918154916148988361484e565b80835292600181169081156148ee57506001146148b457505050565b60009081526020812093945091925b8383106148d4575060209250010190565b6001816020929493945483858701015201910191906148c3565b915050602093945060ff929192191683830152151560051b010190565b90600b61016060405161491d81613b44565b845481526001850154602082015260028501546001600160a01b03908116604080840191909152600387015482166060840152600487015460808401526005870154821660a0840152600687015460c0840152600787015490911660e083015260088601546101008301526009860154610120830152600a860154610140830152519094909285916149be9185916149b791839101614888565b0384613b97565b0152565b919290610160614a79916080855280516080860152602081015160a086015260018060a01b0360408201511660c086015260018060a01b0360608201511660e0860152608081015161010086015260018060a01b0360a08201511661012086015260c081015161014086015260018060a01b0360e082015116828601526101008101516101808601526101208101516101a08601526101408101516101c086015201516101806101e0850152610200840190613dba565b92602083015260408201526060818303910152815180825260208201916020808360051b8301019401926000915b838310614ab657505050505090565b9091929394602080614ae5600193601f198682030187526040838b518051845201519181858201520190613dba565b97019301930191939290614aa7565b90610180610160613cc393805184526020810151602085015260018060a01b03604082015116604085015260018060a01b0360608201511660608501526080810151608085015260018060a01b0360a08201511660a085015260c081015160c085015260018060a01b0360e08201511660e0850152610100810151610100850152610120810151610120850152610140810151610140850152015191816101608201520190613dba565b9080602083519182815201916020808360051b8301019401926000915b838310614bca57505050505090565b9091929394601f1982820301835285519060206040820192805183520151916040602083015282518091526060820190602060608260051b85010194019260005b828110614c2c57505050505060208060019297019301930191939290614bbb565b9091929394605f1983820301855285516020604083019160018060a01b0381511684520151916040602082015282518092526060810190602060608460051b8301019401926000915b818310614c9657505050505060208060019297019501910192919092614c0b565b9091929394602080614cde600193605f198682030189528951906040614cc58351606084526060840190613dba565b9285810151868401520151906040818403910152613dba565b9701950193019190614c75565b9060406020613cc393805184520151918160208201520190613dba565b90929192614ed057825181556020830151600182015560408301516002820180546001600160a01b039283166001600160a01b03199182161790915560608501516003840180549184169183169190911790556080850151600484015560a085015160058401805491841691831691909117905560c0850151600684015560e085015160078401805491909316911617905561010083015160088201556101208301516009820155610140830151600a8201556101609092015180519092600b01906001600160401b038111613b1357614de2825461484e565b601f8111614e88575b506020601f8211600114614e265781929394600092614e1b575b50508160011b916000199060031b1c1916179055565b015190503880614e05565b601f1982169083600052806000209160005b818110614e7057509583600195969710614e57575b505050811b019055565b015160001960f88460031b161c19169055388080614e4d565b9192602060018192868b015181550194019201614e38565b826000526020600020601f830160051c81019160208410614ec6575b601f0160051c01905b818110614eba5750614deb565b60008155600101614ead565b9091508190614ea4565b634e487b7160e01b600052600060045260246000fd5b9180602084016020855252604083019260408260051b82010193836000925b848410614f155750505050505090565b909192939495602080614f3c600193603f19868203018852604061480c6147ff8d8a6145af565b9801940194019294939190614f05565b6001600160a01b03908116600090815260cd60209081526040808320949093168252929092529020548015614f7e5790565b5060ca5490565b9035906101be1981360301821215613a1a570190565b9190820180921161483857565b9190811015613abe576060020190565b8181029291811591840414171561483857565b60d454604051632474521560e21b81527f5a7d4408f4759dddd7fdfd0d21abd99341dc2f52cda14804988a9b2df20766d8600482015233602482015290602090829060449082906001600160a01b03165afa90811561506e57600091615034575b50156112c657565b90506020813d602011615066575b8161504f60209383613b97565b81010312613a1a5761506090614252565b3861502c565b3d9150615042565b6040513d6000823e3d90fd5b60d454604051632474521560e21b81527fc0fc8e4dc5cff6febdf550b80d566f654e2baf1a02ea1060208c2f8ab2dd1b63600482015233602482015290602090829060449082906001600160a01b03165afa90811561506e576000916150345750156112c657565b60d454604051632474521560e21b81526000600482015233602482015290602090829060449082906001600160a01b03165afa90811561506e576000916150345750156112c657565b60026001541461513c576002600155565b60405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606490fd5b60ff6065541661518d57565b60405162461bcd60e51b815260206004820152601060248201526f14185d5cd8589b194e881c185d5cd95960821b6044820152606490fd5b60df54604051630723eb0360e51b815233600482015290602090829060249082906001600160a01b03165afa90811561506e57600091615219575b5061520757565b6040516333df015b60e01b8152600490fd5b90506020813d60201161524b575b8161523460209383613b97565b81010312613a1a5761524590614252565b38615200565b3d9150615227565b9080602083519182815201916020808360051b8301019401926000915b83831061527f57505050505090565b909192939460208061529d600193601f198682030187528951614ceb565b97019301930191939290615270565b8054600160401b811015613b13576152c991600182018155613aa6565b819291549060031b91821b91600019901b1916179055565b81156152eb570490565b634e487b7160e01b600052601260045260246000fd5b600091801591821561535e575b50501561534d576706f05b59d3b2000081019081811161483857811061533c57670de0b6b3a7640000900490565b630a77254f60e01b60005260046000fd5b631550e8b760e01b60005260046000fd5b9150915061537661536f8383614fb8565b92836152e1565b14388061530e56fea264697066735822122062f14c50dff4d12ac150b1cec279799f273bc46e4de9d4eac729aed62267051e64736f6c634300081a0033
Deployed Bytecode
0x610320604052600436101561001357600080fd5b600061024052610240513560e01c80623ec24b1461382f57806301ffc9a7146137d95780630bbabf29146137b95780631682f623146137475780631785884c1461371c578063212d55ec146136fc5780632630c12f146136d157806328c855121461364f5780632beb2e9f146135385780633bf7771f1461317b5780633f4a2d2714613150578063414d19e31461260a57806350f41322146125df57806357c647c6146125945780635c975abb1461256f57806362ad9e9e146117ce578063659be053146117a3578063777d24c01461178357806377c30daf146116915780637b103999146116665780638262b7c5146113fc5780638992c911146113dc5780639968861e146113b15780639c68a60814611391578063a41b4ea414611346578063a8905dab14611312578063ac55640014610648578063b35ac732146105ee578063b4b6ce601461057b578063bb8313c21461055b578063c542914c146103bf578063d64470e914610365578063e92d529a1461033a578063eaf36179146102af5763fa824a82146101a557600080fd5b34610292576020366003190112610292576004356001600160a01b03811690819003610292576101d36150e2565b6040516301ffc9a760e01b8152631e42d3e560e01b6004820152602081602481855afa9081156102a1576102405191610260575b501561024e5760d880546001600160a01b03191682179055610240517f3b8f7040d16ad94528a612b931acee6355587dda723c170b4d5949f59560579f9080a26102405180f35b60405163044aa57560e41b8152600490fd5b90506020813d602011610299575b8161027b60209383613b97565b810103126102925761028c90614252565b38610207565b6102405180fd5b3d915061026e565b6040513d61024051823e3d90fd5b34610292576020366003190112610292576004356102cb61507a565b670de0b6b3a764000081101580610329575b15610317578060ce557f2de46581b5873212cac819667b4965f685fa93f3341a7f60b1ab25321e06daa16102405161024051a26102405180f35b60405163970e254b60e01b8152600490fd5b50678ac7230489e8000081106102dd565b3461029257610240513660031901126102925760d8546040516001600160a01b039091168152602090f35b346102925760403660031901126102925761037e613a04565b6024359060018060a01b0316610240515260d1602052604061024051208054821015610292576020916103b091613aa6565b90549060031b1c604051908152f35b34610292576020366003190112610292576004356001600160401b03811161029257366023820112156102925780600401356001600160401b03811161029257602482019160243691606084020101116102925761041b614fcb565b61024051734a63105e7d0df8fc25250568fcb6f3973885049691905b818110610445576102405180f35b610458610453828487614fa8565b613ae4565b61046e6020610468848689614fa8565b01613ae4565b90604061047c848689614fa8565b0135853b15610292576040516393ed1f6560e01b81526102405160cd60048301526001600160a01b03938416602483015293909216604483015260648201529081608481875af480156102a157610540575b50806104e06104536001938588614fa8565b6104f0602061046884878a614fa8565b7f938d851f75d7ce6b1bbe32add24a326e1d035a93c033dcfff2bff0a48d4337cd6020604061052086898c614fa8565b013592604051938452868060a01b031693868060a01b031692a301610437565b6102405161054d91613b97565b6102405161029257846104ce565b34610292576102405136600319011261029257602060da54604051908152f35b346102925760203660031901126102925760043561059761507a565b670de0b6b3a76400008110156105dc578060cb557fc585f8bc75966d22aaa8e0939f33adbf1bf3f6c52b616ea3055221ed374631a06102405161024051a26102405180f35b604051631d9c9aaf60e31b8152600490fd5b3461029257604036600319011261029257610607613a04565b61060f613a1f565b9060018060a01b0316610240515260c9602052604061024051209060018060a01b03166000526020526020604060002054604051908152f35b34610292576020366003190112610292576004356001600160401b03811161029257803603906101a060031983011261029257610683615181565b60d454604051632474521560e21b81527f8f8b3dc194d940c9ce77bea0cf23855523a0d7b641973e714d9dc5f3528a573a600482015233602482015290602090829060449082906001600160a01b03165afa9081156102a15761024051916112d8575b50156112c6575a60d75460d65460405162ea49c360e81b81526060600480830191909152939590936001600160a01b0393841693928316929061072a908701613a35565b1660648501526024850135906101c2190181121561029257840160048101906101a0608486015281359060018060a01b0382168092036102925761097761095361093061090d6108ea6108c88c8c6108b66108a86108866108726109e39e6101a46109bf9f9e61099b9f61020489015260018060a01b036107ad60248301613a35565b166102248901526001600160a01b036107c860448301613a35565b1661024489015260648101356102648901526001600160a01b036107ee60848301613a35565b1661028489015260a48101356102a48901526001600160a01b0361081460c48301613a35565b166102c489015260e48101356102e489015261010481013561030489015261012481013561032489015261084b61014482016147a3565b15156103448901526101648101356103648901526101848101356103848901520190613bf8565b6101c06103a48701526103c4860191613bd7565b6108966044860186600401613c29565b8583036063190160a4870152906147b0565b926064810190600401613c29565b9160c4606319828603019101526145c3565b6108d860848e0160048f01613c29565b8d83036063190160e48f0152906145c3565b6108fa60a48d018d600401613bf8565b8c8303606319016101048e015290613bd7565b61091d60c48c018c600401613bf8565b8b8303606319016101248d015290613bd7565b61094060e48b018b600401613bf8565b8a8303606319016101448c015290613bd7565b6109646101048a018a600401613bf8565b898303606319016101648b015290613bd7565b610988610124890189600401613bf8565b888303606319016101848a015290613bd7565b6109ac610144880188600401613bf8565b878303606319016101a489015290613bd7565b6109d0610164870187600401613bf8565b868303606319016101c488015290613bd7565b916101848501356101e485015260248401526044830152818061024051920381734a63105e7d0df8fc25250568fcb6f397388504965af49283156102a1576102405191610240519461129d575b50604051610a3d81613b60565b610240805182528051602083015280516040830152805160608301525160809091015260d65460d75460d55460e08701516001600160a01b03938416949284169390911690156112945760408701515160a0860151610aa8916001600160a01b039182169116614f4c565b6101a08801511561128b57606086015160a0870151610ad3916001600160a01b039182169116614f4c565b606087015161024080516001600160a01b03928316905260c96020908152905160409081902060a08b0151841660009081529252908190205460d854915198919092169691949190610b2489613b7b565b8852602088015260408701526060860152608085015260a084015260c083015260e082015260405190637e5eae6360e11b82526101406004830152610b6d610144830184614af4565b90600319838303016024840152610cf1610c93610c7f610c5d610bb0610b9e8b516102c089526102c0890190614b9e565b60208c015188820360208a0152614b9e565b6040808c015160018060a01b03815116828a0152602081015160608a01520151608088015260608b015160a088015260808b015160c088015260a08b015160e088015260c08b0151151561010088015260e08b015115156101208801526101008b015115156101408801526101208b015115156101608801526101408b015161018088015260018060a01b036101608c0151166101a08801526101808b01518782036101c0890152615253565b6101a08a015115156101e08701526101c08a0151868203610200880152613dba565b6101e0890151858203610220870152613dba565b92610cdb610cc7610cb36102008b01968751858203610240870152613dba565b6102208b0151848203610260860152613dba565b6102408a0151838203610280850152613dba565b90610260890151906102a0818403910152613dba565b81516001600160a01b039081166044860152602083015181166064860152604083015181166084860152606083015160a4860152608083015160c486015260a083015160e486015260c083015161010486015260e0909201519091166101248401526102405190839081900381734a63105e7d0df8fc25250568fcb6f397388504965af49283156102a157610240519261024051946111f3575b5060d05480845260001981146110ee5760010160d05560cf54600160401b8110156111d95783610dc4826001610dca940160cf55613bb8565b90614d08565b60cf5460001981019081116110ee578351610240515260dc602052604061024051205560e0830160018060a01b03815116610240515260d1602052610e17604061024051208551906152ac565b60018060a01b03905116610240515260d1602052604061024051205460001981019081116110ee578351610240515260dd60205260406102405120556040830160018060a01b03815116610240515260d2602052610e7d604061024051208551906152ac565b60018060a01b03815116610240515260d2602052604061024051205460001981019081116110ee578451610240515260de602052604061024051205561018087015160018060a01b0360d65416734a63105e7d0df8fc25250568fcb6f397388504963b1561029257604051630d16a28560e01b81526080600482015291610f2490610f0b6084850189614af4565b60d3602486015284810360031901604486015290615253565b906064830152818061024051920381734a63105e7d0df8fc25250568fcb6f397388504965af480156102a1576111be575b50516001600160a01b031615806111aa575b611108575b505060cf546000198101915081116110ee57610f8a610f9091613bb8565b5061490b565b90815192610fa960c06104686024840184600401614f85565b610fb582600401613ae4565b6040848101805160608701516101808b0151935192996001600160a01b039586169690951694937f7eb8abd0eb2f629d9150adef2d047584ab0d0830368a1d5d5243955f73f884e79392839261100d92918c856149c2565b0390a461102860c06104688551936024810190600401614f85565b60018060a01b0360a08501511695608084015160088110156110d4578451602080870151604080516001600160a01b039c8d168152928301939093529181019190915260a0986110ab95606095909116917f68c1d3a775fd37da20ef77475b9074ee3b57e4d73d94e046e892720b5043ff72908690a40151608084015190614f9b565b9260c0830151925190519151926040519485526020850152604084015260608301526080820152f35b634e487b7160e01b61024051526021600452602461024051fd5b634e487b7160e01b61024051526011600452602461024051fd5b60d95460e09091015160a084015160c0949094015192516001600160a01b03948516949093918116921690813b1561029257604051948593630ba6e0bb60e31b855260048501526024840152604483015260806064830152818061117461024051956084830190613dba565b039161024051905af180156102a15761118f575b8080610f6c565b6102405161119c91613b97565b610240516102925783611188565b5060d9546001600160a01b03161515610f67565b610240516111cb91613b97565b610240516102925787610f55565b634e487b7160e01b61024051526041600452602461024051fd5b925092503d8061024051843e6112098184613b97565b82018281039060c082126102925783516001600160401b0381116102925760a091611235918601613e79565b91601f1901126102925760a06040519361124e85613b60565b6020810151855260408101516020860152606081015160408601526080810151606086015201516008811015610292576080840152919286610d8b565b61024051610ad3565b61024051610aa8565b9093506112bf91503d8061024051833e6112b78183613b97565b81019061425f565b9284610a30565b60405163036be76f60e61b8152600490fd5b90506020813d60201161130a575b816112f360209383613b97565b810103126102925761130490614252565b836106e6565b3d91506112e6565b3461029257604036600319011261029257602061133e611330613a04565b611338613a1f565b90614f4c565b604051908152f35b346102925760403660031901126102925761135f613a04565b6024359060018060a01b0316610240515260d2602052604061024051208054821015610292576020916103b091613aa6565b34610292576102405136600319011261029257602060ce54604051908152f35b3461029257610240513660031901126102925760d5546040516001600160a01b039091168152602090f35b34610292576102405136600319011261029257602060ca54604051908152f35b34610292576040366003190112610292576004356024356001600160401b0381116102925761142f903690600401613a76565b61143a92919261512b565b6114426151c5565b81610240515260dc60205261145d6040610240512054613bb8565b506007810180549192916001600160a01b0316330361165457604051602081019061149b8161148d868a86614ee6565b03601f198101835282613b97565b51902084610240515260d3602052604061024051206040516020810191816040810191602085528054809352606082019260608160051b840101916102405152602061024051209361024051905b82821061161957505050611506925003601f198101835282613b97565b5190200361151a575b600180556102405180f35b6115238361490b565b60d654734a63105e7d0df8fc25250568fcb6f39738850496916001600160a01b0390911690823b1561029257604051630d16a28560e01b81526080600482015292839161159290611578906084850190614af4565b60d36024850152838103600319016044850152878b6147b0565b9260648301528180610240519403915af480156102a1576115fe575b507f7eae323942fab89b6464d6efc98661e980ff52b318d132930eed2b31b0fbf38c92600a4291015560018060a01b03905416936115f160405192839283614ee6565b0390a3808080808061150f565b6102405161160b91613b97565b6102405161029257856115ae565b91935091602060026116446040600194605f198b820301875289548152818582015201848901614888565b96019201920185939194926114e9565b60405163014d683360e41b8152600490fd5b3461029257610240513660031901126102925760d4546040516001600160a01b039091168152602090f35b34610292576060366003190112610292576116aa613a04565b6116b2613a1f565b604435906116be614fcb565b734a63105e7d0df8fc25250568fcb6f39738850496803b15610292576040516393ed1f6560e01b81526102405160cd60048301526001600160a01b03868116602484015284166044830152606482018590529091829060849082905af480156102a157611768575b506040519182526001600160a01b039081169216907f938d851f75d7ce6b1bbe32add24a326e1d035a93c033dcfff2bff0a48d4337cd90602090a36102405180f35b6102405161177591613b97565b610240516102925783611726565b34610292576102405136600319011261029257602060cb54604051908152f35b3461029257610240513660031901126102925760e0546040516001600160a01b039091168152602090f35b610160366003190112610292576044356101008190526001600160a01b0381169003610292576064356001600160401b03811161029257611813903690600401613a76565b9060c05260a4356001600160401b03811161029257611836903690600401613a49565b9060a05260c4356001600160401b03811161029257611859903690600401613a49565b6101e0526101c05260e4356001600160401b03811161029257611880903690600401613a49565b6101605261018052610104356001600160401b038111610292576118a8903690600401613a49565b60e05261012052610124356001600160401b038111610292576118cf903690600401613a76565b90610144356001600160401b038111610292576118f0903690600401613a76565b906118f961512b565b6119016151c5565b60cf54151580612546575b1561253457600435610240515260dc602052611931610f8a6040610240512054613bb8565b61020081905260e001516001600160a01b031633036116545760c061020051015160243510156125225760d7546001600160a01b031690813b1561029257611995946040519586948593849363f197ce3560e01b8552610240519860048601613c81565b039134905af180156102a15761250f575b506040516101a08190526001600160401b03608082019081119111176111d95760806101a05101604052610240516101a051526102405160206101a05101526102405160406101a05101526102405160606101a05101526020610200510151151560606101a051015260c06102005101516102405150610240515061024051506102405150670de0b6b3a764000060243502906024358204670de0b6b3a7640000146024351517156110ee57670de0b6b3a76400008204602435036124fa57611a73600182901c83614f9b565b9182106124e557611a83916152e1565b6101a080518290525160600151156124db57611aa6906020610200510151615301565b60206101a0510152611ac460806102005101516101a0515190615301565b60406101a051015260405190611ad982613af8565b610240518252606060208084018290526101a05160408101516102205290810151910151156124d2576102005160a0810151606090910151611b27916001600160a01b039182169116614f4c565b60d65460d75460d5546101a0516060908101516102005160a0810151920151604051631dae49b960e21b81526001600160a01b039384166004820152908316602482015290151598909490939282169282169116602085604481855afa9485156102a157610240519561249e575b5060cb549560606101a051015115159760405161014052610140516102a06101405101106001600160401b036102a0610140510111176111d9576102a061014051016040526024356101405152610220516020610140510152604061014051015260018060a01b0361010051166060610140510152611c138b613f52565b611c206040519182613b97565b806080528b815260208101608052368c60051b60c05101116102925760c0515b8c60051b60c0510181106122885750611d079a9b50608061014051015260843560a061014051015260c061014051015260e061014051015261010061014051015261012061014051015261014080510152610240515061024051506102405150602095604051611cb08882613b97565b6102405181526101606101405101526101806101405101526101a06101405101526101c06101405101526101e06101405101526102405161020061014051015261024051610220610140510152369060a051613d60565b610240610140510152611d203660e05161012051613d60565b610260610140510152611d3a366101605161018051613d60565b61028061014051015260405190630b2aa09960e11b8252606060048301526101408280611ee5611d706064830161020051614af4565b600319838203016024840152845151815260208551015186820152604085510151604082015260018060a01b03606086510151166060820152611ece611eb9611e4a611e35611dd160808a5101516102a060808801526102a0870190614b9e565b60a08a51015160a087015260c08a51015160c087015260018060a01b0360e08b5101511660e087015260018060a01b036101008b5101511661010087015260018060a01b036101208b5101511661012087015289805101518682038b880152614ceb565b61016089510151858203610160870152613dba565b6101808851015115156101808501526101a0885101516101a08501526101c0885101516101c08501526101e08851015115156101e08501526102008851015161020085015260018060a01b03610220895101511661022085015261024088510151848203610240860152613dba565b61026087510151838203610260850152613dba565b906102808651015190610280818403910152613dba565b6102405160448301520381734a63105e7d0df8fc25250568fcb6f397388504965af49182156102a15761024051926121b5575b50611f2c60243560c061020051015161482b565b60c0610200510152611f4c60206101a0510151602061020051015161482b565b6020610200510152611f6c60406101a0510151608061020051015161482b565b6102008051608001919091525160400180516001600160a01b03166121ac5760015b6102005160c081015160a09091015160d75460d85460d6549394735a1235cd7f233b024273dcace98f966e0e7c79189490936001600160a01b03918216939282169282169116853b156102925760405196636727ed3d60e11b88526004880152602487015260448601526064850152608484015260068110156110d45760a483015260e060c4830152816102405191818061203360e482016101e0516101c051613bd7565b03915af480156102a157612199575b50600435610240515260dc825261206c6120626040610240512054613bb8565b6102005191614d08565b60018060a01b039051169060018060a01b0360606102005101511660018060a01b0360a0610200510151166080610200510151602061020051015190865192858801519460608901519660405198895288015260408701526024356060870152608086015260a085015260c084015260e083015261010082015233907fda47f84a849dfb28125ae28a0bf305b75e72bff27796fc4bca36e2f848b0a0e661012060043592a360c081015160e082015191906001600160a01b031660088310156110d4576101008201516101209290920151604080516001600160a01b0390931683526020830193909352918101919091523390600435907f68c1d3a775fd37da20ef77475b9074ee3b57e4d73d94e046e892720b5043ff729080606081015b0390a4600180556102405180f35b610240516121a691613b97565b83612042565b61024051611f8e565b909150610140813d8211612280575b816121d26101409383613b97565b81010312610292576040519061014082016001600160401b038111838210176111d95760405280518252828101518383015260408101516040830152606081015160608301526080810151600581101561029257608083015261223760a08201613e14565b60a083015261224860c08201613e14565b60c083015260e0810151906008821015610292576101209160e084015261010081015161010084015201516101208201529082611f18565b3d91506121c4565b80356001600160401b0381116102925760c0510160408136031261029257604051906122b382613af8565b803582526020810135906001600160401b038211610292570136601f820112156102925780356122e281613f52565b916122f06040519384613b97565b81835260208084019260051b820101903682116102925760208101925b828410612333575050505090602092918382015260805152816080510160805201611c40565b83356001600160401b0381116102925782016040601f198236030112610292576040519061236082613af8565b61236c60208201613a35565b825260408101356001600160401b03811161029257602091010136601f8201121561029257803561239c81613f52565b916123aa6040519384613b97565b81835260208084019260051b820101903682116102925760208101925b8284106123e757505050509181602093848094015281520193019261230d565b83356001600160401b038111610292578201906060601f198336030112610292576040519161241583613b29565b60208101356001600160401b0381116102925760209082010136601f820112156102925761244a903690602081359101613d60565b83526040810135602084015260608101356001600160401b0381116102925760209101019036601f830112156102925760209261248e849336908581359101613d60565b60408201528152019301926123c7565b9094506020813d6020116124ca575b816124ba60209383613b97565b810103126102925751938a611b95565b3d91506124ad565b61024051611b27565b5061024051611aa6565b630a77254f60e01b6102405152600461024051fd5b631550e8b760e01b6102405152600461024051fd5b6102405161251c91613b97565b826119a6565b60405163242e4c2b60e11b8152600490fd5b604051631eab2a3b60e31b8152600490fd5b50600435610240515260dc6020526125646040610240512054613bb8565b50546004351461190c565b34610292576102405136600319011261029257602060ff606554166040519015158152f35b34610292576020366003190112610292576125ad613a04565b6125b56150e2565b60018060a01b03166bffffffffffffffffffffffff60a01b60d954161760d9556102405161024051f35b3461029257610240513660031901126102925760d9546040516001600160a01b039091168152602090f35b6020366003190112610292576004356001600160401b03811161029257806004019061026060031982360301126102925761264361512b565b61264b615181565b6126536151c5565b60018060a01b0360d7541661022482019261266e8482613ddf565b9061024485019361267f8585613ddf565b9190813b15610292576126ae946040519586948593849363f197ce3560e01b8552610240519860048601613c81565b039134905af180156102a15761313d575b5060d65460d75460405163069102a360e01b815260606004820152734a63105e7d0df8fc25250568fcb6f397388504969690956001600160a01b039384169592909316939092612937906129309061291d6102046129156128f36128d18d6128b161289061286f8c61285c6128508c61283d6101246127ba61279b8b610260606461274a8b8061459b565b9201528c61278761277161275e8480613bf8565b60606102c4860152610324850191613bd7565b9260208101356102e48401526040810190613c29565b916103046102c319828603019101526145c3565b6127a86024860189613c29565b8d83036063190160848f0152906145c3565b926001600160a01b036127cf60448301613a35565b1660a48c0152606481013560c48c01526001600160a01b036127f360848301613a35565b1660e48c015260a48101356101048c015260c4810135828c015261281960e482016147a3565b15156101448c015261282e61010482016147a3565b15156101648c01520185613c29565b898303606319016101848b0152906147b0565b916101448d0190613bf8565b868303606319016101a488015290613bd7565b61287d6101648b018e613bf8565b858303606319016101c487015290613bd7565b61289e6101848a018d613bf8565b848303606319016101e486015290613bd7565b906128c06101a489018c613bf8565b918760631982860301910152613bd7565b8d6128e06101c488018b613bf8565b9161022460631982860301910152613bd7565b8c6129026101e487018a613bf8565b9161024460631982860301910152613bd7565b920185613bf8565b8a8303606319016102648c015290613bd7565b9382613c29565b60631988860301610284890152808552602085019460208260051b820101958361024051925b8484106130b25750505050505061298b929161297891613c29565b868303606319016102a488015290613c5d565b9160248401526044830152818061024051920381855af49081156102a15761024051906102405192613092575b506040516129c581613b60565b610240805182528051602083015280516040830152805160608301525160809091015260d65460d75460d55460e08501516001600160a01b039384169491841693919290911690156130885760408501515160a0830151612a32916001600160a01b039182169116614f4c565b935b6101a08601511561307e57606083015160a0840151612a5f916001600160a01b039182169116614f4c565b945b606084015161024080516001600160a01b03928316905260c960209081528151604080822060a08a01518616909252915290518190205460d854915194919092169290612aad85613b7b565b8452602084019485526040808501978852606085019889526080850191825260a085019283526102405160c0860190815260e086019490945251637e5eae6360e11b8152610140600482015297612b086101448a0188614af4565b976003198a8a030160248b01528a516102c08a526102c08a01612b2a91614b9e565b60208c0151908a810360208c0152612b4191614b9e565b60408c0151600160a01b6001900381511660408c0152602081015160608c01526040015160808b015260608c015160a08b015260808c015160c08b015260a08c015160e08b015260c08c015115156101008b015260e08c015115156101208b01526101008c015115156101408b01526101208c015115156101608b01526101408c01516101808b0152600160a01b600190036101608d0151166101a08b01526101808c0151908a81036101c08c0152612bf991615253565b6101a08c015115156101e08b01526101c08c0151908a81036102008c0152612c2091613dba565b6101e08c0151908a81036102208c0152612c3991613dba565b986102008c01998a5190828103610240840152612c5591613dba565b6102208d015190828103610260840152612c6e91613dba565b6102408d015190828103610280840152612c8791613dba565b6102608d015191808203906102a00152612ca091613dba565b86516001600160a01b0390811660448d01529751881660648c01529051871660848b0152905160a48a0152905160c4890152905160e4880152905161010487015260e0909101519091166101248501526102405190849081900381885af49485156102a15761024051936102405196612fe6575b5060d05480855260001981146110ee5760010160d05560cf54600160401b8110156111d95784610dc4826001612d4d940160cf55613bb8565b60cf5460001981019081116110ee578451610240515260dc602052604061024051205560e0840160018060a01b03815116610240515260d1602052612d9a604061024051208651906152ac565b60018060a01b03905116610240515260d1602052604061024051205460001981019081116110ee578451610240515260dd6020526040610240512055604084019060018060a01b03825116610240515260d2602052612e01604061024051208651906152ac565b60018060a01b03825116610240515260d2602052604061024051205460001981019081116110ee578551610240515260de602052604061024051205561018086015160018060a01b0360d6541690823b1561029257604051630d16a28560e01b815260806004820152928391612e7e90610f0b608485018b614af4565b9260648301528180610240519403915af480156102a157612fd3575b50516001600160a01b03161580612fbf575b612f25575b505060cf54600019810193915083116110ee577f7eb8abd0eb2f629d9150adef2d047584ab0d0830368a1d5d5243955f73f884e7612ef4610f8a61218b95613bb8565b9182519260018060a01b0360e0820151169586956101806060604086015195015191015190604051948594856149c2565b60d95460e09091015160a084015160c0949094015192516001600160a01b03948516949093918116921690813b1561029257604051948593630ba6e0bb60e31b8552600485015260248401526044830152608060648301528180612f9161024051956084830190613dba565b039161024051905af180156102a157612fac575b8080612eb1565b61024051612fb991613b97565b82612fa5565b5060d9546001600160a01b03161515612eac565b61024051612fe091613b97565b86612e9a565b935094503d8061024051853e612ffc8185613b97565b83018381039060c082126102925784516001600160401b0381116102925760a091613028918701613e79565b91601f1901126102925760a06040519461304186613b60565b6020810151865260408101516020870152606081015160408701526080810151606087015201516008811015610292576080850152929486612d14565b6102405194612a61565b6102405193612a34565b90506130ab91503d8061024051833e6112b78183613b97565b90836129b8565b909192939497601f198282030184526130cb8984613c29565b808352602083019060208160051b850101938361024051905b838210613107575050505050506020806001929a0194019401929493919061295d565b90919293949560208061312f600193601f19888203018a526131298b87613bf8565b90613bd7565b9801960194939201906130e4565b6102405161314a91613b97565b846126bf565b3461029257610240513660031901126102925760d6546040516001600160a01b039091168152602090f35b60c0366003190112610292576044356001600160401b038111610292576131a6903690600401613a49565b906064356001600160401b038111610292576131c6903690600401613a49565b9190926084356001600160401b038111610292576131e8903690600401613a76565b60a4959195356001600160401b0381116102925761320a903690600401613a76565b61321597919761512b565b61321d615181565b6132256151c5565b600435610240515260dc6020526132426040610240512054613bb8565b509760018060a01b0360d7541690813b156102925761327d946040519586948593849363f197ce3560e01b8552610240519860048601613c81565b039134905af180156102a157613525575b5060d65460d75460d55460058801546003890154604051631dae49b960e21b81526001600160a01b039283166004820181905291831660248201819052958316999383169892909416959294929391926020836044818a5afa9283156102a15761024051936134ef575b506133069060cb5492614f4c565b9160cc549360018060a01b0360d8541695604051998a6101608101106001600160401b036101608d0111176111d95761339a9261338a916101608d016040526024358d5260208d019e8f5260408d019b8c5260608d019d8e5260808d0195865260a08d0196875260c08d0197885260e08d019889526101008d01998a523691613d60565b976101208b019889523691613d60565b986101408901998a52734a63105e7d0df8fc25250568fcb6f397388504963b1561029257604080516319498bc360e21b8152600481018e90526024810191909152985160448a015299516001600160a01b0390811660648a01529651871660848901529851861660a4880152975160c4870152965160e48601529551610104850152945161012484015293511661014482015291516101606101648401528290819061344b906101a4830190613dba565b925181840360431901610184830152610240519361346891613dba565b0381734a63105e7d0df8fc25250568fcb6f397388504965af480156102a1576134dc575b5080547f21b98b9d6fb2ba49b9764bd2d465450aa78464b9f9ddf9846bca69ce7e0e969560406001808060a01b0360078601541694015481519060243582526020820152a3600180556102405180f35b610240516134e991613b97565b8161348c565b9092506020813d60201161351d575b8161350b60209383613b97565b810103126102925751916133066132f8565b3d91506134fe565b6102405161353291613b97565b8561328e565b3461029257608036600319011261029257613551613a04565b613559613a1f565b60643591604435613568614fcb565b734a63105e7d0df8fc25250568fcb6f3973885049693843b1561029257604051635323cdb560e11b81526102405160c960048301526001600160a01b0385811660248401528616604483015260648201849052608482018390529095869060a49082905af49182156102a1577fa73bd1411d7cefafbca3c39b9dfc6d263533671c6b50b796f2f4a31eb63a4711956136329361363c575b50604080516001600160a01b03958616815295909416602086015292840192909252606083019190915281906080820190565b0390a16102405180f35b6102405161364991613b97565b866135ff565b346102925760203660031901126102925760043561366b61507a565b801515806136c0575b156136ae578060cc557fbaff4968173e39aed42691fb55671c5a687a43b3b936a129ab68365a791d006d6102405161024051a26102405180f35b6040516333e75a7f60e11b8152600490fd5b50670de0b6b3a76400008110613674565b3461029257610240513660031901126102925760d7546040516001600160a01b039091168152602090f35b34610292576102405136600319011261029257602060cc54604051908152f35b3461029257610240513660031901126102925760db546040516001600160a01b039091168152602090f35b346102925760203660031901126102925760043561376361507a565b670de0b6b3a764000081116137a7578060ca557f9cc578c88f7a198c4ccced6601ea38cdc1fc2dbacf6b476b689ba507edbe94e66102405161024051a26102405180f35b60405163948b756d60e01b8152600490fd5b34610292576102405136600319011261029257602060d054604051908152f35b346102925760203660031901126102925760043563ffffffff60e01b81168091036102925760209063caaf0f5760e01b811490811561381e575b506040519015158152f35b6301ffc9a760e01b14905082613813565b34610292576020366003190112610292576004356001600160401b0381116102925736602382011215610292578060040135906001600160401b038211610292576024810190602436918460071b010111610292579061388d614fcb565b61024051734a63105e7d0df8fc25250568fcb6f3973885049692905b8281106138b7576102405180f35b6138c5610453828585613ad4565b906138d66020610468838787613ad4565b9160406138e4838787613ad4565b013560606138f3848888613ad4565b013593873b1561029257604051635323cdb560e11b81526102405160c960048301526001600160a01b03948516602483015291909316604484015260648301919091526084820193909352918260a481885af49182156102a1576001926139f1575b507fa73bd1411d7cefafbca3c39b9dfc6d263533671c6b50b796f2f4a31eb63a4711613985610453838787613ad4565b6139956020610468858989613ad4565b906139e860406139a6868a8a613ad4565b013560606139b5878b8b613ad4565b604080516001600160a01b0396871681529690951660208701529385019190915290910135606083015281906080820190565b0390a1016138a9565b610240516139fe91613b97565b85613955565b600435906001600160a01b0382168203613a1a57565b600080fd5b602435906001600160a01b0382168203613a1a57565b35906001600160a01b0382168203613a1a57565b9181601f84011215613a1a578235916001600160401b038311613a1a5760208381860195010111613a1a57565b9181601f84011215613a1a578235916001600160401b038311613a1a576020808501948460051b010111613a1a57565b8054821015613abe5760005260206000200190600090565b634e487b7160e01b600052603260045260246000fd5b9190811015613abe5760071b0190565b356001600160a01b0381168103613a1a5790565b604081019081106001600160401b03821117613b1357604052565b634e487b7160e01b600052604160045260246000fd5b606081019081106001600160401b03821117613b1357604052565b61018081019081106001600160401b03821117613b1357604052565b60a081019081106001600160401b03821117613b1357604052565b61010081019081106001600160401b03821117613b1357604052565b90601f801991011681019081106001600160401b03821117613b1357604052565b60cf54811015613abe5760cf600052600c602060002091020190600090565b908060209392818452848401376000828201840152601f01601f1916010190565b9035601e1982360301811215613a1a5701602081359101916001600160401b038211613a1a578136038313613a1a57565b9035601e1982360301811215613a1a5701602081359101916001600160401b038211613a1a578160051b36038313613a1a57565b81835290916001600160fb1b038311613a1a5760209260051b809284830137010190565b949391929094836040820160408352526060810160608560051b8301019487916000905b828210613cc65750505050613cc39495506020818503910152613c5d565b90565b90919296605f19858203018252613cdd888b613c29565b808352602083019060208160051b85010193836000905b838210613d1557505050505050602080600192990192019201909291613ca5565b909192939495602080613d37600193601f19888203018a526131298b87613bf8565b980196019493920190613cf4565b6001600160401b038111613b1357601f01601f191660200190565b929192613d6c82613d45565b91613d7a6040519384613b97565b829481845281830111613a1a578281602093846000960137010152565b60005b838110613daa5750506000910152565b8181015183820152602001613d9a565b90602091613dd381518092818552858086019101613d97565b601f01601f1916010190565b903590601e1981360301821215613a1a57018035906001600160401b038211613a1a57602001918160051b36038313613a1a57565b51906001600160a01b0382168203613a1a57565b90929192613e3581613d45565b91613e436040519384613b97565b829482845282820111613a1a576020613e5d930190613d97565b565b9080601f83011215613a1a578151613cc392602001613e28565b919061018083820312613a1a5760405190613e9382613b44565b83518252602080850151908301526040840151919384926001600160a01b0381168103613a1a576040840152613ecb60608201613e14565b606084015260808101516080840152613ee660a08201613e14565b60a084015260c081015160c0840152613f0160e08201613e14565b60e0840152610100810151610100840152610120810151610120840152610140810151610140840152610160810151916001600160401b038311613a1a5761016092613f4d9201613e5f565b910152565b6001600160401b038111613b135760051b60200190565b610280526103005261030051601f61028051011215613a1a5761028051516102c052613faa613f9a6102c051613f52565b6040516102e0526102e051613b97565b6102e051506102c0516102e0515260206102e051016103005160206102c05160051b61028051010111613a1a57602061028051016102a0525b60206102c05160051b6102805101016102a0511061400357506102e05190565b6102a051516001600160401b038111613a1a5761028051016040601f198261030051030112613a1a576040519061403982613af8565b602081015182526040810151906001600160401b038211613a1a5761030051603f828401011215613a1a57602082820101519061407582613f52565b926140836040519485613b97565b82845260208085019360051b8183850101010191610300518311613a1a57604081830101935b8385106140d157505050505090602092918382015281520160206102a051016102a052613fe3565b84516001600160401b038111613a1a57602084840101016040601f198261030051030112613a1a576040519061410682613af8565b61411260208201613e14565b82526040810151906001600160401b038211613a1a5761030051603f828401011215613a1a57602082820101519061414982613f52565b926141576040519485613b97565b82845260208085019360051b8183850101010191610300518311613a1a57604081830101935b83851061419e575050505050918160209384809401528152019401936140a9565b84516001600160401b038111613a1a57602084840101016060601f198261030051030112613a1a57604051916141d383613b29565b60208201516001600160401b038111613a1a5760209083010161030051601f82011215613a1a5761030051815161420c92602001613e28565b8352604082015160208401526060820151926001600160401b038411613a1a576142426020949385809561030051920101613e5f565b604082015281520194019361417d565b51908115158203613a1a57565b9190604083820312613a1a5782516001600160401b038111613a1a5781614287918501613e79565b926020810151906001600160401b038211613a1a5701808203916102c08312613a1a576040519261028084018481106001600160401b03821117613b135760405282516001600160401b038111613a1a57826142e4918501613f69565b84526020830151906001600160401b038211613a1a57614308836060938601613f69565b6020860152603f190112613a1a5760405161432281613b29565b61432e60408401613e14565b81526060830151602082015260808301516040820152604084015260a0820151606084015260c0820151608084015260e082015160a08401526143746101008301614252565b60c08401526143866101208301614252565b60e08401526143986101408301614252565b6101008401526143ab6101608301614252565b6101208401526101808201516101408401526143ca6101a08301613e14565b6101608401526101c08201516001600160401b038111613a1a57820181601f82011215613a1a578051906143fd82613f52565b9161440b6040519384613b97565b80835260208084019160051b83010191848311613a1a5760208101915b83831061453157505050506101808401526144466101e08301614252565b6101a08401526102008201516001600160401b038111613a1a578161446c918401613e5f565b6101c08401526102208201516001600160401b038111613a1a5781614492918401613e5f565b6101e08401526102408201516001600160401b038111613a1a57816144b8918401613e5f565b6102008401526102608201516001600160401b038111613a1a57816144de918401613e5f565b6102208401526102808201516001600160401b038111613a1a5781614504918401613e5f565b6102408401526102a08201516001600160401b038111613a1a576145289201613e5f565b61026082015290565b82516001600160401b038111613a1a578201906040828803601f190112613a1a576040519061455f82613af8565b602083015182526040830151916001600160401b038311613a1a5761458c89602080969581960101613e5f565b83820152815201920191614428565b9035605e1982360301811215613a1a570190565b9035603e1982360301811215613a1a570190565b91906102a0526020816102a0518152019060206102a05160051b82010192806102e0526000610280525b6102a05161028051106146005750505090565b909192601f198382030184526146196102e051836145af565b61462f6040830191803584526020810190613c29565b839192604060208396015252606081019160608460051b8301016103005280916000925b8584106146845750505050505060206103005193816102e051016102e05201916001610280510161028052906145ed565b605f1982610300510301855261469a81846145af565b61030051604001906146c7906001600160a01b036146b782613a35565b1661030051526020810190613c29565b80926040602061030051015252606061030051019060608360051b61030051010161026052806102c0526000915b83831061471a5750505050602080600192610260516103005201950193019293614653565b6020600191605f1961030051610260510301815261478c61473e6102c0518661459b565b61477861476261474e8380613bf8565b606061026051526060610260510191613bd7565b9185810135866102605101526040810190613bf8565b906102605183036040610260510152613bd7565b61026052816102c051016102c052019201916146f5565b35908115158203613a1a57565b90602083828152019260208260051b82010193836000925b8484106147d85750505050505090565b90919293949560208061481b600193601f19868203018852604061480c6147ff8d8a6145af565b8035845285810190613bf8565b91909281868201520191613bd7565b98019401940192949391906147c8565b9190820391821161483857565b634e487b7160e01b600052601160045260246000fd5b90600182811c9216801561487e575b602083101461486857565b634e487b7160e01b600052602260045260246000fd5b91607f169161485d565b600092918154916148988361484e565b80835292600181169081156148ee57506001146148b457505050565b60009081526020812093945091925b8383106148d4575060209250010190565b6001816020929493945483858701015201910191906148c3565b915050602093945060ff929192191683830152151560051b010190565b90600b61016060405161491d81613b44565b845481526001850154602082015260028501546001600160a01b03908116604080840191909152600387015482166060840152600487015460808401526005870154821660a0840152600687015460c0840152600787015490911660e083015260088601546101008301526009860154610120830152600a860154610140830152519094909285916149be9185916149b791839101614888565b0384613b97565b0152565b919290610160614a79916080855280516080860152602081015160a086015260018060a01b0360408201511660c086015260018060a01b0360608201511660e0860152608081015161010086015260018060a01b0360a08201511661012086015260c081015161014086015260018060a01b0360e082015116828601526101008101516101808601526101208101516101a08601526101408101516101c086015201516101806101e0850152610200840190613dba565b92602083015260408201526060818303910152815180825260208201916020808360051b8301019401926000915b838310614ab657505050505090565b9091929394602080614ae5600193601f198682030187526040838b518051845201519181858201520190613dba565b97019301930191939290614aa7565b90610180610160613cc393805184526020810151602085015260018060a01b03604082015116604085015260018060a01b0360608201511660608501526080810151608085015260018060a01b0360a08201511660a085015260c081015160c085015260018060a01b0360e08201511660e0850152610100810151610100850152610120810151610120850152610140810151610140850152015191816101608201520190613dba565b9080602083519182815201916020808360051b8301019401926000915b838310614bca57505050505090565b9091929394601f1982820301835285519060206040820192805183520151916040602083015282518091526060820190602060608260051b85010194019260005b828110614c2c57505050505060208060019297019301930191939290614bbb565b9091929394605f1983820301855285516020604083019160018060a01b0381511684520151916040602082015282518092526060810190602060608460051b8301019401926000915b818310614c9657505050505060208060019297019501910192919092614c0b565b9091929394602080614cde600193605f198682030189528951906040614cc58351606084526060840190613dba565b9285810151868401520151906040818403910152613dba565b9701950193019190614c75565b9060406020613cc393805184520151918160208201520190613dba565b90929192614ed057825181556020830151600182015560408301516002820180546001600160a01b039283166001600160a01b03199182161790915560608501516003840180549184169183169190911790556080850151600484015560a085015160058401805491841691831691909117905560c0850151600684015560e085015160078401805491909316911617905561010083015160088201556101208301516009820155610140830151600a8201556101609092015180519092600b01906001600160401b038111613b1357614de2825461484e565b601f8111614e88575b506020601f8211600114614e265781929394600092614e1b575b50508160011b916000199060031b1c1916179055565b015190503880614e05565b601f1982169083600052806000209160005b818110614e7057509583600195969710614e57575b505050811b019055565b015160001960f88460031b161c19169055388080614e4d565b9192602060018192868b015181550194019201614e38565b826000526020600020601f830160051c81019160208410614ec6575b601f0160051c01905b818110614eba5750614deb565b60008155600101614ead565b9091508190614ea4565b634e487b7160e01b600052600060045260246000fd5b9180602084016020855252604083019260408260051b82010193836000925b848410614f155750505050505090565b909192939495602080614f3c600193603f19868203018852604061480c6147ff8d8a6145af565b9801940194019294939190614f05565b6001600160a01b03908116600090815260cd60209081526040808320949093168252929092529020548015614f7e5790565b5060ca5490565b9035906101be1981360301821215613a1a570190565b9190820180921161483857565b9190811015613abe576060020190565b8181029291811591840414171561483857565b60d454604051632474521560e21b81527f5a7d4408f4759dddd7fdfd0d21abd99341dc2f52cda14804988a9b2df20766d8600482015233602482015290602090829060449082906001600160a01b03165afa90811561506e57600091615034575b50156112c657565b90506020813d602011615066575b8161504f60209383613b97565b81010312613a1a5761506090614252565b3861502c565b3d9150615042565b6040513d6000823e3d90fd5b60d454604051632474521560e21b81527fc0fc8e4dc5cff6febdf550b80d566f654e2baf1a02ea1060208c2f8ab2dd1b63600482015233602482015290602090829060449082906001600160a01b03165afa90811561506e576000916150345750156112c657565b60d454604051632474521560e21b81526000600482015233602482015290602090829060449082906001600160a01b03165afa90811561506e576000916150345750156112c657565b60026001541461513c576002600155565b60405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606490fd5b60ff6065541661518d57565b60405162461bcd60e51b815260206004820152601060248201526f14185d5cd8589b194e881c185d5cd95960821b6044820152606490fd5b60df54604051630723eb0360e51b815233600482015290602090829060249082906001600160a01b03165afa90811561506e57600091615219575b5061520757565b6040516333df015b60e01b8152600490fd5b90506020813d60201161524b575b8161523460209383613b97565b81010312613a1a5761524590614252565b38615200565b3d9150615227565b9080602083519182815201916020808360051b8301019401926000915b83831061527f57505050505090565b909192939460208061529d600193601f198682030187528951614ceb565b97019301930191939290615270565b8054600160401b811015613b13576152c991600182018155613aa6565b819291549060031b91821b91600019901b1916179055565b81156152eb570490565b634e487b7160e01b600052601260045260246000fd5b600091801591821561535e575b50501561534d576706f05b59d3b2000081019081811161483857811061533c57670de0b6b3a7640000900490565b630a77254f60e01b60005260046000fd5b631550e8b760e01b60005260046000fd5b9150915061537661536f8383614fb8565b92836152e1565b14388061530e56fea264697066735822122062f14c50dff4d12ac150b1cec279799f273bc46e4de9d4eac729aed62267051e64736f6c634300081a0033
Loading...
Loading
Loading...
Loading
Net Worth in USD
$0.00
Net Worth in ETH
0
Multichain Portfolio | 35 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
Loading...
Loading
Loading...
Loading
Loading...
Loading
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.