Skip to content

Commit

Permalink
Merge pull request #3 from Stakingverse/Improve-natspec-comments
Browse files Browse the repository at this point in the history
Improve Natspec comments
  • Loading branch information
JordyDutch authored Jan 11, 2025
2 parents 7132ef8 + d02ba49 commit 0757993
Show file tree
Hide file tree
Showing 7 changed files with 228 additions and 84 deletions.
21 changes: 19 additions & 2 deletions src/ISLYX.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,26 @@
pragma solidity ^0.8.0;

interface ISLYX {
function getNativeTokenValue(uint256 stakingTokenAmount) external view returns (uint256);
/// @notice Calculate how many staked LYX (including accumulated rewards) in the vault can be obtained in exchange for `sLYXAmount`.
///
/// @dev Determine the value of sLYX in terms of the native token balance (LYX) staked in the vault.
/// This function calculates the amount of LYX backing an amount of sLYX.
/// Formula: (sLYX amount * total stake held by sLYX contract in Vault) / total sLYX minted.
///
/// @param sLyxAmount The amount of sLYX to convert to staked LYX.
/// @return The amount of staked LYX backing a specific sLYX amount (in wei).
function getNativeTokenValue(uint256 sLyxAmount) external view returns (uint256);

function getSLYXTokenValue(uint256 nativeTokenAmount) external view returns (uint256);
/// @notice Calculate how many sLYX tokens can be obtained when converting `stakedLyxAmount`.
///
/// @dev Calculate the amount of sLYX backing an amount of LYX.
/// Formula: (LYX amount * total sLYX minted) / total stake held by sLYX contract in Vault.
///
/// @param stakedLyxAmount The amount of staked LYX to calculate if exchanging to mint sLYX tokens.
/// @return The amount of sLYX backing a specific amount of staked LYX (in wei).
function getSLYXTokenValue(uint256 stakedLyxAmount) external view returns (uint256);

/// @dev Get the current sLYX / LYX exchange rate to calculate how many sLYX are backing a certain amount of LYX.
/// @return The amount of LYX backing 1 sLYX (in wei).
function getExchangeRate() external view returns (uint256);
}
80 changes: 80 additions & 0 deletions src/IVault.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity =0.8.22;

interface IVault {
event Deposited(address indexed account, address indexed beneficiary, uint256 amount);
event Withdrawn(address indexed account, address indexed beneficiary, uint256 amount);
event WithdrawalRequested(address indexed account, address indexed beneficiary, uint256 amount);
event Claimed(address indexed account, address indexed beneficiary, uint256 amount);
event DepositLimitChanged(uint256 previousLimit, uint256 newLimit);
event FeeChanged(uint32 previousFee, uint32 newFee);
event FeeRecipientChanged(address previousFeeRecipient, address newFeeRecipient);
event FeeClaimed(address indexed account, address indexed beneficiary, uint256 amount);
event RewardsDistributed(uint256 balance, uint256 rewards, uint256 fee);
event OracleEnabled(address indexed oracle, bool enabled);
event Rebalanced(
uint256 previousTotalStaked, uint256 previousTotalUnstaked, uint256 totalStaked, uint256 totalUnstaked
);
event StakeTransferred(address indexed from, address indexed to, uint256 amount, bytes data);
event OperatorChanged(address previousOperator, address newOperator);
event VaultRestrictionChanged(bool restricted);
event AllowedListChanged(address indexed depositor, bool indexed allowed);
event ValidatorRegistered(bytes pubkey, bytes signature, bytes32 depositDataRoot);

function depositLimit() external view returns (uint256);
function totalAssets() external view returns (uint256);
function totalShares() external view returns (uint256);

/// @dev Total amount of active stake for the vault on the beacon chain.
/// Increased by 32 LYX every time a new validator is registered for the vault.
/// Updated when the vault oracle rebalances the vault.
///
/// @return The total amount (in wei) of active stake for the vault on the beacon chain.
function totalStaked() external view returns (uint256);

/// @dev Total amount of inactive stake for the vault on the execution layer.
/// Increased by the amount of LYX deposited by users to the vault.
/// Decreased when a user withdraws its staked LYX from the vault, or decreased by 32 LYX every time a new validator is registered for the vault.
/// Updated when the vault oracle rebalances the vault.
///
/// @return The total amount (in wei) of inactive stake for the vault on the execution layer.
function totalUnstaked() external view returns (uint256);
function totalPendingWithdrawal() external view returns (uint256);
function totalValidatorsRegistered() external view returns (uint256);
function fee() external view returns (uint32);
function feeRecipient() external view returns (address);
function totalFees() external view returns (uint256);
function restricted() external view returns (bool);

/// @dev Get the total amount of staked LYX that was deposited by or for associated with `account`.
/// @param account The address to query the staked balance for.
/// @return The amount of LYX staked by (or for) `account`.
function balanceOf(address account) external view returns (uint256);

/// @dev Get the number of shares held by `account` which correspond to the proportion of its stake inside the vault.
/// @param account The address to get the shares for.
/// @return The number of shares held by `account`.
function sharesOf(address account) external view returns (uint256);
function pendingBalanceOf(address account) external view returns (uint256);
function claimableBalanceOf(address account) external view returns (uint256);

/// @notice Stake a certain amount of LYX in the Stakingverse's vault for `beneficiary`.
/// @dev To stake LYX in the vault for `beneficiary`, send the amount of LYX native tokens while calling this function.
/// @param beneficiary The address to stake LYX for in the vault.
function deposit(address beneficiary) external payable;

/// @notice Withdraw a certain `amount` of LYX staked by `msg.sender` in the Stakingverse's vault and transfer this amount to `beneficiary`.
/// @dev The `amount` to withdraw will reduce the staked balance (and therefore reduce its shares) of the address that called this function (caller / `msg.sender`).
/// @param amount The amount of staked LYX to withdraw.
/// @param beneficiary The address to send the withdrawn amount to.
function withdraw(uint256 amount, address beneficiary) external;
function claim(uint256 amount, address beneficiary) external;
function claimFees(uint256 amount, address beneficiary) external;

/// @notice Transfer `amount` of staked LYX from the caller to the `to` address with optional `data`.
///
/// @param to The address to transfer the staked LYX to.
/// @param amount The amount of staked LYX to transfer.
/// @param data Optional data.
function transferStake(address to, uint256 amount, bytes calldata data) external;
}
10 changes: 10 additions & 0 deletions src/IVaultStakeRecipient.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity =0.8.22;

interface IVaultStakeRecipient {
/// @notice Amount of stake have been transfered to the recipient.
/// @param from The address of the sender.
/// @param amount The amount of stake.
/// @param data Additional data.
function onVaultStakeReceived(address from, uint256 amount, bytes calldata data) external;
}
10 changes: 5 additions & 5 deletions src/Errors.sol → src/SLYXErrors.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@ pragma solidity ^0.8.22;
/// - the sLYX token contract
/// - the vault proxy contract
/// - the vault logic implementation contract
///
/// @param recipient One the three recipient address mentioned above.
error InvalidRecipientForSLYXTokensTransfer(address recipient);

/// @dev Reverts when an address other than the vault is trying to mint sLYX
error OnlyVaultAllowedToMint(address caller);
/// @param caller The address trying to mint sLYX tokens.
error OnlyVaultAllowedToMintSLYX(address caller);

/// @dev Revert when providing address(0) for the Vault on initialization
/// @dev Revert when providing address(0) for the Vault when initializing the SLYX token contract.
error InvalidVaultAddress(address stakingVault_);

/// @dev Revert when providing address(0) for the sLYX Token on initialization
error InvalidSLYXTokenAddress(address stakingVault_);
70 changes: 44 additions & 26 deletions src/SLYXToken.sol
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,20 @@ import {PausableUpgradeable} from "@openzeppelin/contracts-upgradeable/security/
import {_LSP4_TOKEN_TYPE_TOKEN} from "@lukso/lsp4-contracts/contracts/LSP4Constants.sol";

// Errors
import {InvalidRecipientForSLYXTokensTransfer, OnlyVaultAllowedToMint, InvalidVaultAddress} from "./Errors.sol";

// Token staking is a process that involves holding assets in a contract to support protocol operations such as market making.
// In exchange, the asset holders are rewarded with tokens which could be of the same type that they deposited, or not.
//
// The Simple Rewards contract allows users to stake native tokens, and are rewarded with a rewardsToken, which they must claim.
// They can withdraw their stake at any time, but rewards stop accruing for them.
// It is a permissionless contract that will distribute rewards during an interval defined on deployment. That is all that it is
/// @dev For depositing native tokens
import {
InvalidRecipientForSLYXTokensTransfer, OnlyVaultAllowedToMintSLYX, InvalidVaultAddress
} from "./SLYXErrors.sol";

/// @title Stakingverse Staked LYX (sLYX) Token contract.
///
/// @notice sLYX tokens represent liquid stake in the Stakingverse vault linked to this contract.
/// - New sLYX tokens are minted by transferring LYX staked in the linked vault to this contract.
/// - sLYX tokens can be burnt to convert them back to staked LYX.
///
/// @dev This contract includes also admin functionalities such as:
/// - pausing minting and burning (as emergency response while a remediation is pending).
/// - upgrading the contract (for security patches or future enhancements).
/// These are implemented using OpenZeppelin's upgradeable libraries
contract SLYXToken is
IVaultStakeRecipient,
ISLYX,
Expand All @@ -35,6 +40,8 @@ contract SLYXToken is
{
using Math for uint256;

/// @dev Address of the Vault contract linked to this SLYX Token contract.
/// sLYX tokens represent liquid stake in this linked vault.
IVault public stakingVault;

constructor() {
Expand Down Expand Up @@ -68,12 +75,20 @@ contract SLYXToken is
}

/// @notice Mint new sLYX tokens by transferring LYX staked in the Vault to this SLYXToken contract.
/// @dev This hook is called when calling the `transferStake(address,uint256,bytes)` function on the linked staking vault. Only the linked vault can call this function.
///
/// @dev This hook is called when:
/// - calling the `transferStake(address,uint256,bytes)` function,
/// - or calling the `deposit(address)` function,
/// on the linked staking vault, passing the SLYXToken contract address as parameter for `address`. Only the linked vault can call this function.
/// New sLYX minted can be monitored by listening for the `Transfer` event on the SLYXToken contract and filtering with `address(0)` as `from`.
/// sLYX tokens can only be minted when the contract is not paused.
///
/// @param from The address to mint sLYX for.
/// @param amount The amoount of staked LYX to be converted into sLYX (at the LYX / sLYX exchange rate).
/// @param data Any optional data to send when notifying the `from` address via its `universalReceiver(...)` function that some sLYX tokens were minted for its address.
function onVaultStakeReceived(address from, uint256 amount, bytes calldata data) external whenNotPaused {
if (msg.sender != address(stakingVault)) {
revert OnlyVaultAllowedToMint(msg.sender);
revert OnlyVaultAllowedToMintSLYX(msg.sender);
}

uint256 shares = Math.mulDiv(amount, stakingVault.totalShares(), stakingVault.totalAssets());
Expand All @@ -82,15 +97,19 @@ contract SLYXToken is
_mint({to: from, amount: shares, force: true, data: data});
}

/// @dev Burning function that allows to convert sLYX back to LYX only when the contract is not paused.
/// @notice Convert `amount` of sLYX tokens back to staked LYX (including any accumulated rewards).
///
/// @dev Burning function to convert sLYX back to LYX at the sLYX / LYX conversion rate.
/// sLYX tokens can only be burnt when the contract is not paused.
///
/// @param from The address to burn sLYX from its balance.
/// @param amount The amount of sLYX to convert to staked LYX.
/// @param data Any optional data to send when notifying the `from` address via its `universalReceiver(...)` function that some sLYX tokens were burnt from its balance and converted back to staked LYX.
function burn(address from, uint256 amount, bytes memory data) public virtual override whenNotPaused {
super.burn(from, amount, data);
}

/// @dev Calculate the amount of LYX backing an amount of sLYX.
///
/// Formula:
/// (sLYX amount * total stake held by sLYX contract in Vault) / total sLYX minted
/// @inheritdoc ISLYX
function getNativeTokenValue(uint256 sLyxAmount) public view returns (uint256) {
// Get the total number of sLYX tokens minted.
uint256 totalSLYXMinted = totalSupply();
Expand All @@ -101,19 +120,15 @@ contract SLYXToken is
return sLyxAmount.mulDiv(sLyxTokenContractStake, totalSLYXMinted);
}

/// @dev Calculate the amount of sLYX backed by an amount of LYX.
///
/// Formula:
/// (LYX amount * total sLYX minted) / total stake held by sLYX contract in Vault
function getSLYXTokenValue(uint256 lyxAmount) public view returns (uint256) {
/// @inheritdoc ISLYX
function getSLYXTokenValue(uint256 stakedLyxAmount) public view returns (uint256) {
uint256 totalSLYXMinted = totalSupply();
uint256 sLYXTokenContractStake = stakingVault.balanceOf(address(this));
uint256 totalSLYXTokenContractStake = stakingVault.balanceOf(address(this));

return lyxAmount.mulDiv(totalSLYXMinted, sLYXTokenContractStake);
return stakedLyxAmount.mulDiv(totalSLYXMinted, totalSLYXTokenContractStake);
}

/// @dev Get the current LYX / sLYX exchange rate
/// @return The amount of LYX backing 1 sLYX
/// @inheritdoc ISLYX
function getExchangeRate() external view returns (uint256) {
return getNativeTokenValue(1 ether);
}
Expand All @@ -122,7 +137,10 @@ contract SLYXToken is
return interfaceId == type(IVaultStakeRecipient).interfaceId || super.supportsInterface(interfaceId);
}

/// @dev Cannot transfer sLYX to the Vault itself or the SLYX Token contract itself otherwise they would get stuck.
/// @dev Prevent transferring sLYX tokens to:
/// - the linked Vault
/// - the SLYX Token contract itself
/// otherwise the tokens would get stuck.
function _beforeTokenTransfer(address, /* from */ address to, uint256, /* amount */ bytes memory /* data */ )
internal
virtual
Expand Down
Loading

0 comments on commit 0757993

Please sign in to comment.