Spectra - DarkTower'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: 19/39

Findings: 2

Award: $84.10

🌟 Selected for report: 0

🚀 Solo Findings: 0

Findings Information

Labels

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

Awards

19.5868 USDC - $19.59

External Links

[L-01] PrincipalToken::_depositIBT doesn't emit an event with _ytReceiver address.

The function properly emits the event with the _ptReceiver address but it is not emitted for _ytReceiver as tokens are also minted for _ytReceiver address. We recommend a similar event emission for better off-chain monitoring.

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

        emit Mint(msg.sender, _ptReceiver, shares);

        IYieldToken(yt).mint(_ytReceiver, shares);
        // @audit-info no emission for _ytReciever.

[L-02] For withdraw functionality, withdraw event should be emitted instead of the redeem event

The vault is ERC-4626 equivalent. withdraw and redeem are two different operations performed in ERC-4626.

The function _withdrawShares is used to perform withdraw not redeem so it should emit withdraw event instead of redeem to avoid confusion off-chain.

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

    function _withdrawShares(
        uint256 _ibts,
        address _receiver,
        address _owner,
        uint256 _ptRate,
        uint256 _ibtRate
    ) internal returns (uint256 shares) {
        if (_ptRate == 0) {
            revert RateError();
        }
        // convert ibts to shares using provided rates
        shares = _ibts.mulDiv(_ibtRate, _ptRate, Math.Rounding.Ceil);
        // burn owner's shares (YT and PT)
        if (block.timestamp < expiry) {
            IYieldToken(yt).burnWithoutUpdate(_owner, shares);
        }
        _burn(_owner, shares);
@>        emit Redeem(_owner, _receiver, shares);
    }

[L-03] RayMath::fromRay should be careful that _decimals should be lesser than or equal to 27

The function doesn't take care of a case where the _decimals is greater or equal to 27, if this happens the operation will output zero.

https://github.com/code-423n4/2024-02-spectra/blob/main/src/libraries/RayMath.sol#L35

    function fromRay(uint256 _a, uint256 _decimals) internal pure returns (uint256 b) {
        // @audit-info check decimals is not greater than 27
        uint256 decimals_ratio = 10 ** (27 - _decimals); // @audit greater than 27 will cause problem here.
        assembly {
            b := div(_a, decimals_ratio)
        }
    }

[L-04] RayMath::fromRay should enforce a check that the _a argument is not zero

The fromRay function in the RayMath library should check if the input variable _a is non-zero.

https://github.com/code-423n4/2024-02-spectra/blob/main/src/libraries/RayMath.sol#L35

    function fromRay(
        uint256 _a,
        uint256 _decimals,
        bool _roundUp
    ) internal pure returns (uint256 b) {
        // @audit-info should check if `_a` is not zero
        uint256 decimals_ratio = 10 ** (27 - _decimals);
        assembly {
            b := div(_a, decimals_ratio)

            if and(eq(_roundUp, 1), gt(mod(_a, decimals_ratio), 0)) {
                b := add(b, 1)
            }
        }
    

#0 - c4-pre-sort

2024-03-03T13:54:33Z

gzeon-c4 marked the issue as sufficient quality report

#1 - c4-judge

2024-03-11T00:53:53Z

JustDravee marked the issue as grade-b

Findings Information

🌟 Selected for report: hunter_w3b

Also found by: 0xbrett8571, DarkTower, Myd, ZanyBonzy, aariiif

Labels

analysis-advanced
grade-b
sufficient quality report
A-05

Awards

64.5087 USDC - $64.51

External Links

Preface

Review process

Architectural Recommendations

Call trace diagrams

Weak spots and mitigations

Conclusion


1. Preface

This analysis report has been approached with the following key points and goals in mind:

  • Devoid of redundant documentation the protocol is familiar with.
  • Engineered towards provision of valuable insights and edge case issues the protocol left unnoticed.
  • Simple to grasp call-trace diagrams for first-time users who stumble upon this report in the future.

2. Review process

D1-2:

  • Understanding of Spectra's concept
  • Mapping out security and attack areas relating to shares, proxy upgrades and role mis-use.
  • Keeping track of the outlined areas with notes on contracts in-scope

D2-4:

  • Brainstorm possible reachability of undesired states
  • Test the areas identified
  • Further review of contract-level mitigations

D4-7:

  • Apply mitigations
  • Draft & submit report

3. Architectural Recommendations

Testing suite:

Good coverage; could be much better - The Spectra team has ensured the codebase is rigoriously tested. There exists a plethora of unit tests and some stateless fuzz tests within the test suite bringing the coverage up to 90%. The team has tested the contracts in-scope with various params in a separate test contract. This technique covers testing for expected inputs/outputs as well as separate test contracts for unexpected inputs and fail-safe end results for those unexpected inputs. Such in-depth suite of unit & stateless fuzzing tests ensure a pretty good coverage for the core functions call traces. The codebase would be even further bullet-proofed with some invariant tests.

What is unique within Spectra?

  • Tokenized yield: Users when depositing an IBT (interest bearing token) e.g aUSDC into Spectra get the underlying asset they supplied for the IBT in the corresponding protocol as well as the promised yield by that protocol. This concept is what we refer to as the zero-day yield. Bob deposits 1000 USDC into Aave 1st January 2024. He is entitled to 30 USDC at the end of the year on a 3% APY. It's 2 days into the deposit transaction on Aave and Bob intends to utilize his 1000 USDC but withdrawing from Aave means forfeiting his 30 USDC promised yield. Bob decides to deposit the aUSDC share token Aave issued Bob as a representation of his 1000 USDC into Spectra. Spectra gives Bob an opportunity to withdraw 1000 USDC and close to 30 USDC. In essence, Spectra has taken over the burden of holding 1000 aUSDC for 365 days. This is the zero-day yield concept.

  • Principal and Yield tokens are transferrable: Generally, you would expect the tokenized PT and YT tokens to be non-transferrable between addresses because of risk attached to redeeming 2x for 1. Spectra has this issue covered because they never actually redeem such IBT token yields from their own balance - each balance of IBT or underlying asset is provided by users, hence redeems are facilitated using the user deposited balances in the IBT protocols e.g Aave USDC Lending pool.

  • Liquidity provision with tokenized yield positions: Users can use their tokenized Principal Token for providing liquidity on Curve Pools. This creates an extra profit route for users on top of the yield they already tokenized.

Comparison between Spectra and what was previously out there

FeatureSpectraTempus
GoalPermissionless interest rate derivatives protocol on EthereumPioneer the transition of Tradfi interest rate derivatives on the Ethereum blockchain
TokensUnwraps IBT into it's underlying form and yield form to be redeemed wheneverUnwraps YBT into a principal token and yield token to be traded between users
Benefits to usersParty A gains the risk protection of a fixed rate. Party B gains possibility of profit for anticipated yield interest rate increaseParty A & B trade the PT and YT on the TempusAMM. A set of smart contracts facilitating price speculation for YT and PT swaps. Generally, this created a fragmented liquidity problem within the Tempus protocol.
RiskUnderlying asset is transferred to party B post trade of yield before or post maturity (e.g Bob takes George's YT for close to 30 USDC)The asset prices were basically determined by the AMM in part but also the Balancer Pools they're tied to. Underlying risk still remained in the fact that the AMM price was adverse from the real market value of the IBT i.e underlying asset plus profit

4. Call trace diagrams

Entry point: Entry

Exit point: Entry

5. Weak spots and mitigations

  • Any amount can be tokenized Spectra doesn't actually hold the IBT token users deposit. Another user can buy the yield from the user looking to tokenize aUSDC. What happens when you have too little shares that are almost always worthless to take on in the Ethereum network because of the fees? They end up not being taken on by the other users looking to make a profit from a perceived interest rate increase in the future. There's no lower bound for how much can be tokenized minimum. We recommend for the Spectra team to enforce a minimum yield tokenization.

  • Issue with the zero-day yield: The protocol can enter a state of influx deposits that outweigh bids. For example, the Spectra protocol doesn't have anything to do with the user's deposited aUSDC, the user maintains full control of the asset's deposit and withdrawal. Should the user intend to sell their yield, another user can agree to take on the position in hopes of making a profit. This sets up a scenario where user A keeps bringing in aUSDC they've minted yesterday from Aave hoping to get a quick bidder to sell the tokenized yield to, therefore creating a dump facilitating mechanism. This issue existed within the Tempus AMM with fragmented liquidity. There's no easy fix for this issue but enforcing some limits on transaction sizes as we suggested up above is sufficient to mitigate this risk.

6. Conclusion

In conclusion, the Spectra team is building a protocol not yet available on the Ethereum blockchain with it's rich sets of unique features most importantly, the yield tokenization and liquidity provision feature. Tempus was participating in this realm but has been sunsetted. We expect the Spectra protocol will gain massive adoption in the near short term post launch.

Time spent:

10 hours

#0 - c4-pre-sort

2024-03-03T14:06:34Z

gzeon-c4 marked the issue as high quality report

#1 - c4-pre-sort

2024-03-03T14:06:38Z

gzeon-c4 marked the issue as sufficient quality report

#2 - c4-judge

2024-03-11T00:51:14Z

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