[ARFC] Add stS to Aave v3 Sonic Instance

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
:green_circle: 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.
:yellow_circle: The role is controlled in a way that could expose the system and users to some risk depending on the actions it can control.
:red_circle: 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 :green_circle:
DEFAULT_ADMIN_ROLE: 1-day Timelock setWithdrawDelay, setTreasury, setProtocolFeeBIPS, setUndelegatePaused, setUndelegateFromPoolPaused, setWithdrawPaused, setDepositPaused HIGH :green_circle:
OPERATOR_ROLE: 3-of-6 Safe delegate, operatorInitiateClawBack, operatorExecuteClawBack, donate, pause HIGH :green_circle:
CLAIM_ROLE: EOA (0xFaC3…1718) claimRewards HIGH :green_circle:
  • 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 the SFC.delegate(validatorId, amount) contract, which transfers the amount of S to the corresponding validatorId.
    • 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 the operatorExecuteClawBack(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.
  • Deposit and Withdrawals
    • Users can deposit S via the deposit(amount) function. Internally, the function verifies that at least 0.01 S was deposited, calculates the actual exchange rate via the convertToShares(amount) function, increases the totalPool for internal accounting, and then mints stS shares to the user.
    • Users can request a withdrawal via the undelegate(validatorId, amount) and undelegateFromPool(amount) functions. The main difference between them is that the first method will undelegate the amountAssets from the validatorId via the SFC.undelegate(validatorId, withdrawId, amountAssets) function decreases the amount from the totalDelegated, while the second uses the assets within the stS contract, reducing the amount from the totalPool 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 the withdraw(withdrawId, bool emergency). The emergency flag is marked as true when the user is concerned that his assets have been slashed, and the amount withdrawn will be less than the actual request.
  • Exchange Rate
    • The stS exchange rate can be obtained by the general ERC4626 function convertToAssets(shares), which internally uses the totalAssets() and totalSupply(). There is also a getRate() function that calls convertToAssets(1 ether).
    • The system relies on a proper internal accounting, where the totalAssets() is composed of the sum of totalPool (S in the stS contract), totalDelegated (S delegated to validators), and the pendingClawBackAmount (amount requested to be unstaking from validators).
  • 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 from totalPool and add to the totalDelegated 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 kind CLAW_BACK and call SFC.undelegate(validatorId, withdrawId, amountAssets). It also internally accounts for decreasing the amountAssets from totalDelegated to adding it to the pendingClawBackAmount storage variable.
    • After the 14-day withdrawal waiting period, the operator will call operatorExecuteClawBack(uint256 withdrawId, bool emergency), which internally calls SFC.withdraw(validatorId, withdrawId). Internally, it is accounted for by decreasing the amountAssets from the pendingClawBackAmount and adding it to the totalPool storage variable.
    • The emergency parameter flags the acknowledgment of the operator that the validatorId 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 each validatorId and verifying that the total claimed is at least 0.000001 S. Internally, a protocolFeeBIPS of 10% is applied to the rewards claimed and sends the fee to the treasury address. It finishes by adding the remaining to the totalPool storage variable, increasing the stS <> S exchange rate.


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.

5 Likes