Skip to content

Commit

Permalink
Merge pull request #966 from lidofinance/feat/separate-mints
Browse files Browse the repository at this point in the history
Separate mint shares
  • Loading branch information
folkyatina authored Feb 28, 2025
2 parents 3241ec7 + 130b59e commit cac27de
Show file tree
Hide file tree
Showing 5 changed files with 26 additions and 14 deletions.
11 changes: 8 additions & 3 deletions contracts/0.4.24/Lido.sol
Original file line number Diff line number Diff line change
Expand Up @@ -586,7 +586,7 @@ contract Lido is Versioned, StETHPermit, AragonApp {
* @dev can be called only by accounting
*/
function mintShares(address _recipient, uint256 _amountOfShares) public {
require(msg.sender == getLidoLocator().accounting() || msg.sender == getLidoLocator().vaultHub(), "APP_AUTH_FAILED");
_auth(getLidoLocator().accounting());
_whenNotStopped();

_mintShares(_recipient, _amountOfShares);
Expand Down Expand Up @@ -614,12 +614,14 @@ contract Lido is Versioned, StETHPermit, AragonApp {
* @notice Mint shares backed by external ether sources
* @param _recipient Address to receive the minted shares
* @param _amountOfShares Amount of shares to mint
* @dev Can be called only by accounting (authentication in mintShares method).
* @dev Can be called only by VaultHub
* NB: Reverts if the the external balance limit is exceeded.
*/
function mintExternalShares(address _recipient, uint256 _amountOfShares) external {
require(_recipient != address(0), "MINT_RECEIVER_ZERO_ADDRESS");
require(_amountOfShares != 0, "MINT_ZERO_AMOUNT_OF_SHARES");
_auth(getLidoLocator().vaultHub());
_whenNotStopped();

uint256 newExternalShares = EXTERNAL_SHARES_POSITION.getStorageUint256().add(_amountOfShares);
uint256 maxMintableExternalShares = _getMaxMintableExternalShares();
Expand All @@ -628,7 +630,10 @@ contract Lido is Versioned, StETHPermit, AragonApp {

EXTERNAL_SHARES_POSITION.setStorageUint256(newExternalShares);

mintShares(_recipient, _amountOfShares);
_mintShares(_recipient, _amountOfShares);
// emit event after minting shares because we are always having the net new ether under the hood
// for vaults we have new locked ether and for fees we have a part of rewards
_emitTransferAfterMintingShares(_recipient, _amountOfShares);

emit ExternalSharesMinted(_recipient, _amountOfShares, getPooledEthByShares(_amountOfShares));
}
Expand Down
2 changes: 1 addition & 1 deletion contracts/0.8.25/Accounting.sol
Original file line number Diff line number Diff line change
Expand Up @@ -341,7 +341,7 @@ contract Accounting {
);

if (_update.totalVaultsTreasuryFeeShares > 0) {
LIDO.mintExternalShares(LIDO_LOCATOR.treasury(), _update.totalVaultsTreasuryFeeShares);
_contracts.vaultHub.mintVaultsTreasuryFeeShares(LIDO_LOCATOR.treasury(), _update.totalVaultsTreasuryFeeShares);
}

_notifyRebaseObserver(_contracts.postTokenRebaseReceiver, _report, _pre, _update);
Expand Down
23 changes: 14 additions & 9 deletions contracts/0.8.25/vaults/VaultHub.sol
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ contract VaultHub is PausableUntilWithRoles {

/// @notice Lido stETH contract
IStETH public immutable STETH;
address public immutable accounting;
address public immutable ACCOUNTING;

/// @param _stETH Lido stETH contract
/// @param _connectedVaultsLimit Maximum number of vaults that can be connected simultaneously
Expand All @@ -85,7 +85,7 @@ contract VaultHub is PausableUntilWithRoles {
if (_relativeShareLimitBP > TOTAL_BASIS_POINTS) revert RelativeShareLimitBPTooHigh(_relativeShareLimitBP, TOTAL_BASIS_POINTS);

STETH = _stETH;
accounting = _accounting;
ACCOUNTING = _accounting;
CONNECTED_VAULTS_LIMIT = _connectedVaultsLimit;
RELATIVE_SHARE_LIMIT_BP = _relativeShareLimitBP;

Expand Down Expand Up @@ -363,27 +363,27 @@ contract VaultHub is PausableUntilWithRoles {
/// @notice Forces validator exit from the beacon chain when vault is unhealthy
/// @param _vault The address of the vault to exit validators from
/// @param _pubkeys The public keys of the validators to exit
/// @param _refundRecepient The address that will receive the refund for transaction costs
/// @param _refundRecipient The address that will receive the refund for transaction costs
/// @dev When the vault becomes unhealthy, anyone can force its validators to exit the beacon chain
/// This returns the vault's deposited ETH back to vault's balance and allows to rebalance the vault
function forceValidatorExit(
address _vault,
bytes calldata _pubkeys,
address _refundRecepient
address _refundRecipient
) external payable {
if (msg.value == 0) revert ZeroArgument("msg.value");
if (_vault == address(0)) revert ZeroArgument("_vault");
if (_pubkeys.length == 0) revert ZeroArgument("_pubkeys");
if (_refundRecepient == address(0)) revert ZeroArgument("_refundRecepient");
if (_refundRecipient == address(0)) revert ZeroArgument("_refundRecipient");
if (_pubkeys.length % PUBLIC_KEY_LENGTH != 0) revert InvalidPubkeysLength();
_requireUnhealthy(_vault);

uint256 numValidators = _pubkeys.length / PUBLIC_KEY_LENGTH;
uint64[] memory amounts = new uint64[](numValidators);

IStakingVault(_vault).triggerValidatorWithdrawal{value: msg.value}(_pubkeys, amounts, _refundRecepient);
IStakingVault(_vault).triggerValidatorWithdrawal{value: msg.value}(_pubkeys, amounts, _refundRecipient);

emit ForceValidatorExitTriggered(_vault, _pubkeys, _refundRecepient);
emit ForceValidatorExitTriggered(_vault, _pubkeys, _refundRecipient);
}

function _disconnect(address _vault) internal {
Expand Down Expand Up @@ -490,7 +490,7 @@ contract VaultHub is PausableUntilWithRoles {
uint256[] memory _locked,
uint256[] memory _treasureFeeShares
) external {
if (msg.sender != accounting) revert NotAuthorized("updateVaults", msg.sender);
if (msg.sender != ACCOUNTING) revert NotAuthorized("updateVaults", msg.sender);
VaultHubStorage storage $ = _getVaultHubStorage();

for (uint256 i = 0; i < _valuations.length; i++) {
Expand Down Expand Up @@ -522,6 +522,11 @@ contract VaultHub is PausableUntilWithRoles {
}
}

function mintVaultsTreasuryFeeShares(address _recipient, uint256 _amountOfShares) external {
if (msg.sender != ACCOUNTING) revert NotAuthorized("mintVaultsTreasuryFeeShares", msg.sender);
STETH.mintExternalShares(_recipient, _amountOfShares);
}

function _vaultAuth(address _vault, string memory _operation) internal view {
if (msg.sender != OwnableUpgradeable(_vault).owner()) revert NotAuthorized(_operation, msg.sender);
}
Expand Down Expand Up @@ -558,7 +563,7 @@ contract VaultHub is PausableUntilWithRoles {
event BurnedSharesOnVault(address indexed vault, uint256 amountOfShares);
event VaultRebalanced(address indexed vault, uint256 sharesBurned);
event VaultProxyCodehashAdded(bytes32 indexed codehash);
event ForceValidatorExitTriggered(address indexed vault, bytes pubkeys, address refundRecepient);
event ForceValidatorExitTriggered(address indexed vault, bytes pubkeys, address refundRecipient);

error StETHMintFailed(address vault);
error AlreadyHealthy(address vault);
Expand Down
2 changes: 2 additions & 0 deletions contracts/0.8.9/LidoLocator.sol
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ contract LidoLocator is ILidoLocator {

error ZeroAddress();

//solhint-disable immutable-vars-naming
address public immutable accountingOracle;
address public immutable depositSecurityModule;
address public immutable elRewardsVault;
Expand All @@ -52,6 +53,7 @@ contract LidoLocator is ILidoLocator {
address public immutable accounting;
address public immutable wstETH;
address public immutable vaultHub;
//solhint-enable immutable-vars-naming

/**
* @notice declare service locations
Expand Down
2 changes: 1 addition & 1 deletion test/0.8.25/vaults/vaulthub/vaulthub.forceExit.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ describe("VaultHub.sol:forceExit", () => {
it("reverts if zero refund recipient", async () => {
await expect(vaultHub.forceValidatorExit(vaultAddress, SAMPLE_PUBKEY, ZeroAddress, { value: 1n }))
.to.be.revertedWithCustomError(vaultHub, "ZeroArgument")
.withArgs("_refundRecepient");
.withArgs("_refundRecipient");
});

it("reverts if pubkeys are not valid", async () => {
Expand Down

0 comments on commit cac27de

Please sign in to comment.