Spectra - sl1'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: 9/39

Findings: 2

Award: $272.07

🌟 Selected for report: 0

πŸš€ Solo Findings: 0

Awards

107.431 USDC - $107.43

Labels

bug
2 (Med Risk)
satisfactory
sufficient quality report
edited-by-warden
:robot:_33_group
duplicate-210

External Links

Lines of code

https://github.com/code-423n4/2024-02-spectra/blob/383202d0b84985122fe1ba53cfbbb68f18ba3986/src/tokens/PrincipalToken.sol#L229-L237 https://github.com/code-423n4/2024-02-spectra/blob/383202d0b84985122fe1ba53cfbbb68f18ba3986/src/tokens/PrincipalToken.sol#L278-L287 https://github.com/code-423n4/2024-02-spectra/blob/383202d0b84985122fe1ba53cfbbb68f18ba3986/src/tokens/PrincipalToken.sol#L493-L495 https://github.com/code-423n4/2024-02-spectra/blob/383202d0b84985122fe1ba53cfbbb68f18ba3986/src/tokens/PrincipalToken.sol#L488-L490 https://github.com/code-423n4/2024-02-spectra/blob/383202d0b84985122fe1ba53cfbbb68f18ba3986/src/tokens/PrincipalToken.sol#L460-L462

Vulnerability details

Bug description

Principal Token contract is an ERC5095 vault that allows users to tokenize their yield in a permissionless manner.

Important The contest page explicitly states that PrincipalToken must conform with the EIP-5095.

The following specification of redeem() is taken directly from the EIP-5095:

At or after maturity, burns exactly principalAmount of Principal Tokens from from and sends underlyingAmount of underlying tokens to to. MUST support a redeem flow where the Principal Tokens are burned from holder directly where holder is msg.sender or msg.sender has EIP-20 approval over the principal tokens of holder.

The following specification of withdraw() is taken directly from the EIP-5095:

Burns principalAmount from holder and sends exactly underlyingAmount of underlying tokens to receiver. MUST support a withdraw flow where the principal tokens are burned from holder directly where holder is msg.sender or msg.sender has EIP-20 approval over the principal tokens of holder.

Both these functions should allow an approved user to redeem or withdraw approved Principal tokens, but currently, that's impossible. All redeem functions internally call _beforeRedeem(), which has this check in place:

if (_owner != msg.sender) {
            revert UnauthorizedCaller();
        }

The same can be said about every withdraw function. All of them call _beforeWithdraw(), which has the same check in place.

As per EIP-5095 both convertToUnderlying() and convertToPrincipal() functions MUST NOT revert unless due to integer overflow caused by an unreasonably large input. In case of convertToUnderlying() function, it calls _convertSharesToIBTs(), which can revert with RateError when ibtRate is 0. In case of convertToPrincipal() function, it calls _convertIBTsToShares(), which can revert with RateError when ptRate is 0.

As per EIP-5095 maxWithdraw() must not revert, but it can revert because of whenNotPausedModifier or because of the RateError mentioned above.

Impact

PrincipalToken contract does not respect the EIP standards it claims to follow. This breaks compatibility and violates invariants stated in the README.

Ensure that PrincipalToken is EIP-compliant. Alternatively, document that some functions deviate from the ERC5095 specification.

Assessed type

Other

#0 - c4-pre-sort

2024-03-03T09:18:55Z

gzeon-c4 marked the issue as duplicate of #33

#1 - c4-pre-sort

2024-03-03T09:19:19Z

gzeon-c4 marked the issue as sufficient quality report

#2 - c4-judge

2024-03-11T00:32:44Z

JustDravee marked the issue as satisfactory

Findings Information

Labels

bug
disagree with severity
downgraded by judge
grade-a
insufficient quality report
primary issue
QA (Quality Assurance)
sponsor disputed
edited-by-warden
Q-02

Awards

164.6381 USDC - $164.64

External Links

Lines of code

https://github.com/code-423n4/2024-02-spectra/blob/383202d0b84985122fe1ba53cfbbb68f18ba3986/src/tokens/PrincipalToken.sol#L369-L374 https://github.com/code-423n4/2024-02-spectra/blob/383202d0b84985122fe1ba53cfbbb68f18ba3986/src/tokens/PrincipalToken.sol#L329-L337

Vulnerability details

Bug description

claimFees() function allows the fee collector set by the protocol to claim fees in IBT. claimFees()

function claimFees() external override returns (uint256 assets) {
        if (msg.sender != IRegistry(registry).getFeeCollector()) {
            revert UnauthorizedCaller();
        }
        uint256 ibts = unclaimedFeesInIBT;
        unclaimedFeesInIBT = 0;
        assets = IERC4626(ibt).redeem(ibts, msg.sender, address(this));
        emit FeeClaimed(msg.sender, ibts, assets);
    }

claimYield() allows users to claim their yield in IBT. claimYield()

function claimYield(address _receiver) public override returns (uint256 yieldInAsset) {
        uint256 yieldInIBT = _claimYield();
        if (yieldInIBT != 0) {
            yieldInAsset = IERC4626(ibt).redeem(yieldInIBT, _receiver, address(this));
        }
    }

As can be seen, both these functions redeem ibts from the ERC4626 vault, but they lack minAssets check. To protect users or feeCollector from receiving less assets for their amount of ibts, minAssets parameter should be used.

Impact

The entire amount of ibts can be lost.

claimYield()

- function claimYield(address _receiver) public override returns (uint256 yieldInAsset) {
+ function claimYield(address _receiver, uint256 minAssets) public override returns (uint256 yieldInAsset) {
        uint256 yieldInIBT = _claimYield();
        if (yieldInIBT != 0) {
            yieldInAsset = IERC4626(ibt).redeem(yieldInIBT, _receiver, address(this));
+           require(yieldInAsset >= minAssets);
        }
    }

claimFees()

- function claimFees() external override returns (uint256 assets) {
+ function claimFees(uint256 minAssets) external override returns (uint256 assets) {
        if (msg.sender != IRegistry(registry).getFeeCollector()) {
            revert UnauthorizedCaller();
        }
        uint256 ibts = unclaimedFeesInIBT;
        unclaimedFeesInIBT = 0;
        assets = IERC4626(ibt).redeem(ibts, msg.sender, address(this));
+       require(assets >= minAssets);
        emit FeeClaimed(msg.sender, ibts, assets);
    }

Assessed type

Other

#0 - c4-pre-sort

2024-03-03T12:04:10Z

gzeon-c4 marked the issue as insufficient quality report

#1 - gzeon-c4

2024-03-03T12:05:25Z

unlikely for "claim slippage" to exists and the protocol should record claimed amount lack poc to show exploit

#2 - c4-pre-sort

2024-03-03T12:05:31Z

gzeon-c4 marked the issue as primary issue

#3 - c4-judge

2024-03-11T01:07:07Z

JustDravee marked the issue as unsatisfactory: Insufficient proof

#4 - c4-judge

2024-03-11T01:42:37Z

JustDravee marked the issue as unsatisfactory: Invalid

#5 - kazantseff

2024-03-12T08:47:13Z

Hey @JustDravee, The PT contract is built on top of the ERC4626 vault and the whole idea of it, is that rates are subject to volatility, so the claim slippage will exist. redeem function of the PT contract has slippage protection and it does exactly the same action as two of the function described here. Could you please take another look?

#6 - c4-sponsor

2024-03-14T13:08:19Z

yanisepfl (sponsor) disputed

#7 - c4-sponsor

2024-03-14T13:08:23Z

yanisepfl marked the issue as disagree with severity

#8 - yanisepfl

2024-03-14T13:13:24Z

Hello @kazantseff and @JustDravee,

While the issue reported is a good catch that we will tackle, it only points out an inconsistency in our code. The standards that we follow do not necessitate to have slippage protection, and we consider it as a good-to-have feature.

Therefore, we rather consider it as a low severity issue.

Thanks for the report!

#9 - c4-judge

2024-03-14T21:47:29Z

JustDravee removed the grade

#10 - c4-judge

2024-03-14T21:47:35Z

JustDravee changed the severity to QA (Quality Assurance)

#11 - c4-judge

2024-03-14T21:47:43Z

JustDravee marked the issue as grade-b

#12 - c4-judge

2024-03-15T11:40:44Z

JustDravee marked the issue as grade-a

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