Spectra - erosjohn's results

A permissionless interest rate derivatives protocol on Ethereum.

General Information

Platform: Code4rena

Start Date: 23/02/2024

Pot Size: $36,500 USDC

Total HM: 2

Participants: 39

Period: 7 days

Judge: Dravee

Id: 338

League: ETH

Spectra

Findings Distribution

Researcher Performance

Rank: 23/39

Findings: 1

Award: $80.57

🌟 Selected for report: 0

🚀 Solo Findings: 0

Awards

80.5733 USDC - $80.57

Labels

bug
2 (Med Risk)
partial-75
sufficient quality report
:robot:_33_group
duplicate-210

External Links

Lines of code

https://github.com/code-423n4/2024-02-spectra/blob/main/src/tokens/PrincipalToken.sol#L446 https://github.com/code-423n4/2024-02-spectra/blob/main/src/tokens/PrincipalToken.sol#L460 https://github.com/code-423n4/2024-02-spectra/blob/main/src/tokens/PrincipalToken.sol#L483

Vulnerability details

Impact

The sponsor mentioned in Main invariants that Principal Token is ERC5095, but PrincipalToken.sol is not fully EIP-5095 compliant, variation from the standard could break composability.

Proof of Concept

According to EIP-5095 method specifications (https://eips.ethereum.org/EIPS/eip-5095) For maxRedeem

    function maxRedeem(address owner) public view override returns (uint256) {
        return _maxBurnable(owner);
    }

    function _maxBurnable(address _user) internal view returns (uint256 maxBurnable) {
        if (block.timestamp >= expiry) {
            maxBurnable = balanceOf(_user);
        } else {
            uint256 ptBalance = balanceOf(_user);
            uint256 ytBalance = IYieldToken(yt).balanceOf(_user);
            maxBurnable = (ptBalance > ytBalance) ? ytBalance : ptBalance;
        }
    }

Violates the following standard:

  1. MUST factor in both global and user-specific limits, like if redemption is entirely disabled (even temporarily) it MUST return 0.

For maxWithdraw

    function maxWithdraw(address owner) public view override whenNotPaused returns (uint256) {
        return convertToUnderlying(_maxBurnable(owner));
    }

Violates the following standards:

  1. MUST factor in both global and user-specific limits, like if withdrawals are entirely disabled (even temporarily) it MUST return 0.
  2. MUST NOT revert.

For previewWithdraw

    function previewWithdraw(
        uint256 assets
    ) external view override whenNotPaused returns (uint256) {
        uint256 ibts = IERC4626(ibt).previewWithdraw(assets);
        return previewWithdrawIBT(ibts);
    }

Violates the following standard:

  1. MUST NOT revert due to principal token contract specific user/global limits. MAY revert due to other conditions that would also cause withdraw to revert.

When PrincipalToken.sol is paused, maxRedeem and maxWithdraw should return 0, and all these functions should not revert.

Tools Used

Manual Review

  1. maxRedeem and maxWithdraw should be updated to return 0 when contract is paused.
  2. Do not use the whenNotPaused modifier because that would cause a revert(maxRedeem and maxWithdraw should never revert according to EIP-5095).

Assessed type

Error

#0 - c4-pre-sort

2024-03-03T09:20:37Z

gzeon-c4 marked the issue as duplicate of #33

#1 - c4-pre-sort

2024-03-03T09:20:39Z

gzeon-c4 marked the issue as sufficient quality report

#2 - c4-judge

2024-03-11T00:30:31Z

JustDravee marked the issue as partial-75

#3 - c4-judge

2024-03-11T00:30:37Z

JustDravee marked the issue as satisfactory

#4 - c4-judge

2024-03-14T06:21:45Z

JustDravee marked the issue as partial-75

AuditHub

A portfolio for auditors, a security profile for protocols, a hub for web3 security.

Built bymalatrax © 2024

Auditors

Browse

Contests

Browse

Get in touch

ContactTwitter