stS technical analysis
Summary
This is a technical analysis of all the smart contracts of the asset and its main dependencies.
Disclosure: This is not an exhaustive security review of the asset like the ones done by the Beets Team, but an analysis from an Aave technical service provider on different aspects we consider critical to review before a new type of listing. Consequently, like with any security review, this is not an absolute statement that the asset is flawless, only that, in our opinion, we don’t see significant problems with its integration with Aave, apart from different trust points.
Analysis
stS is an LST representing $S staked via the Beets platform on the Sonic blockchain, accruing staking rewards from the $S delegated to Sonic’s PoS validators. Users can mint stS at its current exchange rate by depositing S, and redeem S later on, after a 14-day unstaking period.
For the context of this analysis, our focus has been on the following aspects, critical for the correct and secure integration with Aave:
- Mechanism to update the exchange rate of the asset for the underlying ETH, in this case, S.
- A recommendation of pricing strategy to be used in the integration asset <> Aave.
- Any miscellaneous aspect of the code we can consider important.
- Analysis of the access control (ownerships, admin roles) and the nature of the entities involved in the system. Regarding the table permissions’ holders and their criticality/risk, it is done following these guidelines:
Criticality | Description |
---|---|
CRITICAL | Usually super-admin functionality: it can compromise the system by completely changing its fundamentals, leading to loss of funds if misused or exploited. E.g. proxy admin, default admin |
HIGH | It can control several parts of the system with some risk of losing funds. E.g., general owners or admin roles involved in the flow of funds |
MEDIUM | It can cause malfunction and/or minor financial losses if misused or exploited. E.g., fee setter, fee recipient addresses |
LOW | It can cause system malfunctions but on non-critical parts without meaningful/direct financial losses. E.g., updating descriptions or certain non-critical parameters. |
Risk | Description |
---|---|
![]() |
The role is controlled via a mechanism we consider safe, such as on-chain governance, a timelock contract, or setups involving multi-sigs under certain circumstances. |
![]() |
The role is controlled in a way that could expose the system and users to some risk depending on the actions it can control. |
![]() |
The role is controlled via a clearly non-secure method, representing risks for the system and users. |
General points
- It relies on a single contract with most dependencies from OZ for access control, tokenization, upgradability, and security. The proxy contract uses the OZ UUPS upgradable pattern.
- The upgradeability admin of the system is an OZ Timelock with a 3-week delay.
- The system uses a role-based access control split across Timelocks, Safe wallets, and one EOA (for automation purposes).
Contracts
The following is a non-exhaustive overview of the main smart contracts involved with stS.
stS
The main contract of the system and entry point for minting stS. Users can deposit S for stS at its current exchange rate. Later, users can request redemption and wait 14 days to withdraw S. It uses a role-based access control, where operators can delegate S to validators via the Special Fee Contract (SFC) to start accruing rewards, a claimer to claim staking rewards, and a default admin in charge of more sensitive permissions. It is an upgradable contract using the OZ UUPS pattern.
Permission Owner | functions | Criticality | Risk |
---|---|---|---|
owner: 3-week Timelock | upgradeAndCall | CRITICAL | ![]() |
DEFAULT_ADMIN_ROLE : 1-day Timelock |
setWithdrawDelay, setTreasury, setProtocolFeeBIPS, setUndelegatePaused, setUndelegateFromPoolPaused, setWithdrawPaused, setDepositPaused | HIGH | ![]() |
OPERATOR_ROLE : 3-of-6 Safe |
delegate, operatorInitiateClawBack, operatorExecuteClawBack, donate, pause | HIGH | ![]() |
CLAIM_ROLE : EOA (0xFaC3…1718) |
claimRewards | HIGH | ![]() |
- Access Control
- The
DEFAULT_ADMIN_ROLE
controls the general configuration of the stS system and pauses isolated parts of the system. - The Default admin can update the withdrawal delay period, the protocol fee, and the treasury address that receives the fees via the
setWithdrawDelay(delay)
,setProtocolFeeBIPS(fee)
,setTreasury(address)
functions, respectively. - The default admin can pause deposits, withdrawals, and delegations via the
setDepositPaused(bool)
,setWithdrawPaused(bool)
,setUndelegatePaused(bool)
,setUndelegateFromPoolPaused(bool)
functions, respectively. - The operator can delegate S to validators via the
delegate(validatorId, amount)
function. It delegates by calling theSFC.delegate(validatorId, amount)
contract, which transfers theamount
of S to the correspondingvalidatorId
. - The operator can request a withdrawal of the delegated assets from a validator by calling the
operatorInitiateClawBack(validatorId, amount)
function. After the 14-day unstaking period, the operator can finish it by calling theoperatorExecuteClawBack(withdrawId, bool emergency)
function. - The operator can pause the entire system (deposits, withdrawals, delegations) via the
pause()
function. - The operator can increase the exchange rate via the
donate(msg.value)
function. - The claimer can claim staking rewards from multiple validators via the
claimRewards(validatorsIds[])
function.
- The
- Deposit and Withdrawals
- Users can deposit S via the
deposit(amount)
function. Internally, the function verifies that at least0.01 S
was deposited, calculates the actual exchange rate via theconvertToShares(amount)
function, increases thetotalPool
for internal accounting, and then mints stS shares to the user. - Users can request a withdrawal via the
undelegate(validatorId, amount)
andundelegateFromPool(amount)
functions. The main difference between them is that the first method will undelegate theamountAssets
from thevalidatorId
via theSFC.undelegate(validatorId, withdrawId, amountAssets)
function decreases the amount from thetotalDelegated
, while the second uses the assets within the stS contract, reducing the amount from thetotalPool
storage variable. - The minimum amount that users can request is
0.000001 S
. - Both functions will burn the stS shares from the user and create a
withdrawId
for the user, with the kind of withdrawal (POOL
,VALIDATOR
) and the amount. - After the 14-day
withdrawDelay
period, users can claim their S via thewithdraw(withdrawId, bool emergency)
. The emergency flag is marked astrue
when the user is concerned that his assets have been slashed, and the amount withdrawn will be less than the actual request.
- Users can deposit S via the
- Exchange Rate
- The stS exchange rate can be obtained by the general ERC4626 function
convertToAssets(shares)
, which internally uses thetotalAssets()
andtotalSupply()
. There is also agetRate()
function that callsconvertToAssets(1 ether)
. - The system relies on a proper internal accounting, where the
totalAssets()
is composed of the sum oftotalPool
(S in the stS contract),totalDelegated
(S delegated to validators), and thependingClawBackAmount
(amount requested to be unstaking from validators).
- The stS exchange rate can be obtained by the general ERC4626 function
- Delegation, staking, and rewards
- The delegation of S to Sonic validators starts via the
delegate(validatorId, amount)
function, which will call the SFC (Special Fee Contract), which tracks validators and delegators distributing rewards to them. Internally, the stS contract will decrease the amount fromtotalPool
and add to thetotalDelegated
storage variable. There is no minimum S that can be delegated. - The undelegation/withdrawal from the SFC contract happens in a two-step process, where first operators request it via the
operatorInitiateClawBack(validatorId, amountAssets)
, which will create a withdrawId with the kindCLAW_BACK
and callSFC.undelegate(validatorId, withdrawId, amountAssets)
. It also internally accounts for decreasing theamountAssets
fromtotalDelegated
to adding it to thependingClawBackAmount
storage variable. - After the 14-day withdrawal waiting period, the operator will call
operatorExecuteClawBack(uint256 withdrawId, bool emergency)
, which internally callsSFC.withdraw(validatorId, withdrawId)
. Internally, it is accounted for by decreasing theamountAssets
from thependingClawBackAmount
and adding it to thetotalPool
storage variable. - The
emergency
parameter flags the acknowledgment of the operator that thevalidatorId
might have been slashed, which affects the number of assets withdrawn, and it is very important to mention that this will decrease the stS <> S exchange rate. - The claimer (automated EOA) claims staked rewards via the
claimRewards(validatorIds[])
function, claiming the rewards in the SFC contract of eachvalidatorId
and verifying that the total claimed is at least0.000001 S
. Internally, aprotocolFeeBIPS
of 10% is applied to the rewards claimed and sends the fee to thetreasury
address. It finishes by adding the remaining to thetotalPool
storage variable, increasing the stS <> S exchange rate.
- The delegation of S to Sonic validators starts via the
Pricing strategy
We suggest pricing stS by using a CAPO adapter with the exchange rate provided by the stS contract and price component using the S Chainlink Feed.
We can confirm that the stS is safeguarded against any kind of donation attack due to its internal accounting for S across the system.
However, it is important to mention that the system contains a donate(msg.value)
function, only callable by the operator, that allows S to be sent to the contract, increasing the exchange rate.
Additionally, even if not in our technical scope, the asset is slashable both high-level and on the smart contract, so risk contributors should take that into account for parameter suggestions.
Miscellaneous
- The system has 2 security reviews from Trail of Bits and Spearbit with no High or Critical findings. The reports can be found here.
- The system has an Immunefi bug bounty of 200k. More details can be found here.
Conclusion
We think stS doesn’t have any problem in terms of integration with Aave, and there is no major technical blocker for listing.