diff --git a/.env.example b/.env.example index 151a611..3659add 100644 --- a/.env.example +++ b/.env.example @@ -54,4 +54,10 @@ SLYX_TOKEN_CONTRACT_OWNER_ADDRESS="0x..." NEW_SLYX_PROXY_ADMIN_ADDRESS="0x..." # Parameter used when upgrading the SLYX Token proxy to a new implementation -NEW_SLYX_TOKEN_IMPLEMENTATION_ADDRESS="0x..." \ No newline at end of file +NEW_SLYX_TOKEN_IMPLEMENTATION_ADDRESS="0x..." + +# ======================= +# SLYX Metadata parameters +# ======================= + +SLYX_TOKEN_METADATA_VALUE="0x..." \ No newline at end of file diff --git a/.solhintignore b/.solhintignore index 7da089a..536c1f5 100644 --- a/.solhintignore +++ b/.solhintignore @@ -1 +1,2 @@ -src/IDepositContract.sol \ No newline at end of file +src/IDepositContract.sol +src/Vault.sol \ No newline at end of file diff --git a/README.md b/README.md index aab4b87..ca927a5 100755 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ Repository for the Stakingverse contracts. This repository includes the followin | Staking Vault `Vault.sol` Implementation
(commit [`33d1619` on Universal.Page repository](https://github.com/Universal-Page/contracts/tree/33d1619a19162444c870b8a5a4bf42eb4532818c)) | [`0x2Cb02ef26aDDAB15686ed634d70699ab64F195f4`](https://explorer.lukso.network/address/0x2Cb02ef26aDDAB15686ed634d70699ab64F195f4?tab=contract) | | Staking Vault `StakingverseVault.sol` Implementation (upgraded) | [`0x1711b2e1b64F38ca33E51b717CFd27ACD1bd2E2D`](https://explorer.lukso.network/address/0x1711b2e1b64F38ca33E51b717CFd27ACD1bd2E2D?tab=contract) | | | SLYX Token Proxy | [`0x8a3982f0a7d154d11a5f43eec7f50e52ebbc8f7d`](https://explorer.lukso.network/address/0x8a3982f0a7d154d11a5f43eec7f50e52ebbc8f7d?tab=contract) | -| SLYX Token Implementation | _To be deployed_ | +| SLYX Token Implementation | [`0x08b28405A11348745A3187De2A29C730C53EB29B`](https://explorer.lukso.network/address/0x08b28405A11348745A3187De2A29C730C53EB29B?tab=contract) | - [Stakingverse Contracts](#stakingverse-contracts) - [Installation](#installation) diff --git a/audits/README.md b/audits/README.md index 39ece90..f779c9b 100644 --- a/audits/README.md +++ b/audits/README.md @@ -18,4 +18,8 @@ Previous audits for this Vault contract can be found in the [UniversalPage contr
-- [MiloTruck Audit](Stakingverse_Audit_MiloTruck.pdf) - Audit of both StakingverseVault.sol and Liquid Staking contracts \ No newline at end of file +- [MiloTruck Audit](Stakingverse_Audit_MiloTruck.pdf) - Audit of both StakingverseVault.sol and Liquid Staking contracts + +
+ +- [Nethermind AI Agent Audit](nethermind-ai-agent-audit-report-stakingverse-pool-contracts.pdf) - Audit of both StakingverseVault.sol and Liquid Staking contracts by the BETA version of the [Nethermind](https://www.nethermind.io) Audit Agent. Check the overview of the findings [here](nethermind-findings.md). \ No newline at end of file diff --git a/audits/nethermind-ai-agent-audit-report-stakingverse-pool-contracts.pdf b/audits/nethermind-ai-agent-audit-report-stakingverse-pool-contracts.pdf new file mode 100644 index 0000000..f6be73f Binary files /dev/null and b/audits/nethermind-ai-agent-audit-report-stakingverse-pool-contracts.pdf differ diff --git a/audits/nethermind-findings.md b/audits/nethermind-findings.md new file mode 100644 index 0000000..30b067a --- /dev/null +++ b/audits/nethermind-findings.md @@ -0,0 +1,32 @@ + +| Severity | Number | +| ---------- | ------ | +| **High** | 1 | +| **Medium** | 5 | +| **Low** | 12 | +| **Info** | 3 | +| **TOTAL** | 21 | + +| # | Severity | Contract | Issue Description | Risk Level | Impact | Resolution | +| ---- | -------- | ------------------------------------- | ------------------------------------------------------------------------ | ---------- | ------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| H-1 | High | StakingverseVault.sol | First depositor exploit due to flawed share calculation | High | Share price inflation | ✅ Fixed. Same finding than in MiloTruck audit. Commented in the source code as warning. This contract is used for an upgrade so it will not apply. | +| M-1 | Medium | StakingverseVault.sol | Single oracle has unilateral power to register validators and rebalance | Medium | Potential manipulation of vault balance | ☑️ Acknowledged. Design decision. There are no single oracle that have unilateral power. Oracles are controlled by the admin. If an oracle address is compromised, the operator can register another oracle address and remove the compromised one. | +| M-2 | Medium | StakingverseVault.sol | Insufficient validation in validator registration | Medium | Incorrect validator registrations | ⚪️ Not Applicable / False Positive. Deposit contract on mainnet perform these checks already. https://explorer.lukso.network/address/0xCAfe00000000000000000000000000000000CAfe?tab=contract | +| M-3 | Medium | StakingverseVault.sol | Pending withdrawal rebalancing vulnerability | Medium | Manipulated withdrawal state | ☑️ Acknowledged. The oracle of Stakingverse can be trusted. | +| M-4 | Medium | SLYXToken.sol | Reentrancy risk in `_afterTokenTransfer` during burn | Medium | Possible reentrancy attack | ✅ Mitigated. The `transferStake(...)` function in the `StakingverseVault` contract contains a `nonReentrant` modifier, which mitigates this issue. | +| M-5 | Medium | StakingverseVault.sol | Complex rebalance logic vulnerability | Medium | Misallocation of rewards or fees | ⚪️ Not Applicable / False Positive? Not clear, the issue description seems very broad and not clear to me | +| L-1 | Low | SLYXToken.sol | Use of TransparentUpgradeableProxy assumptions in `_beforeTokenTransfer` | Low | Possible failure on upgrade | ✅ Fixed. The `implementation()` function can only be called by the proxy admin, which would lead to this internal call to fail. This check was removed. | +| L-2 | Low | StakingverseVault.sol | Operators can adjust fee and deposit limit without timelock | Low | Centralized control risk | ☑️ Acknowledged. Operator is controlled mainly by Stakingverse. Would not be the case that drastic fee changes occur out of the sudden. If fee changes, these are communicated to the communities. | +| L-3 | Low | StakingverseVault.sol | Math precision loss in share calculations | Low | Inaccurate balance calculations | ☑️ Acknowledged. We know already and cannot really change much in the logic because it is already deployed and upgrading could incur risks. | +| L-4 | Low | StakingverseVault.sol | Missing zero transfer check in `transferStake()` | Low | Unnecessary gas costs | ☑️ Acknowledged. | +| L-5 | Low | StakingverseVault.sol | Possible rounding issues in fee calculation | Low | Loss of fees due to rounding down | ☑️ Acknowledged. | +| L-6 | Low | SLYXToken.sol | Unprotected initialization parameters | Low | Risk of invalid owner initialization | ⚪️ Not Applicable / False Positive. Check for `address(0)` is performed in the inheritance of the contract. | +| L-7 | Low | SLYXToken.sol | Incomplete error handling in LSP1 Universal Receiver | Low | Potential failures not caught | ⚪️ Not Applicable / False Positive. The description of the issue is not clear. | +| L-8 | Low | StakingverseVault.sol | Missing fee recipient validation in `setFee` | Low | Locked fees without recipient | ⚪️ Not Applicable / False Positive. | +| L-9 | Low | StakingverseVault.sol | No validation for validator existence in `registerValidator` | Low | Easier for malicious oracle to register fake validators | ☑️ Acknowledged. The oracle of Stakingverse can be trusted. | +| L-10 | Low | StakingverseVault.sol | Lack of address validation in `transferStake` | Low | Possible silent failures | ☑️ Acknowledged. Intended behaviour. | +| L-11 | Low | StakingverseVault.sol | Zero value asset loss risk | Low | Incorrect share price calculations | ☑️ Acknowledged. Cannot be applicable as contract is already live and this is an upgrade. | +| L-12 | Low | SLYXToken.sol | Division-by-zero risk in `onVaultStakeReceived` | Low | Possible denial of service | ⚪️ Not Applicable / False Positive. The Vault is already live in production. It is very unlikely that `totalAssets()` will return zero. | +| I-1 | Info | StakingverseVault.sol / SLYXToken.sol | Vault logic failure if proxy pattern changes | Info | Risk in future upgrades | ☑️ Acknowledged. Upgrade will be carefully reviewed including storage layout and the proxy pattern will not be changed (only storage layout will be used). | +| I-2 | Info | SLYXToken.sol | Missing events for critical state changes | Info | Harder to track contract state | ⚪️ Not Applicable / False Positive. Events are emitted on the parent contract in the underlying functions. | +| I-3 | Info | StakingverseVault.sol / SLYXToken.sol | Missing events for critical operations | Info | Difficult off-chain monitoring | ⚪️ Not Applicable / False Positive. Events are emitted on the parent contract in the underlying functions. | diff --git a/output.log b/output.log deleted file mode 100644 index fa2b4f3..0000000 --- a/output.log +++ /dev/null @@ -1,17 +0,0 @@ -Verifying proxy Vault at 0x9F49a95b0c3c9e2A6c77a16C177928294c0F6F04 -Implementation address: 0x2cb02ef26addab15686ed634d70699ab64f195f4 -Verifying TransparentUpgradeableProxy at 0x9F49a95b0c3c9e2A6c77a16C177928294c0F6F04 -Start verifying contract `0x9F49a95b0c3c9e2A6c77a16C177928294c0F6F04` deployed on kovan - -Contract [lib/openzeppelin-contracts/contracts/proxy/transparent/TransparentUpgradeableProxy.sol:TransparentUpgradeableProxy] "0x9F49a95b0c3c9e2A6c77a16C177928294c0F6F04" is already verified. Skipping verification. -Verifying TransparentUpgradeableProxy at 0x2cb02ef26addab15686ed634d70699ab64f195f4 -Start verifying contract `0x2Cb02ef26aDDAB15686ed634d70699ab64F195f4` deployed on kovan - -Submitting verification for [lib/openzeppelin-contracts/contracts/proxy/transparent/TransparentUpgradeableProxy.sol:TransparentUpgradeableProxy] 0x2Cb02ef26aDDAB15686ed634d70699ab64F195f4. -Submitted contract for verification: - Response: `OK` - GUID: `2cb02ef26addab15686ed634d70699ab64f195f466216bea` - URL: https://kovan.etherscan.io/address/0x2cb02ef26addab15686ed634d70699ab64f195f4 -Contract verification status: -Response: `OK` -Details: `Unknown UID` diff --git a/package-lock.json b/package-lock.json index 04b1b69..b625823 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6,7 +6,8 @@ "": { "name": "Stakingverse pool contracts", "dependencies": { - "@lukso/lsp7-contracts": "~0.16.3" + "@lukso/lsp17contractextension-contracts": "~0.16.3", + "@lukso/lsp7-contracts": "~0.16.4" }, "devDependencies": { "@lukso/lsp1delegate-contracts": "^0.15.0", @@ -875,6 +876,16 @@ "@openzeppelin/contracts": "^4.9.3" } }, + "node_modules/@lukso/lsp0-contracts/node_modules/@lukso/lsp17contractextension-contracts": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/@lukso/lsp17contractextension-contracts/-/lsp17contractextension-contracts-0.15.0.tgz", + "integrity": "sha512-fwLrXi1jyiw6DlP6mt+NTweSxyPI3KaKk5UN9OwuT5KbaC7Upm50TFuA/IX+V4gY8/iVdr6uhy7nLg1+LQpSAw==", + "dev": true, + "dependencies": { + "@erc725/smart-contracts": "^7.0.0", + "@openzeppelin/contracts": "^4.9.3" + } + }, "node_modules/@lukso/lsp1-contracts": { "version": "0.15.0", "resolved": "https://registry.npmjs.org/@lukso/lsp1-contracts/-/lsp1-contracts-0.15.0.tgz", @@ -916,13 +927,11 @@ } }, "node_modules/@lukso/lsp17contractextension-contracts": { - "version": "0.15.0", - "resolved": "https://registry.npmjs.org/@lukso/lsp17contractextension-contracts/-/lsp17contractextension-contracts-0.15.0.tgz", - "integrity": "sha512-fwLrXi1jyiw6DlP6mt+NTweSxyPI3KaKk5UN9OwuT5KbaC7Upm50TFuA/IX+V4gY8/iVdr6uhy7nLg1+LQpSAw==", - "dev": true, + "version": "0.16.3", + "resolved": "https://registry.npmjs.org/@lukso/lsp17contractextension-contracts/-/lsp17contractextension-contracts-0.16.3.tgz", + "integrity": "sha512-R/ePe/rXIxpsAunI7EcgpObLfVrmm/J8JnvTfgdxIzSLwuSFHgVDiG9l70lKrE3QCpBp5Cpt+7bX4EqX8c7EXw==", "dependencies": { - "@erc725/smart-contracts": "^7.0.0", - "@openzeppelin/contracts": "^4.9.3" + "@openzeppelin/contracts": "^4.9.6" } }, "node_modules/@lukso/lsp1delegate-contracts": { @@ -941,6 +950,16 @@ "@openzeppelin/contracts": "^4.9.3" } }, + "node_modules/@lukso/lsp1delegate-contracts/node_modules/@lukso/lsp17contractextension-contracts": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/@lukso/lsp17contractextension-contracts/-/lsp17contractextension-contracts-0.15.0.tgz", + "integrity": "sha512-fwLrXi1jyiw6DlP6mt+NTweSxyPI3KaKk5UN9OwuT5KbaC7Upm50TFuA/IX+V4gY8/iVdr6uhy7nLg1+LQpSAw==", + "dev": true, + "dependencies": { + "@erc725/smart-contracts": "^7.0.0", + "@openzeppelin/contracts": "^4.9.3" + } + }, "node_modules/@lukso/lsp1delegate-contracts/node_modules/@lukso/lsp4-contracts": { "version": "0.15.0", "resolved": "https://registry.npmjs.org/@lukso/lsp4-contracts/-/lsp4-contracts-0.15.0.tgz", @@ -1033,10 +1052,21 @@ "@openzeppelin/contracts": "^4.9.3" } }, + "node_modules/@lukso/lsp6-contracts/node_modules/@lukso/lsp17contractextension-contracts": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/@lukso/lsp17contractextension-contracts/-/lsp17contractextension-contracts-0.15.0.tgz", + "integrity": "sha512-fwLrXi1jyiw6DlP6mt+NTweSxyPI3KaKk5UN9OwuT5KbaC7Upm50TFuA/IX+V4gY8/iVdr6uhy7nLg1+LQpSAw==", + "dev": true, + "dependencies": { + "@erc725/smart-contracts": "^7.0.0", + "@openzeppelin/contracts": "^4.9.3" + } + }, "node_modules/@lukso/lsp7-contracts": { - "version": "0.16.3", - "resolved": "https://registry.npmjs.org/@lukso/lsp7-contracts/-/lsp7-contracts-0.16.3.tgz", - "integrity": "sha512-Si4mAhRbRUPQ9umzrXhGxgTFWLhIrCChUNCLDyk4CK4nNm8wvDmFj+Expal+aTvl+ylqMcaPKgcXshle3MQRow==", + "version": "0.16.4", + "resolved": "https://registry.npmjs.org/@lukso/lsp7-contracts/-/lsp7-contracts-0.16.4.tgz", + "integrity": "sha512-dxaU2mRL55sqsuvbk7G3DLHJz/ouQsDRGh/252NLicQAE4/5EAzVYoO3zjb/iNxff23a+m8xAtjw3bknmOHHfQ==", + "license": "Apache-2.0", "dependencies": { "@lukso/lsp1-contracts": "~0.15.0", "@lukso/lsp17contractextension-contracts": "~0.16.0", @@ -1045,14 +1075,6 @@ "@openzeppelin/contracts": "^4.9.6" } }, - "node_modules/@lukso/lsp7-contracts/node_modules/@lukso/lsp17contractextension-contracts": { - "version": "0.16.2", - "resolved": "https://registry.npmjs.org/@lukso/lsp17contractextension-contracts/-/lsp17contractextension-contracts-0.16.2.tgz", - "integrity": "sha512-pYUvWQddNO9VJRL0GV0NYQg0Wzt5jkWwTb2OSgGXg//RbDMrAcumX4ROniEfxGFWqISaoIBWk2WXrN2eLRQMJA==", - "dependencies": { - "@openzeppelin/contracts": "^4.9.6" - } - }, "node_modules/@lukso/lsp8-contracts": { "version": "0.15.0", "resolved": "https://registry.npmjs.org/@lukso/lsp8-contracts/-/lsp8-contracts-0.15.0.tgz", @@ -1067,6 +1089,16 @@ "@openzeppelin/contracts": "^4.9.3" } }, + "node_modules/@lukso/lsp8-contracts/node_modules/@lukso/lsp17contractextension-contracts": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/@lukso/lsp17contractextension-contracts/-/lsp17contractextension-contracts-0.15.0.tgz", + "integrity": "sha512-fwLrXi1jyiw6DlP6mt+NTweSxyPI3KaKk5UN9OwuT5KbaC7Upm50TFuA/IX+V4gY8/iVdr6uhy7nLg1+LQpSAw==", + "dev": true, + "dependencies": { + "@erc725/smart-contracts": "^7.0.0", + "@openzeppelin/contracts": "^4.9.3" + } + }, "node_modules/@lukso/lsp8-contracts/node_modules/@lukso/lsp4-contracts": { "version": "0.15.0", "resolved": "https://registry.npmjs.org/@lukso/lsp4-contracts/-/lsp4-contracts-0.15.0.tgz", diff --git a/package.json b/package.json index 29c644b..45d4b7c 100644 --- a/package.json +++ b/package.json @@ -34,7 +34,8 @@ "test:upgrade:fork": "FOUNDRY_OUT=build forge clean && forge test --fork-url https://rpc.mainnet.lukso.network --match-contract ForkMainnetUpgradeTest" }, "dependencies": { - "@lukso/lsp7-contracts": "~0.16.3" + "@lukso/lsp7-contracts": "~0.16.4", + "@lukso/lsp17contractextension-contracts": "~0.16.3" }, "devDependencies": { "@lukso/lsp1delegate-contracts": "^0.15.0", diff --git a/script/README.md b/script/README.md index 4496520..4f0716b 100644 --- a/script/README.md +++ b/script/README.md @@ -126,7 +126,7 @@ NEW_VAULT_IMPLEMENTATION_ADDRESS="0x..." source .env ``` -3. Run these scripts to deploy the Vault proxy +3. Run these scripts to upgrade the Vault proxy ```bash # [Simulation] Upgrade the Vault to a new implementation (dry run) @@ -145,7 +145,7 @@ forge script --chain 4201 script/StakingverseVaultScriptTestnet.s.sol:UpgradeVau forge script --chain 4201 script/SLYXTokenScriptTestnet.s.sol:DeploySLYXTokenImplementation --rpc-url $LUKSO_TESTNET_RPC_URL -vvvv # [Transaction] Deploy the SLYX Token implementation + verify it -forge scrip --chain 4201 script/SLYXTokenScriptTestnet.s.sol:DeploySLYXTokenImplementation --rpc-url $LUKSO_TESTNET_RPC_URL --broadcast --verify --verifier blockscout --verifier-url $BLOCKSCOUT_TESTNET_API_URL -vvvv +forge script --chain 4201 script/SLYXTokenScriptTestnet.s.sol:DeploySLYXTokenImplementation --rpc-url $LUKSO_TESTNET_RPC_URL --broadcast --verify --verifier blockscout --verifier-url $BLOCKSCOUT_TESTNET_API_URL -vvvv ``` ## Deploy the SLYX Token proxy contract @@ -189,7 +189,7 @@ NEW_SLYX_TOKEN_IMPLEMENTATION_ADDRESS="0x..." source .env ``` -3. Run these scripts to deploy the Vault proxy +3. Run these scripts to upgrade the sLYX token proxy ```bash # [Simulation] Upgrade the SLYX Token proxy to a new implementation (dry run) diff --git a/script/SLYXTokenScriptMainnet.s.sol b/script/SLYXTokenScriptMainnet.s.sol index ab0f7d0..01a04ed 100644 --- a/script/SLYXTokenScriptMainnet.s.sol +++ b/script/SLYXTokenScriptMainnet.s.sol @@ -4,6 +4,7 @@ pragma solidity ^0.8.13; import {Script} from "forge-std/Script.sol"; import {console} from "forge-std/console.sol"; import {Strings} from "@openzeppelin/contracts/utils/Strings.sol"; +import {LSP2Utils} from "@lukso/lsp2-contracts/contracts/LSP2Utils.sol"; import { TransparentUpgradeableProxy, @@ -12,7 +13,14 @@ import { import {SLYXToken} from "../src/SLYXToken.sol"; import {IVault} from "../src/IVault.sol"; +import {IERC725Y} from "@erc725/smart-contracts/contracts/interfaces/IERC725Y.sol"; +import {_INTERFACEID_LSP0} from "@lukso/lsp0-contracts/contracts/LSP0Constants.sol"; +import { + _LSP4_METADATA_KEY, + _LSP4_CREATORS_ARRAY_KEY, + _LSP4_CREATORS_MAP_KEY_PREFIX +} from "@lukso/lsp4-contracts/contracts/LSP4Constants.sol"; import {PROXY_ADMIN_MAINNET, SLYX_TOKEN_PROXY_MAINNET} from "./MainnetConstants.sol"; contract DeploySLYXTokenImplementation is Script { @@ -83,3 +91,36 @@ contract ChangeAdmin is Script { console.log(string.concat("SLYXToken proxy admin changed to ", Strings.toHexString(newProxyAdmin))); } } + +contract SetMetadata is Script { + function run() external { + bytes memory encodedMetadataValue = vm.envBytes("SLYX_TOKEN_METADATA_VALUE"); + + vm.startBroadcast(); + IERC725Y(SLYX_TOKEN_PROXY_MAINNET).setData(_LSP4_METADATA_KEY, encodedMetadataValue); + vm.stopBroadcast(); + } +} + +contract SetCreator is Script { + function run() external { + // Stakingverse Universal Profile address + address creator = 0x900Be67854A47282211844BbdF5Cc0f332620513; + + bytes32[] memory dataKeys = new bytes32[](3); + bytes[] memory dataValues = new bytes[](3); + + dataKeys[0] = _LSP4_CREATORS_ARRAY_KEY; + dataValues[0] = abi.encodePacked(uint128(1)); + + dataKeys[1] = LSP2Utils.generateArrayElementKeyAtIndex(_LSP4_CREATORS_ARRAY_KEY, 0); + dataValues[1] = abi.encodePacked(creator); + + dataKeys[2] = LSP2Utils.generateMappingKey(_LSP4_CREATORS_MAP_KEY_PREFIX, bytes20(creator)); + dataValues[2] = abi.encodePacked(_INTERFACEID_LSP0, uint128(0)); + + vm.startBroadcast(); + IERC725Y(SLYX_TOKEN_PROXY_MAINNET).setDataBatch(dataKeys, dataValues); + vm.stopBroadcast(); + } +} diff --git a/src/ISLYX.sol b/src/ISLYX.sol index 2a67ef6..2d51fc3 100644 --- a/src/ISLYX.sol +++ b/src/ISLYX.sol @@ -2,26 +2,25 @@ pragma solidity ^0.8.0; interface ISLYX { - /// @notice Calculate how many staked LYX (including accumulated rewards) in the vault can be obtained in exchange for `sLYXAmount`. + /// @notice Calculate the amount of sLYX tokens to the equivalent staked LYX (including accumulated rewards) in the linked vault. /// - /// @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. + /// @dev This function determines the amount of native tokens (staked LYX) that are backing a given amount of sLYX tokens. + /// Formula: (sLYX amount * total LYX stake held by the `SLYXToken` contract address in the 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). + /// @param sLyxAmount The amount (in wei) of sLYX tokens to convert to staked LYX. + /// @return The equivalent amount of staked LYX (in wei) backing the specified sLYX amount. function getNativeTokenValue(uint256 sLyxAmount) external view returns (uint256); - /// @notice Calculate how many sLYX tokens can be obtained when converting `stakedLyxAmount`. + /// @notice Calculate how many sLYX tokens can be obtained in exchange for `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. + /// Formula: (LYX amount * total sLYX minted) / total stake held by the `SLYXToken` contract address in the 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). + /// @param stakedLyxAmount The amount (in wei) of staked LYX to convert into sLYX tokens. + /// @return The equivalent amount of sLYX (in wei) backing a specific amount of staked LYX. 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. + /// @dev Get the current sLYX / LYX exchange rate to calculate how many sLYX back a certain amount of LYX. /// @return The amount of LYX backing 1 sLYX (in wei). function getExchangeRate() external view returns (uint256); } diff --git a/src/SLYXErrors.sol b/src/SLYXErrors.sol index 0b3b463..501b16f 100644 --- a/src/SLYXErrors.sol +++ b/src/SLYXErrors.sol @@ -1,17 +1,13 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity ^0.8.22; -/// @dev Reverts when trying to transfer sLYX to one of these invalid recipient address: -/// - 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 trying to transfer sLYX to either the sLYX token contract or the vault contract. +/// @param invalidRecipient One the recipient addresses mentioned in the previous comment. +error InvalidRecipientForSLYXTokensTransfer(address invalidRecipient); -/// @dev Reverts when an address other than the vault is trying to mint sLYX +/// @dev Reverts when an address other than the vault is trying to mint sLYX. /// @param caller The address trying to mint sLYX tokens. error OnlyVaultAllowedToMintSLYX(address caller); -/// @dev Revert when providing address(0) for the Vault when initializing the SLYX token contract. +/// @dev Reverts when providing `address(0)` for the Vault when initializing the `SLYXToken` contract. error InvalidVaultAddress(address stakingVault_); diff --git a/src/SLYXToken.sol b/src/SLYXToken.sol index ea52aaf..59f2f82 100644 --- a/src/SLYXToken.sol +++ b/src/SLYXToken.sol @@ -17,63 +17,63 @@ import { InvalidRecipientForSLYXTokensTransfer, OnlyVaultAllowedToMintSLYX, InvalidVaultAddress } from "./SLYXErrors.sol"; -// -// -// -// ++++++++++++ -// ;+++++++++++++++++++ -// +++++++$$$$$$$$$$$$+++++++ -// +++++++$$$$$$$$$$$$$$$$$$+++++++ -// +++++++$$$$$$$$$$$$$$$$$$$$$$$$+++++++ -// +++++++$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$+++++++ -// +++++++$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$+++++++ -// ++++++x$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$x++++++ -// +++++++$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$+++++++ -// +++++++$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$+++++++ -// ++++++$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$++++++ -// +++++$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$+++++ -// ;++++$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$+++++ -// +++$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$+++ -// ++++$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$++++ -// +++$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$+++++++++$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$+++ -// :+++$$$$$$$$$$$$$$$$$$$$$$$$$$$$$&++++++++++++++++++$$$$$$$$$$$$$$$$$$$$x+++++$$$$+++: -// :+++$$$$$$$$$$$$$$$$$$$$$$$$$$$+++++++++++++++++++++++&$$$$$$$$$&++++++++++++++$$$+++: -// :+++$$$$$$$$$$$$$$$$$$$$$$$$$+++++++++++++++++++++++++++&$$$$$$$$$$$$$+++++++X$$$$+++: -// :+++$$$$$$$$$$$$$$$$$$$$$$$X++++++++++++++++++++++++++++++$$$$$$$$$$$$++++++$$$$$$+++: -// :+++$$$$$$$$$$$$$$$$$$$$$$+++++++++++++++++++++++++++++++++$$$$$$$$$+++++X$$$$$$$$+++: -// :+++$$$$$$$$$$$$$$$$$$$$$+++++++++++++++++++++++++++++++++++$$$$$$+++++$$$$$$$$$$$+++: -// :+++$$$$$$$$$$$$$$$$$$$$$++++++++++++++++++++++++++++++++++++$$X++++$$$$$$$$$$$$$$+++: -// :+++$$$$$$$$$$$$$$$$$$$$+++++++++++++++++++++++++++++++++++++++++$$$$$$$$$$$$$$$$$+++: -// :+++$$$$$$$$$$$$$$$$$$$$++++++++++++++++++++++++++++++++++++++$$$$$$$$$$$$$$$$$$$$+++: -// :+++$$$$$$$$$$$$$$$$$$$$+++++++++++++++++++++++++++++++++++$$$$$$$$$$$$$$$$$$$$$$$+++: -// :+++$$$$$$$$$$$$$$$$$$$$+++++++++++++++++++++++++++++++$$$$$$$$$$$$$$$$$$$$$$$$$$$+++: -// :+++$$$$$$$$$$$$$$$$$$$$++++++++++++++++++++++++++++$$$$$++++$$$$$$$$$$$$$$$$$$$$$+++: -// :+++$$$$$$$$$$$$$$$+$$$$$+++++++++++++++++++++++$$$$$$+++++++$$$$$$$$$$$$$$$$$$$$$+++: -// :+++$$$$$$$$$$$$$+$$$$$$$+++++++++++++++++++$$$$$$++++++++++$$$$$$$$$$$$$$$$$$$$$$+++: -// :+++$$$$$$$$$$+++$$$$$$$$$++++++++++++++$$$$$$+++++++++++++$$$$$$$$$$$$$$$$$$$$$$$+++: -// :+++$$$$$$$++++&$$$$$$$$$$$++++++++&$$$$$$++++++++++++++++$$$$$$$$$$$$$$$$$$$$$$$$+++: -// :+++$$$$$++++++&$$$$$$++++++++$$$$$$$+++++++++++++++++++$$$$$$$$$$$$$$$$$$$$$$$$$$+++: -// :+++$$$$+++++++++++++++++$$$$$$$&++++++++++++++++++++++$$$$$$$$$$$$$$$$$$$$$$$$$$$+++: -// :+++$$$++++++++++++$$$$$$$$$$$$$$x++++++++++++++++++$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$+++: -// +++$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$++++++++++$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$+++ -// ++++$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$++++ -// +++$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$+++ -// +++++$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$+++++ -// +++++$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$+++++ -// ++++++$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$++++++ -// +++++++$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$+++++++ -// +++++++$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$+++++++ -// ++++++X$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$x++++++ -// +++++++$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$+++++++ -// +++++++$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$+++++++ -// +++++++$$$$$$$$$$$$$$$$$$$$$$$$+++++++ -// +++++++$$$$$$$$$$$$$$$$$$+++++++ -// +++++++$$$$$$$$$$$$+++++++ -// ++++++++++++++++++++ -// ++++++++++++ -// -// -// +// +// +// +// ++++++++++++ +// ;+++++++++++++++++++ +// +++++++$$$$$$$$$$$$+++++++ +// +++++++$$$$$$$$$$$$$$$$$$+++++++ +// +++++++$$$$$$$$$$$$$$$$$$$$$$$$+++++++ +// +++++++$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$+++++++ +// +++++++$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$+++++++ +// ++++++x$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$x++++++ +// +++++++$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$+++++++ +// +++++++$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$+++++++ +// ++++++$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$++++++ +// +++++$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$+++++ +// ;++++$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$+++++ +// +++$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$+++ +// ++++$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$++++ +// +++$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$+++++++++$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$+++ +// :+++$$$$$$$$$$$$$$$$$$$$$$$$$$$$$&++++++++++++++++++$$$$$$$$$$$$$$$$$$$$x+++++$$$$+++: +// :+++$$$$$$$$$$$$$$$$$$$$$$$$$$$+++++++++++++++++++++++&$$$$$$$$$&++++++++++++++$$$+++: +// :+++$$$$$$$$$$$$$$$$$$$$$$$$$+++++++++++++++++++++++++++&$$$$$$$$$$$$$+++++++X$$$$+++: +// :+++$$$$$$$$$$$$$$$$$$$$$$$X++++++++++++++++++++++++++++++$$$$$$$$$$$$++++++$$$$$$+++: +// :+++$$$$$$$$$$$$$$$$$$$$$$+++++++++++++++++++++++++++++++++$$$$$$$$$+++++X$$$$$$$$+++: +// :+++$$$$$$$$$$$$$$$$$$$$$+++++++++++++++++++++++++++++++++++$$$$$$+++++$$$$$$$$$$$+++: +// :+++$$$$$$$$$$$$$$$$$$$$$++++++++++++++++++++++++++++++++++++$$X++++$$$$$$$$$$$$$$+++: +// :+++$$$$$$$$$$$$$$$$$$$$+++++++++++++++++++++++++++++++++++++++++$$$$$$$$$$$$$$$$$+++: +// :+++$$$$$$$$$$$$$$$$$$$$++++++++++++++++++++++++++++++++++++++$$$$$$$$$$$$$$$$$$$$+++: +// :+++$$$$$$$$$$$$$$$$$$$$+++++++++++++++++++++++++++++++++++$$$$$$$$$$$$$$$$$$$$$$$+++: +// :+++$$$$$$$$$$$$$$$$$$$$+++++++++++++++++++++++++++++++$$$$$$$$$$$$$$$$$$$$$$$$$$$+++: +// :+++$$$$$$$$$$$$$$$$$$$$++++++++++++++++++++++++++++$$$$$++++$$$$$$$$$$$$$$$$$$$$$+++: +// :+++$$$$$$$$$$$$$$$+$$$$$+++++++++++++++++++++++$$$$$$+++++++$$$$$$$$$$$$$$$$$$$$$+++: +// :+++$$$$$$$$$$$$$+$$$$$$$+++++++++++++++++++$$$$$$++++++++++$$$$$$$$$$$$$$$$$$$$$$+++: +// :+++$$$$$$$$$$+++$$$$$$$$$++++++++++++++$$$$$$+++++++++++++$$$$$$$$$$$$$$$$$$$$$$$+++: +// :+++$$$$$$$++++&$$$$$$$$$$$++++++++&$$$$$$++++++++++++++++$$$$$$$$$$$$$$$$$$$$$$$$+++: +// :+++$$$$$++++++&$$$$$$++++++++$$$$$$$+++++++++++++++++++$$$$$$$$$$$$$$$$$$$$$$$$$$+++: +// :+++$$$$+++++++++++++++++$$$$$$$&++++++++++++++++++++++$$$$$$$$$$$$$$$$$$$$$$$$$$$+++: +// :+++$$$++++++++++++$$$$$$$$$$$$$$x++++++++++++++++++$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$+++: +// +++$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$++++++++++$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$+++ +// ++++$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$++++ +// +++$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$+++ +// +++++$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$+++++ +// +++++$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$+++++ +// ++++++$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$++++++ +// +++++++$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$+++++++ +// +++++++$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$+++++++ +// ++++++X$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$x++++++ +// +++++++$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$+++++++ +// +++++++$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$+++++++ +// +++++++$$$$$$$$$$$$$$$$$$$$$$$$+++++++ +// +++++++$$$$$$$$$$$$$$$$$$+++++++ +// +++++++$$$$$$$$$$$$+++++++ +// ++++++++++++++++++++ +// ++++++++++++ +// +// +// // .----------------------------------------------------------------------------------------------------. // | | // | ███████╗████████╗ █████╗ ██╗ ██╗██╗███╗ ██╗ ██████╗ ██╗ ██╗███████╗██████╗ ███████╗███████╗ | @@ -91,14 +91,14 @@ import { /// - 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). +/// @dev This contract also includes admin functionalities such as: +/// - pausing functionalities to mint and burn (as an emergency response while a remediation is pending). /// - upgrading the contract (for security patches or future enhancements). -/// These are implemented using OpenZeppelin's upgradeable libraries +/// These are implemented using OpenZeppelin's upgradeable contracts library. contract SLYXToken is IVaultStakeRecipient, ISLYX, LSP7BurnableInitAbstract, PausableUpgradeable { using Math for uint256; - /// @dev Address of the Vault contract linked to this SLYX Token contract. + /// @dev Address of the Vault contract linked to this `SLYXToken` contract. /// sLYX tokens represent liquid stake in this linked vault. IVault public stakingVault; @@ -132,23 +132,22 @@ contract SLYXToken is IVaultStakeRecipient, ISLYX, LSP7BurnableInitAbstract, Pau _unpause(); } - /// @notice Mint new sLYX tokens by transferring LYX staked in the Vault to this SLYXToken contract. + /// @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, - /// - 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. + /// - calling the `transferStake(address,uint256,bytes)` function + /// - or calling the `deposit(address)` function + /// on the linked vault, passing the `SLYXToken` contract address to the `address` parameter. Only the linked vault can call this function. + /// Newly minted sLYX tokens can be monitored by listening for the `Transfer` event on the `SLYXToken` contract and filtering with `address(0)` as `from`. + /// Note that 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. + /// @param from The address to mint sLYX tokens for. + /// @param amount The amount of staked LYX to be converted into sLYX tokens (at the LYX / sLYX exchange rate). + /// @param data Any optional data to send when notifying the `from` address via their `universalReceiver(...)` function that some sLYX tokens were minted for their address. /// - /// - /// Warning: If using this SLYXToken contract with a newly deployed StakingverseVault, perform an initial deposit with a - /// beneficiary address other than the SLYXToken contract address. This ensures correct share calculations and prevents - /// minting an extra 1e3 SLYX initially due to the vault not subtracting `_MINIMUM_REQUIRED_SHARES` on the first deposit. + /// Warning: If using this `SLYXToken` contract with a newly deployed StakingverseVault, perform an initial deposit with a + /// beneficiary address other than the `SLYXToken` contract address. This ensures correct share calculations and prevents + /// minting an extra 1e3 sLYX initially due to the vault not subtracting `_MINIMUM_REQUIRED_SHARES` on the first deposit. function onVaultStakeReceived(address from, uint256 amount, bytes calldata data) external whenNotPaused { if (msg.sender != address(stakingVault)) { revert OnlyVaultAllowedToMintSLYX(msg.sender); @@ -165,13 +164,13 @@ contract SLYXToken is IVaultStakeRecipient, ISLYX, LSP7BurnableInitAbstract, Pau /// @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 from The address to burn sLYX from their 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. + /// @param data Any optional data to send when notifying the `from` address via their `universalReceiver(...)` function that some sLYX tokens were burnt from their balance and converted back to staked LYX. /// - /// Warning: note that if the link vault is set to restricted mode (= only a whitelist of stakers) + /// Warning: Note that if the linked vault is set to restricted mode (= only a whitelist of stakers) /// and sLYX tokens are transferred to a non-whitelisted address, this address will not be able to - /// burn the tokens and redeemed them for staked LYX. + /// burn the tokens and redeem them for staked LYX. function burn(address from, uint256 amount, bytes memory data) public virtual override whenNotPaused { super.burn(from, amount, data); } @@ -184,7 +183,7 @@ contract SLYXToken is IVaultStakeRecipient, ISLYX, LSP7BurnableInitAbstract, Pau // Use 1:1 ratio if no sLYX is minted if (totalSLYXMinted == 0) return sLyxAmount; - // Get the total LYX balance held by the sLYX contract on the Vault. + // Get the total LYX balance held by the sLYX contract on the linked vault. uint256 sLyxTokenContractStake = stakingVault.balanceOf(address(this)); // Calculate how many % the amount being burnt in proportion to the total supply. @@ -213,8 +212,8 @@ contract SLYXToken is IVaultStakeRecipient, ISLYX, LSP7BurnableInitAbstract, Pau } /// @dev Prevent transferring sLYX tokens to: - /// - the linked Vault - /// - the SLYX Token contract itself + /// - the linked vault + /// - the `SLYXToken` contract itself /// otherwise the tokens would get stuck. function _beforeTokenTransfer( address, /* from */ @@ -232,7 +231,7 @@ contract SLYXToken is IVaultStakeRecipient, ISLYX, LSP7BurnableInitAbstract, Pau /// 1. convert `amount` of sLYX to the equivalent amount as LYX staked in the linked staking vault (including any accrued rewards). /// 2. transfer this equivalent amount back as staked LYX to `from`. /// - /// This is based on the sLYX / staked LYX ratio. The function accepts rounding error of 1 Wei as loss. + /// This is based on the sLYX / staked LYX ratio. The function accepts a rounding error of 1 wei as loss. /// Note that the `transferStake` call is done in this hook after balances and total supply have been decreased /// to follow the check-effect-interaction pattern as best practice. function _afterTokenTransfer(address from, address to, uint256 amount, bool, /* force */ bytes memory data) @@ -250,7 +249,7 @@ contract SLYXToken is IVaultStakeRecipient, ISLYX, LSP7BurnableInitAbstract, Pau // We need to account the burnt amount in the total supply for the calculation. uint256 totalSLYXMintedBeforeBurning = totalSupply() + amount; - // calculate the number of staked LYX (+ any accunmulated rewards) that should be transferred back to the `from` address. + // calculate the number of staked LYX (+ any accumulated rewards) that should be transferred back to the `from` address. uint256 amountAsStakedLYX = amount.mulDiv(sLyxTokenContractStake, totalSLYXMintedBeforeBurning); stakingVault.transferStake(from, amountAsStakedLYX, data);