Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion contracts/amm/libraries/types/AmmInternalTypes.sol
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ library AmmInternalTypes {
struct OpenSwapItem {
/// @notice Swap ID
uint32 swapId;
/// @notcie Next swap ID in linked list
/// @notice Next swap ID in linked list
uint32 nextSwapId;
/// @notice Previous swap ID in linked list
uint32 previousSwapId;
Expand Down
161 changes: 145 additions & 16 deletions contracts/chains/ethereum/amm-commons/AmmCloseSwapLens.sol
Original file line number Diff line number Diff line change
@@ -1,48 +1,60 @@
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity 0.8.26;

import "@openzeppelin/contracts/utils/Address.sol";

import "../../../interfaces/types/IporTypes.sol";
import "../../../interfaces/types/AmmTypes.sol";
import "../../../interfaces/IIporOracle.sol";
import "../../../interfaces/IAmmCloseSwapLens.sol";
import "../../../interfaces/IAmmCloseSwapService.sol";
import "../../../interfaces/IAmmStorage.sol";
import "../../../libraries/errors/IporErrors.sol";
import "../../../libraries/IporContractValidator.sol";
import "../../../libraries/AmmCloseSwapServicePoolConfigurationLib.sol";
import "../../../base/amm/libraries/SwapLogicBaseV1.sol";
import "../../../base/amm/libraries/SwapCloseLogicLibBaseV1.sol";
import "../../../amm/libraries/SwapCloseLogicLib.sol";
import "../../../base/types/AmmTypesBaseV1.sol";
import {StorageLibBaseV1} from "../../../base/libraries/StorageLibBaseV1.sol";
import "../../../base/amm/libraries/SwapLogicBaseV1.sol";
import "../../../base/amm/libraries/SwapCloseLogicLibBaseV1.sol";
import "../../../base/amm/services/AmmCloseSwapServiceBaseV1.sol";

/// @dev Legacy AmmCloseSwapLens for DAI/USDT/USDC which uses legacy SwapCloseLogicLib (not BaseV1)
/// @dev It is not recommended to use service contract directly, should be used only through IporProtocolRouter.
contract AmmCloseSwapLens is IAmmCloseSwapLens {
using Address for address;
using IporContractValidator for address;
using SwapLogicBaseV1 for AmmTypesBaseV1.Swap;
using AmmCloseSwapServicePoolConfigurationLib for IAmmCloseSwapLens.AmmCloseSwapServicePoolConfiguration;

address public immutable usdt;
address public immutable usdc;
address public immutable dai;
address public immutable stETH;
address public immutable iporOracle;
address public immutable messageSigner;
address public immutable spreadRouter;

constructor(address iporOracle_, address messageSigner_, address spreadRouter_) {
constructor(
address usdt_,
address usdc_,
address dai_,
address stETH_,
address iporOracle_
) {
usdt = usdt_.checkAddress();
usdc = usdc_.checkAddress();
dai = dai_.checkAddress();
stETH = stETH_.checkAddress();
iporOracle = iporOracle_.checkAddress();
messageSigner = messageSigner_.checkAddress();
spreadRouter = spreadRouter_.checkAddress();
}

function getAmmCloseSwapServicePoolConfiguration(
address asset
address asset_
) external view override returns (AmmCloseSwapServicePoolConfiguration memory) {
StorageLibBaseV1.AssetServicesValue memory servicesCfg = StorageLibBaseV1.getAssetServicesStorage().value[
asset
asset_
];

if (servicesCfg.ammCloseSwapService != address(0)) {
return IAmmCloseSwapService(servicesCfg.ammCloseSwapService).getPoolConfiguration();
} else {
revert IporErrors.UnsupportedAsset(IporErrors.ASSET_NOT_SUPPORTED, asset);
revert IporErrors.UnsupportedAsset(IporErrors.ASSET_NOT_SUPPORTED, asset_);
}
}

Expand All @@ -54,6 +66,36 @@ contract AmmCloseSwapLens is IAmmCloseSwapLens {
uint256 closeTimestamp,
AmmTypes.CloseSwapRiskIndicatorsInput calldata riskIndicatorsInput
) external view override returns (AmmTypes.ClosingSwapDetails memory closingSwapDetails) {
if (asset == usdt || asset == usdc || asset == dai) {
closingSwapDetails = _getClosingSwapDetailsForStable(
asset,
account,
direction,
swapId,
closeTimestamp,
riskIndicatorsInput
);
} else if (asset == stETH) {
closingSwapDetails = _getClosingSwapDetailsForStEth(
account,
direction,
swapId,
closeTimestamp,
riskIndicatorsInput
);
} else {
revert IporErrors.UnsupportedAsset(IporErrors.ASSET_NOT_SUPPORTED, asset);
}
}

function _getClosingSwapDetailsForStable(
address asset,
address account,
AmmTypes.SwapDirection direction,
uint256 swapId,
uint256 closeTimestamp,
AmmTypes.CloseSwapRiskIndicatorsInput calldata riskIndicatorsInput
) internal view returns (AmmTypes.ClosingSwapDetails memory closingSwapDetails) {
StorageLibBaseV1.AssetServicesValue memory servicesCfg = StorageLibBaseV1.getAssetServicesStorage().value[
asset
];
Expand All @@ -76,6 +118,7 @@ contract AmmCloseSwapLens is IAmmCloseSwapLens {
require(swap.id > 0, AmmErrors.INCORRECT_SWAP_ID);

int256 swapPnlValueToDate;

if (direction == AmmTypes.SwapDirection.PAY_FIXED_RECEIVE_FLOATING) {
swapPnlValueToDate = SwapLogicBaseV1.calculatePnlPayFixed(
swap.openTimestamp,
Expand All @@ -86,7 +129,7 @@ contract AmmCloseSwapLens is IAmmCloseSwapLens {
block.timestamp,
accruedIpor.ibtPrice
);
} else {
} else if (direction == AmmTypes.SwapDirection.PAY_FLOATING_RECEIVE_FIXED) {
swapPnlValueToDate = SwapLogicBaseV1.calculatePnlReceiveFixed(
swap.openTimestamp,
swap.collateral,
Expand All @@ -96,6 +139,8 @@ contract AmmCloseSwapLens is IAmmCloseSwapLens {
block.timestamp,
accruedIpor.ibtPrice
);
} else {
revert(AmmErrors.UNSUPPORTED_DIRECTION);
}

(closingSwapDetails.closableStatus, closingSwapDetails.swapUnwindRequired) = SwapCloseLogicLibBaseV1
Expand Down Expand Up @@ -133,8 +178,8 @@ contract AmmCloseSwapLens is IAmmCloseSwapLens {
closingSwapDetails.pnlValue
) = SwapCloseLogicLib.calculateSwapUnwindWhenUnwindRequired(
AmmTypes.UnwindParams({
messageSigner: messageSigner,
spreadRouter: spreadRouter,
messageSigner: StorageLibBaseV1.getMessageSignerStorage().value,
spreadRouter: poolCfg.spread,
ammStorage: poolCfg.ammStorage,
ammTreasury: poolCfg.ammTreasury,
direction: direction,
Expand All @@ -150,4 +195,88 @@ contract AmmCloseSwapLens is IAmmCloseSwapLens {
closingSwapDetails.pnlValue = swapPnlValueToDate;
}
}

function _getClosingSwapDetailsForStEth(
address account,
AmmTypes.SwapDirection direction,
uint256 swapId,
uint256 closeTimestamp,
AmmTypes.CloseSwapRiskIndicatorsInput calldata riskIndicatorsInput
) internal view returns (AmmTypes.ClosingSwapDetails memory closingSwapDetails) {
StorageLibBaseV1.AssetServicesValue memory servicesCfg = StorageLibBaseV1.getAssetServicesStorage().value[
stETH
];

if (servicesCfg.ammCloseSwapService == address(0)) {
revert IporErrors.UnsupportedAsset(IporErrors.ASSET_NOT_SUPPORTED, stETH);
}

IAmmCloseSwapLens.AmmCloseSwapServicePoolConfiguration memory poolCfg = IAmmCloseSwapService(
servicesCfg.ammCloseSwapService
).getPoolConfiguration();

IporTypes.AccruedIpor memory accruedIpor = IIporOracle(iporOracle).getAccruedIndex(
block.timestamp,
poolCfg.asset
);

AmmTypesBaseV1.Swap memory swap = IAmmStorageBaseV1(poolCfg.ammStorage).getSwap(direction, swapId);

require(swap.id > 0, AmmErrors.INCORRECT_SWAP_ID);

int256 swapPnlValueToDate = swap.calculatePnl(block.timestamp, accruedIpor.ibtPrice);

(closingSwapDetails.closableStatus, closingSwapDetails.swapUnwindRequired) = SwapCloseLogicLibBaseV1
.getClosableStatusForSwap(
AmmTypesBaseV1.ClosableSwapInput({
account: account,
asset: poolCfg.asset,
closeTimestamp: closeTimestamp,
swapBuyer: swap.buyer,
swapOpenTimestamp: swap.openTimestamp,
swapCollateral: swap.collateral,
swapTenor: swap.tenor,
swapState: swap.state,
swapPnlValueToDate: swapPnlValueToDate,
minLiquidationThresholdToCloseBeforeMaturityByCommunity: poolCfg
.minLiquidationThresholdToCloseBeforeMaturityByCommunity,
minLiquidationThresholdToCloseBeforeMaturityByBuyer: poolCfg
.minLiquidationThresholdToCloseBeforeMaturityByBuyer,
timeBeforeMaturityAllowedToCloseSwapByCommunity: poolCfg
.timeBeforeMaturityAllowedToCloseSwapByCommunity,
timeBeforeMaturityAllowedToCloseSwapByBuyer: poolCfg.getTimeBeforeMaturityAllowedToCloseSwapByBuyer(
swap.tenor
),
timeAfterOpenAllowedToCloseSwapWithUnwinding: poolCfg
.getTimeAfterOpenAllowedToCloseSwapWithUnwinding(swap.tenor)
})
);

if (closingSwapDetails.swapUnwindRequired == true) {
(
closingSwapDetails.swapUnwindPnlValue,
closingSwapDetails.swapUnwindOpeningFeeAmount,
closingSwapDetails.swapUnwindFeeLPAmount,
closingSwapDetails.swapUnwindFeeTreasuryAmount,
closingSwapDetails.pnlValue
) = SwapCloseLogicLibBaseV1.calculateSwapUnwindWhenUnwindRequired(
AmmTypesBaseV1.UnwindParams({
asset: poolCfg.asset,
messageSigner: StorageLibBaseV1.getMessageSignerStorage().value,
spread: poolCfg.spread,
ammStorage: poolCfg.ammStorage,
ammTreasury: poolCfg.ammTreasury,
closeTimestamp: closeTimestamp,
swapPnlValueToDate: swapPnlValueToDate,
indexValue: accruedIpor.indexValue,
swap: swap,
unwindingFeeRate: poolCfg.unwindingFeeRate,
unwindingFeeTreasuryPortionRate: poolCfg.unwindingFeeTreasuryPortionRate,
riskIndicatorsInputs: riskIndicatorsInput
})
);
} else {
closingSwapDetails.pnlValue = swapPnlValueToDate;
}
}
}
2 changes: 1 addition & 1 deletion contracts/interfaces/ILiquidityMiningLens.sol
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ interface ILiquidityMiningLens {
uint128 compositeMultiplierCumulativePrevBlock;
/// @notice lpToken account's balance
uint128 lpTokenBalance;
/// @notive PowerUp is a result of logarithmic equastion,
/// @notice PowerUp is a result of logarithmic equastion,
/// @dev powerUp < 100 *10^18
uint72 powerUp;
/// @notice balance of Power Tokens delegated to LiquidityMining
Expand Down
32 changes: 20 additions & 12 deletions test/utils/factory/IporProtocolFactory.sol
Original file line number Diff line number Diff line change
Expand Up @@ -784,9 +784,11 @@ contract IporProtocolFactory is Test {

deployerContracts.ammCloseSwapLens = address(
new AmmCloseSwapLens({
iporOracle_: address(amm.iporOracle),
messageSigner_: messageSignerAddress,
spreadRouter_: address(amm.spreadRouter)
usdt_: address(amm.usdt.asset),
usdc_: address(amm.usdc.asset),
dai_: address(amm.dai.asset),
stETH_: address(amm.stEth.asset),
iporOracle_: address(amm.iporOracle)
})
);

Expand Down Expand Up @@ -1018,9 +1020,11 @@ contract IporProtocolFactory is Test {

deployerContracts.ammCloseSwapLens = address(
new AmmCloseSwapLens({
iporOracle_: address(iporProtocol.iporOracle),
messageSigner_: messageSignerAddress,
spreadRouter_: address(iporProtocol.spreadRouter)
usdt_: address(iporProtocol.asset),
usdc_: address(_fakeAsset),
dai_: address(_fakeAsset),
stETH_: address(_fakeAsset),
iporOracle_: address(iporProtocol.iporOracle)
})
);

Expand Down Expand Up @@ -1214,9 +1218,11 @@ contract IporProtocolFactory is Test {

deployerContracts.ammCloseSwapLens = address(
new AmmCloseSwapLens({
iporOracle_: address(iporProtocol.iporOracle),
messageSigner_: messageSignerAddress,
spreadRouter_: address(iporProtocol.spreadRouter)
usdt_: address(_fakeAsset),
usdc_: address(iporProtocol.asset),
dai_: address(_fakeAsset),
stETH_: address(_fakeAsset),
iporOracle_: address(iporProtocol.iporOracle)
})
);

Expand Down Expand Up @@ -1412,9 +1418,11 @@ contract IporProtocolFactory is Test {

deployerContracts.ammCloseSwapLens = address(
new AmmCloseSwapLens({
iporOracle_: address(iporProtocol.iporOracle),
messageSigner_: messageSignerAddress,
spreadRouter_: address(iporProtocol.spreadRouter)
usdt_: address(_fakeAsset),
usdc_: address(_fakeAsset),
dai_: address(iporProtocol.asset),
stETH_: address(_fakeAsset),
iporOracle_: address(iporProtocol.iporOracle)
})
);

Expand Down
Loading