From d6cdd896b96bd9ff31439691de4324b2d24b2f02 Mon Sep 17 00:00:00 2001 From: TomasCImach Date: Fri, 1 Nov 2024 15:34:22 -0300 Subject: [PATCH 1/4] add liquidate callback function --- contracts/OverlayV1Market.sol | 6 ++++++ contracts/interfaces/IOverlayV1Token.sol | 1 + .../callback/IOverlayMarketLiquidateCallback.sol | 12 ++++++++++++ 3 files changed, 19 insertions(+) create mode 100644 contracts/interfaces/callback/IOverlayMarketLiquidateCallback.sol diff --git a/contracts/OverlayV1Market.sol b/contracts/OverlayV1Market.sol index f31baf79..c134dc97 100644 --- a/contracts/OverlayV1Market.sol +++ b/contracts/OverlayV1Market.sol @@ -8,6 +8,7 @@ import "./interfaces/IOverlayV1Factory.sol"; import "./interfaces/IOverlayV1Market.sol"; import "./interfaces/IOverlayV1Token.sol"; import "./interfaces/feeds/IOverlayV1Feed.sol"; +import "./interfaces/callback/IOverlayMarketLiquidateCallback.sol"; import "./libraries/FixedCast.sol"; import "./libraries/FixedPoint.sol"; @@ -507,6 +508,11 @@ contract OverlayV1Market is IOverlayV1Market, Pausable { pos.isLong ? oiLongShares : oiShortShares ); + // if the owner of the potision has LIQUIDATE_CALLBACK_ROLE, make a callback + if (ov.hasRole(LIQUIDATE_CALLBACK_ROLE, owner)) { + IOverlayMarketLiquidateCallback(owner).overlayMarketLiquidateCallback(positionId); + } + // burn the pnl for the position + insurance margin ov.burn(cost - value + marginToBurn); diff --git a/contracts/interfaces/IOverlayV1Token.sol b/contracts/interfaces/IOverlayV1Token.sol index 83e6128d..8640e621 100644 --- a/contracts/interfaces/IOverlayV1Token.sol +++ b/contracts/interfaces/IOverlayV1Token.sol @@ -10,6 +10,7 @@ bytes32 constant GOVERNOR_ROLE = keccak256("GOVERNOR"); bytes32 constant GUARDIAN_ROLE = keccak256("GUARDIAN"); bytes32 constant PAUSER_ROLE = keccak256("PAUSER"); bytes32 constant RISK_MANAGER_ROLE = keccak256("RISK_MANAGER"); +bytes32 constant LIQUIDATE_CALLBACK_ROLE = keccak256("LIQUIDATE_CALLBACK"); interface IOverlayV1Token is IAccessControl, IERC20 { // mint/burn diff --git a/contracts/interfaces/callback/IOverlayMarketLiquidateCallback.sol b/contracts/interfaces/callback/IOverlayMarketLiquidateCallback.sol new file mode 100644 index 00000000..06a45ef1 --- /dev/null +++ b/contracts/interfaces/callback/IOverlayMarketLiquidateCallback.sol @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.5.0; + +/// @title Callback for OverlayV1Market#liquidate +interface IOverlayMarketLiquidateCallback { + /// @notice Called to `owner` after executing a liquidation. + /// amount0Delta and amount1Delta can both be 0 if no tokens were swapped. + /// @param positionId The positionId of the liquidated position + function overlayMarketLiquidateCallback( + uint256 positionId + ) external; +} \ No newline at end of file From 1bfd6b5435ba556131b20f018b9ea6d3dce9e108 Mon Sep 17 00:00:00 2001 From: Tomas C Imach Date: Wed, 6 Nov 2024 16:17:15 -0300 Subject: [PATCH 2/4] move liquidate callback to the top, before pos state is updated --- contracts/OverlayV1Market.sol | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/contracts/OverlayV1Market.sol b/contracts/OverlayV1Market.sol index c134dc97..b4bb474f 100644 --- a/contracts/OverlayV1Market.sol +++ b/contracts/OverlayV1Market.sol @@ -433,6 +433,14 @@ contract OverlayV1Market is IOverlayV1Market, Pausable { uint256 marginToBurn; uint256 marginRemaining; Position.Info memory pos; + + // if the owner of the potision has LIQUIDATE_CALLBACK_ROLE, make a callback + // must be executed before the new postion state si stored + // moved to the top of the script to avoid stack too deep + if (ov.hasRole(LIQUIDATE_CALLBACK_ROLE, owner)) { + IOverlayMarketLiquidateCallback(owner).overlayMarketLiquidateCallback(positionId); + } + // avoids stack too deep { // check position exists @@ -508,11 +516,6 @@ contract OverlayV1Market is IOverlayV1Market, Pausable { pos.isLong ? oiLongShares : oiShortShares ); - // if the owner of the potision has LIQUIDATE_CALLBACK_ROLE, make a callback - if (ov.hasRole(LIQUIDATE_CALLBACK_ROLE, owner)) { - IOverlayMarketLiquidateCallback(owner).overlayMarketLiquidateCallback(positionId); - } - // burn the pnl for the position + insurance margin ov.burn(cost - value + marginToBurn); From a81715091989b1bd4077501351885aff67a2d3b2 Mon Sep 17 00:00:00 2001 From: Tomas C Imach Date: Thu, 19 Dec 2024 13:35:55 -0300 Subject: [PATCH 3/4] wrap overlayMarketLiquidateCallback on ty/catch to avoid revert on failure --- contracts/OverlayV1Market.sol | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/contracts/OverlayV1Market.sol b/contracts/OverlayV1Market.sol index b4bb474f..a835a056 100644 --- a/contracts/OverlayV1Market.sol +++ b/contracts/OverlayV1Market.sol @@ -151,6 +151,8 @@ contract OverlayV1Market is IOverlayV1Market, Pausable { /// @param oiShort oiShort after public update event Update(uint256 oiLong, uint256 oiShort); + event LiquidateCallbackFailed(address owner, uint256 positionId); + constructor() { (address _ov, address _feed, address _factory) = IOverlayV1Deployer(msg.sender).parameters(); @@ -435,10 +437,14 @@ contract OverlayV1Market is IOverlayV1Market, Pausable { Position.Info memory pos; // if the owner of the potision has LIQUIDATE_CALLBACK_ROLE, make a callback - // must be executed before the new postion state si stored - // moved to the top of the script to avoid stack too deep + // must be executed before the new postion state is stored + // attempt the callback without reverting on failure if (ov.hasRole(LIQUIDATE_CALLBACK_ROLE, owner)) { - IOverlayMarketLiquidateCallback(owner).overlayMarketLiquidateCallback(positionId); + try IOverlayMarketLiquidateCallback(owner).overlayMarketLiquidateCallback(positionId) { + + } catch { + emit LiquidateCallbackFailed(owner, positionId); + } } // avoids stack too deep From 0d23b52e6bef239c1e4753f182c01e184e973bd8 Mon Sep 17 00:00:00 2001 From: "Arthur G." Date: Fri, 17 Jan 2025 16:39:00 +0100 Subject: [PATCH 4/4] refactor: ov to ovl --- contracts/OverlayV1Market.sol | 7 +++---- .../callback/IOverlayMarketLiquidateCallback.sol | 6 ++---- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/contracts/OverlayV1Market.sol b/contracts/OverlayV1Market.sol index 45a355dd..5b6be343 100644 --- a/contracts/OverlayV1Market.sol +++ b/contracts/OverlayV1Market.sol @@ -439,10 +439,9 @@ contract OverlayV1Market is IOverlayV1Market, Pausable { // if the owner of the potision has LIQUIDATE_CALLBACK_ROLE, make a callback // must be executed before the new postion state is stored // attempt the callback without reverting on failure - if (ov.hasRole(LIQUIDATE_CALLBACK_ROLE, owner)) { - try IOverlayMarketLiquidateCallback(owner).overlayMarketLiquidateCallback(positionId) { - - } catch { + if (ovl.hasRole(LIQUIDATE_CALLBACK_ROLE, owner)) { + try IOverlayMarketLiquidateCallback(owner).overlayMarketLiquidateCallback(positionId) {} + catch { emit LiquidateCallbackFailed(owner, positionId); } } diff --git a/contracts/interfaces/callback/IOverlayMarketLiquidateCallback.sol b/contracts/interfaces/callback/IOverlayMarketLiquidateCallback.sol index 06a45ef1..a6c48f41 100644 --- a/contracts/interfaces/callback/IOverlayMarketLiquidateCallback.sol +++ b/contracts/interfaces/callback/IOverlayMarketLiquidateCallback.sol @@ -6,7 +6,5 @@ interface IOverlayMarketLiquidateCallback { /// @notice Called to `owner` after executing a liquidation. /// amount0Delta and amount1Delta can both be 0 if no tokens were swapped. /// @param positionId The positionId of the liquidated position - function overlayMarketLiquidateCallback( - uint256 positionId - ) external; -} \ No newline at end of file + function overlayMarketLiquidateCallback(uint256 positionId) external; +}