Organization
- @sentient-labs
Engagement Type
Cantina Reviews
Period
-
Repositories
Researchers
Findings
Low Risk
7 findings
5 fixed
2 acknowledged
Informational
7 findings
3 fixed
4 acknowledged
Gas Optimizations
1 findings
0 fixed
1 acknowledged
Low Risk7 findings
Hard-coded Gas Amount
Severity
- Severity: Low
≈
Likelihood: Low×
Impact: Low Submitted by
red-swan
Description
There are several locations that have hard-coded gas amounts:
emergencyWithdrawinSentientRewardDistributoremergencyWithdrawinSentientStakingDepositManager
This is considered a bad practice in that gas costs can change or it might not be enough for smart contracts to utilize.
Recommendation
Consider forwarding all gas when making external calls
Floating Pragma
Severity
- Severity: Low
≈
Likelihood: Low×
Impact: Low Submitted by
red-swan
Description
On-chain code should have clear descriptions of what is does. That includes what compiler the developer intends the code to be compiled with so that future, valid but breaking compiler versions can be avoided.
Recommendation
Consider fixing the compiler version of ArtifactFactory.
Duplicate Code
Severity
- Severity: Low
≈
Likelihood: Low×
Impact: Low Submitted by
red-swan
Description
There are a few locations in the codebase where multiple functions provide the same functionality. Such code can make it harder to understand the features of a codebase or how it can be used.
- In
ArtifactManagerthere are two functions that provide the active artifacts:getArtifactListandartifactList(). - In
ArtifactManagerthere are wrappers around the AccessControlgrantRoleandrevokeRolefunctions just for a role bespoke to this contract:grantFactoryRoleandrevokeFactoryRole.
Recommendation
Consider whether these superfluous functions are critical to the contract's lifecycle and remove them if not.
Risk of Native Token Loss
State
- Acknowledged
Severity
- Severity: Low
≈
Likelihood: Low×
Impact: Low Submitted by
red-swan
Description
The
depositfunction inSentientStakingDepositManageris payable to allow users to stake SENT tokens on artifacts. It also allows users to stake ERC20 tokens. It does not, however, check, that if a user is staking ERC20 that they have not accidentally sent SENT with the transaction.Recommendation
Consider adding such a check to protect a user from creating such an unintended, yet valid transaction.
Roots can't be denied when paused
State
- Acknowledged
Severity
- Severity: Low
Submitted by
Om Parikh
Description
If a merkle root was set just before a pause, then due to
whenNotPausedmodifier, authorized parties might not be able to deny it and window may pass before unpausing.Given pauses are only executed during catastrophic times, and not allowing to deny roots during such periods might allow a malicious root being added.
for e.g:
- at t=t0, root was added
- at t=t1, pause was triggered
- now at t=t3 when sufficient time has passed, approve could be called (front-running deny if required)
Recommendation
- consider allowing
denyMerkleRooteven if paused - this could be tackled on operational front by batching pausing with increase
merkleRootPendingPeriodto a duration larger than for which contracts will be paused for
ADMIN_ROLE can set bad timestamp
Severity
- Severity: Low
≈
Likelihood: Low×
Impact: Low Submitted by
red-swan
Description
The
reportArtifactUsagefunction in theArtifactManagercontract allows the holder of theADMIN_ROLEto storage usage data for any artifact. There is an implicit assumption that thetimestampused should increase over time and we believe this should be made explicit. It would effect all reporting on artifact usage if it were ever set to a previous time.Recommendation
Revert if the timestamp is less or equal to a previous timestamp.
Unnecessary Computation
Severity
- Severity: Low
≈
Likelihood: Low×
Impact: Low Submitted by
red-swan
Description
In the
ArtifactManagercontract artifacts usage is stored and retrievable by iterating through the stored details. However, there is a top-level variable stored calledusageCountthat denotes the total usage of an artifact over time. This means then, that the function_calculateTotalUsagefunction is entirely superfluous as it iterates through to calculate this stored variable.Recommendation
Remove this function and retrieve the stored value when needed
Informational7 findings
Duplicate Event Emissions
State
- Acknowledged
Severity
- Severity: Informational
≈
Likelihood: Low×
Impact: Low Submitted by
red-swan
Description
In the codebase there are several functions that emit events to update off-chain systems of on-chain state changes. Some of these do not guard against the possibility of emitting an event even though the on-chain state has not changed. For example:
- SentientRewardDistributor.setMerkleProofPendingPeriod
- SentientStakingDepositManager.setDepositEnabled
- SentientStakingDepositManager.setWithdrawEnabled
- ArtifactFactory.setArtifactBeacon
- ArtifactManager.setArtifactFactory
Recommendation
Check input parameters and revert if they are identical to the existing state they are assigning to
Code Clarity
State
- Acknowledged
Severity
- Severity: Informational
≈
Likelihood: Low×
Impact: Low Submitted by
red-swan
Description
There are several locations where the code can be improved in its accuracy or clarity
- The use of
uncheckedto increment loop counters inArtifactManagerwhen solidity now does this automatically as of version0.8.22 - The use of
abi.encodeWithSelectoris not type safe and should be eschewed in favor orabi.encodeCall - Contracts that inherit
Contexthave_msgSender()and_msgData()to allow chain-specific unwrapping of these values. Therefore, a consistent style of using these overmsg.senderormsg.datais preferred.ArtifactFactory,ArtifactManager,SentientRewardDistributor, andSentientStakingDepositManagerall are inconsistent in this style. - In
SentientRewardDistributor, theSentientRewardDistributor__InvalidProofserror is unused - In
SentientStakingDepositManager, theStakingTokenIdstruct is unused - In
ArtifactManager, there are two functions namedartifactListthat return different pieces of information. - In
Artifact, theMAX_INITIAL_OWNERSconstant is unused - Both
IArtifactandIArtifactManagerdefine aArtifactInfostruct which should be named differently as they represent different things - Consider documenting that, in
SentientRewardDistributor.claimRewardsforkeccak256(abi.encodePacked(msg.sender, amounts[i]));holds valid only if there is atmost 1 entry per address in when constructing root. - Using
codesize()inWSENT::withdrawprovides minimal gas optimizations at the expense of readbility and transparency. Consider replacing it with0x00for readability or even use mainlineWETH9 - The
_artifactListstate variable in theArtifactManagercontract is used to house only active artifacts. Its name however may mislead readers/developers to think that it houses all artifacts instead. - The comments in
Artifact'sinitializefunction describe storage slots and packing that is incorrect._owneris stored in its own slot and the ArtifactInfo struct stores_expectedApyand_commissionRateas uint256 so there's no packing with those two either.
Recommendation
Consider updating the highlighted sections above to be more accurate or clear in their intent.
Lack of Security Contact
State
- Acknowledged
Severity
- Severity: Informational
≈
Likelihood: Low×
Impact: Low Submitted by
red-swan
Description
Adding a security contact into the smart contract allows good-faith actors to notify developers of issues in the code as quick as possible. Without such, it may be impossible for a found bug to be reported. None of the contracts in scope had such a security contact.
Recommendation
Consider adding a NatSpec comment containing a security contact at the top of the contract. Here is an instructive example from
ethereum-lists.Typo in pendingMerkleProofs variable naming
State
- Acknowledged
Severity
- Severity: Informational
Submitted by
Om Parikh
Description
consider renaming to
pendingMerkleProofstopendingMerkleRootsto reflect its correct purpose.Recommendation
- mapping(address => bytes32) public pendingMerkleProofs+ mapping(address => bytes32) public pendingMerkleRoots- mapping(address => bytes32) public pendingMerkleProofsTimestamp+ mapping(address => bytes32) public pendingMerkleRootsTimestamp- uint256 public merkleProofPendingPeriod = 1 hours;+ uint256 public merkleRootPendingPeriod = 1 hours;Unable to withdraw native token
Severity
- Severity: Informational
≈
Likelihood: Low×
Impact: Low Submitted by
red-swan
Description
In
SentientStakingDepositManager, thedepositfunction allows users to use native tokens for their staking. Thewithdrawfunction returns these deposits as wrapped tokens.Recommendation
Consider allowing the user to withdraw native tokens from the contract.
Inactive reports are not reportable
Severity
- Severity: Informational
Submitted by
red-swan
Description
In the
ArtifactManagercontract, artifact reporting is retrieved using several functions. Though many of these only work if the artifact is active. As these are view functions and developed to provide value on chain, it seems curious that this reporting distinction would be made as the data is not private in any way.Recommendation
Consider whether inactive artifacts should be reported on as well.
Unchecked Loop Increments Are Now Optimized
Severity
- Severity: Informational
Submitted by
red-swan
Description
In the codebase there are several locations where loop variables are incremented using unchecked variables. This was a widespread practice as the Solidity Compiler would add expensive bounds checking in loops. But as of Solidity v0.8.22, the compiler no longer does this and instead uses the method as coded here. An overview of the feature is explained in the Solidity blog here.
Recommendation
Consider removing unchecked loop increments
Gas Optimizations1 finding
replace ReentrancyGuard with ReentrancyGuardTransient
State
- Acknowledged
Severity
- Severity: Gas optimization
Submitted by
Om Parikh
Description
In
SentientStakingDepositManagerandSentientRewardDistributor,ReentrancyGuardcan be switched withReentrancyGuardTransientfor consistent gas savings across most of the user transactions.Recommendation
Consider switching to
ReentrancyGuardTransientif transient opcodes are supported