Skip to content

Instantly share code, notes, and snippets.

@MarioPoneder
Last active July 19, 2024 01:25
Show Gist options
  • Save MarioPoneder/abfae63a5b456a1edf683d55266bbbaf to your computer and use it in GitHub Desktop.
Save MarioPoneder/abfae63a5b456a1edf683d55266bbbaf to your computer and use it in GitHub Desktop.
Security review of PossumLabsCrypto/Core

Security review report

Project: PossumLabsCrypto/Core
Commit: ae14254de919ba19809df09bf5a7e4d3070dc625
Start Date: 2024-07-15

Scope:

  • src/PossumCore.sol (nSLOC: 184, coverage: 100% lines / 98.52% statements / 92.11% branches / 100% functions)

Overview

  • [L-01] The guardian is allowed to whitelist the PossumCore contract
  • [L-02] Unused Core Fragments are forfeited on unstaking
  • [I-01] Inconsistent UX concerning the clamping of amounts
  • [I-02] User protection on zero PSM balance can be circumvented by malicious actor
  • [I-03] Forfeiting of Core Fragments on unstaking can be circumvented due to rounding

[L-01] The guardian is allowed to whitelist the PossumCore contract

Description

The guardian is allowed to whitelist address(this), i.e. the PossumCore contract itself. In this case, core stakers could increase their claimable reservedRewards by distributing to the PossumCore contract without changing its PSM balance. Therefore, no meaningful distribution is made while the total value of rewards, that can be extracted, potentially doubles since distributed PSM stays within the contract.

Recommendation

Add an additional check for address(this) to the updateWhitelist method:

function updateWhitelist(address _destination, bool _listed) external onlyGuardian {
    if (_destination == address(0) || _destination == address(this)) {
        revert InvalidAddress();
    }

   ...
}

Status:
βœ… Resolved in commit 338a5e49df17a1c4842dbd0a21a7468bf56795fc.

[L-02] Unused Core Fragments are forfeited on unstaking

Description

In the unstakeAndClaim method, the user's storedCoreFragments, which are unused Core Fragments intended for distribution, are forfeited in proportion to the unstaking amount and eventually reach zero on full unstaking.

Recommendation

It is advised to consider an alternative implementation which allows user's to keep and further distribute their unused Core Fragments after unstaking. However, no further interest should be accrued based on the rewards from distributions after unstaking due to the absent base stake.

Status:
πŸ†— Acknowledged.

[I-01] Inconsistent UX concerning the clamping of amounts

Description

In the unstakeAndClaim method, any amount > balance is silently clamped to balance. However, in the distributeCoreFragments method, any amount > userFragments leads to an InvalidAmount error.

Recommendation

It is recommended to always revert on an exceeding amount, but to explicitly clamp the amount to balance/userFragments when type(uint256).max is specified (for better UX).

Status:
βœ… Resolved in commits 7cf67e1f2fcae21a1cb6d920f6ed4b996e618724 & 0eded7695af347f387908d7d706de4fc15ecefed.

[I-02] User protection on zero PSM balance can be circumvented by malicious actor

Description

The stake mehtod employs a if (availablePSM == 0) revert InsufficientRewards() check to prevent users from staking when the PossumCore contract is out of PSM for rewards and distribution. Similary, the distributeCoreFragments method employs the same check to prevent users from executing an empty distribution in such a case.
However, a malicious actor could donate as little as 1 wei of PSM to the PossumCore contract to circumvent this check and therefore remove the present zero balance protection for honest users.

Status:
πŸ†— Acknowledged.

[I-03] Forfeiting of Core Fragments on unstaking can be circumvented due to rounding

Description

In the unstakeAndClaim method, the user's storedCoreFragments, which are unused Core Fragments intended for distribution, are forfeited in proportion to the unstaking amount and eventually reach zero on full unstaking.
However, the current computation of fragments to forefeit affectedFragments = (fragments * amount) / balance allows to unstake small amounts while the affectedFragments are still zero due to rounding.

Recommendation

Add a check that prevents unstaking without reducing the user's Core Fragments:

if (affectedFragments == 0 && fragments > 0) revert InvalidAmount();

Status:
πŸ†— Acknowledged.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment