Spectra - 0xLuckyLuke'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: 38/39

Findings: 1

Award: $19.59

🌟 Selected for report: 0

πŸš€ Solo Findings: 0

Findings Information

Labels

bug
grade-b
QA (Quality Assurance)
sufficient quality report
edited-by-warden
Q-08

Awards

19.5868 USDC - $19.59

External Links

[L-1] Addressing Maturity-Related Redemption Inconsistencies

Summary

Users can initiate redemptions regardless of the maturity status of the shares. This violates the expected behavior specified in EIP-5095, which recommends redemptions only after maturity.

Vulnerability Detail

The function PrincipalToken::_beforeRedeem allows users to redeem shares without considering maturity, which contradicts EIP-5095 standard behavior which is mentioned in the following:

https://eips.ethereum.org/EIPS/eip-5095 "PTs mature at a precise second, but given the reactive nature of smart contracts, there can’t be an event marking maturity, because there is no guarantee of any activity at or after maturity. Emitting an event to notify of maturity in the first transaction after maturity would be imprecise and expensive. Instead, integrators are recommended to either use the first Redeem event, or to track themselves when each PT is expected to have matured."

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

Impact

Off-chain information may be inaccurate as redemptions can occur before maturity, leading to potential misinformation. Contradicts standard behavior, potentially causing confusion among users and integrators.

Tool used

Manual Review

Recommendation

Implement maturity checks to ensure redemptions occur only after maturity, adhering to the EIP-5095 standard. Enhance off-chain communication to provide accurate information regarding redemption eligibility and maturity status.

[L-2] Return value of updateYield() is not validated

Proof of Concept

The PrincipalToken::updateYield function returns the updated user yield.

    function beforeYtTransfer(address _from, address _to) external override {
        if (msg.sender != yt) {
            revert UnauthorizedCaller();
        }
        updateYield(_from);
        updateYield(_to);
    }

Tools Used

VScode

Consider checking the return value to be valid.

[I-1] Streamlining Token Handling in Flash Loan Functions

Details

Simplified Token Handling: The _token parameter in the flashLoan function seems unnecessary since the token address (ibt) remains constant throughout the function. Removing this parameter would streamline the interface.

Efficient Loan Calculation: The maxFlashLoan function accurately determines the maximum loan amount based on the contract's token balance, returning 0 if the token address differs from ibt.

Secure Fee Computation: The flashFee function calculates fees exclusively for the designated token (ibt) and ensures safety by reverting with an AddressError if the provided token address doesn't match.

Overall: The functions demonstrate clear logic and adhere to best practices for flash loan implementations, with potential for interface simplification and efficient token handling.

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

function flashLoan(
    IERC3156FlashBorrower _receiver,
-    address _token,
    uint256 _amount,
    bytes calldata _data
) external override returns (bool) {
-    if (_amount > maxFlashLoan(_token)) revert FlashLoanExceedsMaxAmount();
+    if (_amount > maxFlashLoan(ibt)) revert FlashLoanExceedsMaxAmount();

-    uint256 fee = flashFee(_token, _amount);
+    uint256 fee = flashFee(ibt, _amount);
    _updateFees(fee);

    // Initiate the flash loan by lending the requested IBT amount
    IERC20(ibt).safeTransfer(address(_receiver), _amount);

    // Execute the flash loan
-    if (_receiver.onFlashLoan(msg.sender, _token, _amount, fee, _data) != ON_FLASH_LOAN)
+    if (_receiver.onFlashLoan(msg.sender, ibt, _amount, fee, _data) != ON_FLASH_LOAN)
        revert FlashLoanCallbackFailed();

    // Repay the debt + fee
    IERC20(ibt).safeTransferFrom(address(_receiver), address(this), _amount + fee);

    return true;
}

[I-2] Only msg.sender can redeem and withdraw

Impact

It can reduce the level of compatiblity and integrity in different situations and with other platforms.

Proof of Concept

in _beforeRedeem and _beforeWithdraw functions of the PrincipalToken contract if msg.sender is not equal to the owner, it reverts.

 function _beforeRedeem(uint256 _shares, address _owner) internal nonReentrant whenNotPaused {
        if (_owner != msg.sender) {
            revert UnauthorizedCaller();
        }
     ...

Tools Used

VScode

It is recommended that when input address is not the same as msg.sender check if msg.sender has alloance from the _owner address.

#0 - c4-pre-sort

2024-03-03T13:55:42Z

gzeon-c4 marked the issue as sufficient quality report

#1 - c4-judge

2024-03-11T01:33:50Z

JustDravee marked the issue as grade-b

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