Skip to content

Commit

Permalink
add DC_ETHx_Burner
Browse files Browse the repository at this point in the history
  • Loading branch information
1kresh committed Jul 22, 2024
1 parent 0adad5e commit 0d908f2
Show file tree
Hide file tree
Showing 10 changed files with 584 additions and 26 deletions.
140 changes: 140 additions & 0 deletions src/contracts/burners/DC_ETHx_Burner.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.25;

import {SelfDestruct} from "src/contracts/SelfDestruct.sol";

import {IDC_ETHx_Burner} from "src/interfaces/burners/DC_ETHx/IDC_ETHx_Burner.sol";
import {IStaderStakePoolsManager} from "src/interfaces/burners/DC_ETHx/IStaderStakePoolsManager.sol";
import {IUserWithdrawalManager} from "src/interfaces/burners/DC_ETHx/IUserWithdrawalManager.sol";
import {IStaderConfig} from "src/interfaces/burners/DC_ETHx/IStaderConfig.sol";

import {IDefaultCollateral} from "@symbiotic/collateral/interfaces/defaultCollateral/IDefaultCollateral.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {Math} from "@openzeppelin/contracts/utils/math/Math.sol";
import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";

contract DC_ETHx_Burner is IDC_ETHx_Burner {
using Math for uint256;
using EnumerableSet for EnumerableSet.UintSet;

/**
* @inheritdoc IDC_ETHx_Burner
*/
address public immutable COLLATERAL;

/**
* @inheritdoc IDC_ETHx_Burner
*/
address public immutable ASSET;

/**
* @inheritdoc IDC_ETHx_Burner
*/
address public immutable STADER_CONFIG;

/**
* @inheritdoc IDC_ETHx_Burner
*/
address public immutable USER_WITHDRAW_MANAGER;

/**
* @inheritdoc IDC_ETHx_Burner
*/
address public immutable STAKE_POOLS_MANAGER;

EnumerableSet.UintSet private _requestIds;

constructor(address collateral, address staderConfig) {
COLLATERAL = collateral;

ASSET = IDefaultCollateral(collateral).asset();

STADER_CONFIG = staderConfig;
USER_WITHDRAW_MANAGER = IStaderConfig(STADER_CONFIG).getUserWithdrawManager();
STAKE_POOLS_MANAGER = IStaderConfig(STADER_CONFIG).getStakePoolManager();

IERC20(ASSET).approve(USER_WITHDRAW_MANAGER, type(uint256).max);
}

/**
* @inheritdoc IDC_ETHx_Burner
*/
function requestIdsLength() external view returns (uint256) {
return _requestIds.length();
}

/**
* @inheritdoc IDC_ETHx_Burner
*/
function requestIds(uint256 index, uint256 maxRequestIds) external view returns (uint256[] memory requestIds_) {
uint256 length = Math.min(index + maxRequestIds, _requestIds.length()) - index;

requestIds_ = new uint256[](length);
for (uint256 i; i < length;) {
requestIds_[i] = _requestIds.at(index);
unchecked {
++i;
++index;
}
}
}

/**
* @inheritdoc IDC_ETHx_Burner
*/
function triggerWithdrawal(uint256 maxRequests) external returns (uint256 firstRequestId, uint256 lastRequestId) {
IDefaultCollateral(COLLATERAL).withdraw(address(this), IERC20(COLLATERAL).balanceOf(address(this)));
uint256 amount = IERC20(ASSET).balanceOf(address(this));

uint256 maxWithdrawalAmount = IStaderStakePoolsManager(STAKE_POOLS_MANAGER).previewDeposit(
IStaderConfig(STADER_CONFIG).getMaxWithdrawAmount()
);
uint256 minWithdrawalAmount = IStaderStakePoolsManager(STAKE_POOLS_MANAGER).previewDeposit(
IStaderConfig(STADER_CONFIG).getMinWithdrawAmount()
) + 1;

uint256 requests = amount / maxWithdrawalAmount;
if (amount % maxWithdrawalAmount >= minWithdrawalAmount) {
requests += 1;
}
requests = Math.min(requests, maxRequests);

if (requests == 0) {
revert InsufficientWithdrawal();
}

uint256 requestsMinusOne = requests - 1;
firstRequestId = IUserWithdrawalManager(USER_WITHDRAW_MANAGER).nextRequestId();
lastRequestId = firstRequestId + requestsMinusOne;
uint256 requestId = firstRequestId;
for (; requestId < lastRequestId; ++requestId) {
_requestIds.add(requestId);
IUserWithdrawalManager(USER_WITHDRAW_MANAGER).requestWithdraw(maxWithdrawalAmount, address(this));
}
_requestIds.add(requestId);
IUserWithdrawalManager(USER_WITHDRAW_MANAGER).requestWithdraw(
Math.min(amount - requestsMinusOne * maxWithdrawalAmount, maxWithdrawalAmount), address(this)
);

emit TriggerWithdrawal(msg.sender, firstRequestId, lastRequestId);

return (firstRequestId, lastRequestId);
}

/**
* @inheritdoc IDC_ETHx_Burner
*/
function triggerBurn(uint256 requestId) external {
if (!_requestIds.remove(requestId)) {
revert InvalidRequestId();
}

IUserWithdrawalManager(USER_WITHDRAW_MANAGER).claim(requestId);

new SelfDestruct{value: address(this).balance}();

emit TriggerBurn(msg.sender, requestId);
}

receive() external payable {}
}
4 changes: 3 additions & 1 deletion src/contracts/burners/DC_swETH_Burner.sol
Original file line number Diff line number Diff line change
Expand Up @@ -74,8 +74,10 @@ contract DC_swETH_Burner is IDC_swETH_Burner, IERC721Receiver {
uint256 amount = IERC20(ASSET).balanceOf(address(this));

uint256 maxWithdrawalAmount = ISwEXIT(SWEXIT).withdrawRequestMaximum();
uint256 minWithdrawalAmount = ISwEXIT(SWEXIT).withdrawRequestMinimum();

uint256 requests = amount / maxWithdrawalAmount;
if (amount % maxWithdrawalAmount >= ISwEXIT(SWEXIT).withdrawRequestMinimum()) {
if (amount % maxWithdrawalAmount >= minWithdrawalAmount) {
requests += 1;
}
requests = Math.min(requests, maxRequests);
Expand Down
74 changes: 74 additions & 0 deletions src/interfaces/burners/DC_ETHx/IDC_ETHx_Burner.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.25;

interface IDC_ETHx_Burner {
error InsufficientWithdrawal();
error InvalidRequestId();

/**
* @notice Emitted when a withdrawal is triggered.
* @param caller caller of the function
* @param firstRequestId first request ID that was created
* @param lastRequestId last request ID that was created
*/
event TriggerWithdrawal(address indexed caller, uint256 firstRequestId, uint256 lastRequestId);

/**
* @notice Emitted when a burn is triggered.
* @param caller caller of the function
* @param requestId request ID of the withdrawal that was claimed and burned
*/
event TriggerBurn(address indexed caller, uint256 requestId);

/**
* @notice Get an address of the Default Collateral contract.
*/
function COLLATERAL() external view returns (address);

/**
* @notice Get an address of the collateral's asset.
*/
function ASSET() external view returns (address);

/**
* @notice Get an address of the Stader Config contract.
*/
function STADER_CONFIG() external view returns (address);

/**
* @notice Get an address of the User Withdraw Manager contract.
*/
function USER_WITHDRAW_MANAGER() external view returns (address);

/**
* @notice Get an address of the Stake Pools Manager contract.
*/
function STAKE_POOLS_MANAGER() external view returns (address);

/**
* @notice Get the number of unprocessed request IDs.
*/
function requestIdsLength() external view returns (uint256);

/**
* @notice Get a list of unprocessed request IDs.
* @param index index of the first request ID
* @param maxRequestIds maximum number of request IDs to return
* @return requestIds request IDs
*/
function requestIds(uint256 index, uint256 maxRequestIds) external view returns (uint256[] memory requestIds);

/**
* @notice Trigger a withdrawal of ETH from the collateral's underlying asset.
* @param maxRequests maximum number of withdrawal requests to create
* @return firstRequestId first request ID that was created
* @return lastRequestId last request ID that was created
*/
function triggerWithdrawal(uint256 maxRequests) external returns (uint256 firstRequestId, uint256 lastRequestId);

/**
* @notice Trigger a claim and a burn of ETH.
* @param requestId request ID of the withdrawal to process
*/
function triggerBurn(uint256 requestId) external;
}
14 changes: 14 additions & 0 deletions src/interfaces/burners/DC_ETHx/IStaderConfig.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.25;

interface IStaderConfig {
function getUserWithdrawManager() external view returns (address);

function getStakePoolManager() external view returns (address);

function getMinWithdrawAmount() external view returns (uint256);

function getMaxWithdrawAmount() external view returns (uint256);

function getMinBlockDelayToFinalizeWithdrawRequest() external view returns (uint256);
}
10 changes: 10 additions & 0 deletions src/interfaces/burners/DC_ETHx/IStaderStakePoolsManager.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.25;

interface IStaderStakePoolsManager {
// returns the amount of share corresponding to `_assets` assets
function previewDeposit(uint256 _assets) external view returns (uint256);

// return the amount of assets corresponding to `_shares` shares
function previewWithdraw(uint256 _shares) external view returns (uint256);
}
28 changes: 28 additions & 0 deletions src/interfaces/burners/DC_ETHx/IUserWithdrawalManager.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.25;

interface IUserWithdrawalManager {
function nextRequestId() external view returns (uint256);

function nextRequestIdToFinalize() external view returns (uint256);

/**
* @notice put a withdrawal request
* @param _ethXAmount amount of ethX shares to withdraw
* @param _owner owner of withdraw request to redeem
* @return requestId
*/
function requestWithdraw(uint256 _ethXAmount, address _owner) external returns (uint256);

/**
* @notice finalize user requests
* @dev check for safeMode to finalizeRequest
*/
function finalizeUserWithdrawalRequest() external;

/**
* @notice transfer the eth of finalized request to recipient and delete the request
* @param _requestId request id to redeem
*/
function claim(uint256 _requestId) external;
}
Loading

0 comments on commit 0d908f2

Please sign in to comment.