Skip to content
Open
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
46 changes: 46 additions & 0 deletions contracts/StakeManagerV2.sol
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ contract StakeManagerV2 is
ReentrancyGuardUpgradeable
{
using SafeERC20Upgradeable for IBnbX;
using SafeERC20Upgradeable for IERC20Upgradeable;

bytes32 public constant OPERATOR_ROLE = keccak256("OPERATOR_ROLE");
bytes32 public constant MANAGER_ROLE = keccak256("MANAGER_ROLE");
Expand All @@ -47,6 +48,9 @@ contract StakeManagerV2 is
uint256 public totalBnbxSupplyAtUndelegation;
bool public redemptionEnabled;

bool public assetCustodied;
uint256 public sweepToCustodyTimestamp;

// @custom:oz-upgrades-unsafe-allow constructor
constructor() {
_disableInitializers();
Expand Down Expand Up @@ -174,6 +178,7 @@ contract StakeManagerV2 is
/// @dev Caller must approve the contract to burn the specified BNBx amount before calling.
/// @dev The exchange rate is based on totalBnbUndelegated and totalBnbxSupplyAtUndelegation snapshot.
function redeemBnbxForBnb(uint256 _amountInBnbX) external override nonReentrant returns (uint256) {
if (assetCustodied) revert AssetCustodied();
if (!redemptionEnabled) revert RedemptionNotEnabled();
if (_amountInBnbX == 0) revert ZeroAmount();
if (totalBnbxSupplyAtUndelegation == 0) revert ZeroAmount();
Expand Down Expand Up @@ -439,6 +444,47 @@ contract StakeManagerV2 is
emit SetRedemptionEnabled(_enabled);
}

/// @notice Set the delay before `sweepToCustody` can be called.
/// @dev Can only be called by an address with the DEFAULT_ADMIN_ROLE.
function setCustodyDelay(uint256 _custodyDelay) external onlyRole(DEFAULT_ADMIN_ROLE) {
if (_custodyDelay == 0) revert ZeroCustodyDelay();
sweepToCustodyTimestamp = block.timestamp + _custodyDelay;
emit SetCustodyDelay(sweepToCustodyTimestamp);
}

/// @notice Sweep all BNB or ERC20 tokens to a custody address after the delay has elapsed.
/// @dev Can only be called by an address with the DEFAULT_ADMIN_ROLE.
/// @dev Pass `address(0)` as `_asset` to sweep native BNB.
function sweepToCustody(
address _asset,
address _custody
)
external
nonReentrant
onlyRole(DEFAULT_ADMIN_ROLE)
{
if (_custody == address(0)) revert ZeroAddress();
if (sweepToCustodyTimestamp == 0 || block.timestamp < sweepToCustodyTimestamp) {
revert CustodyDelayNotElapsed();
}

assetCustodied = true;

uint256 bal;
if (_asset == address(0)) {
bal = address(this).balance;
if (bal == 0) revert ZeroAmount();
(bool success,) = payable(_custody).call{ value: bal }("");
if (!success) revert TransferFailed();
} else {
bal = IERC20Upgradeable(_asset).balanceOf(address(this));
if (bal == 0) revert ZeroAmount();
IERC20Upgradeable(_asset).safeTransfer(_custody, bal);
}

emit SweptToCustody(_asset, _custody, bal);
}

/*//////////////////////////////////////////////////////////////
internal functions
//////////////////////////////////////////////////////////////*/
Expand Down
7 changes: 7 additions & 0 deletions contracts/interfaces/IStakeManagerV2.sol
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ interface IStakeManagerV2 {
error WithdrawalBelowMinimum();
error RedemptionNotEnabled();
error InsufficientBnbBalance();
error CustodyDelayNotElapsed();
error ZeroCustodyDelay();
error AssetCustodied();

function delegate(string calldata _referralId) external payable returns (uint256);
function requestWithdraw(uint256 _amount, string calldata _referralId) external returns (uint256);
Expand All @@ -49,6 +52,8 @@ interface IStakeManagerV2 {
function pause() external;
function unpause() external;
function setRedemptionEnabled(bool _enabled) external;
function setCustodyDelay(uint256 _custodyDelay) external;
function sweepToCustody(address _asset, address _custody) external;

function convertBnbToBnbX(uint256 _amount) external view returns (uint256);
function convertBnbXToBnb(uint256 _amountInBnbX) external view returns (uint256);
Expand All @@ -72,4 +77,6 @@ interface IStakeManagerV2 {
event ClaimedAllBnbFromAllOperators(uint256 _totalClaimedBnb);
event RedeemedBnbxForBnb(address indexed _account, uint256 _amountInBnbX, uint256 _amountInBnb);
event SetRedemptionEnabled(bool _enabled);
event SetCustodyDelay(uint256 _sweepToCustodyTimestamp);
event SweptToCustody(address indexed _asset, address indexed _custody, uint256 _amount);
}
Loading