Centrifuge - 0xAadi's results

The institutional ecosystem for on-chain credit.

General Information

Platform: Code4rena

Start Date: 08/09/2023

Pot Size: $70,000 USDC

Total HM: 8

Participants: 84

Period: 6 days

Judge: gzeon

Total Solo HM: 2

Id: 285

League: ETH

Centrifuge

Findings Distribution

Researcher Performance

Rank: 78/84

Findings: 1

Award: $12.79

QA:
grade-b

🌟 Selected for report: 0

šŸš€ Solo Findings: 0

Awards

12.7917 USDC - $12.79

Labels

bug
grade-b
QA (Quality Assurance)
sufficient quality report
Q-14

External Links

Summary

Low Risk Issues

IssueInstances
[L‑01]Ward/Authorized admin can't able to execute functions in LiquidityPool.sol6
[L‑02]Missing Event for critical parameters change2
[L‑03]delay can unintentionally be set to an indefinite value1

Non-critical Issues

IssueInstances
[N‑01]Remove commented code1
[N‑02]require statement is not needed in src/PoolManager.sol#deployLiquidityPool1
[N‑03]Adding events within the incoming and outgoing functions enhances transparency1

Low Risk Issues

[L‑01] Ward/Authorized admin can't able to execute functions in LiquidityPool.sol

Some functions in src/LiquidityPool.sol are intended to be executed by ward/authorized admins. However, these functions are not being executed by ward/authorized admins due to the faulty implementation of the withApproval() modifier.

https://github.com/code-423n4/2023-09-centrifuge/blob/512e7a71ebd9ae76384f837204216f26380c9f91/src/LiquidityPool.sol#L96C4-L100C6

    /// @dev Either msg.sender is the owner or a ward on the contract
    modifier withApproval(address owner) {
        require(msg.sender == owner, "LiquidityPool/no-approval");
        _;
    }

The comment in the preceding code suggests that the code is meant to grant access to a ward, but the logic to do so has not been implemented.

As a result of this issue, authorized admins are unable to access the following functions.

There is 6 instance of this issue:

GitHub: 174, 200, 214, 231, 247, 253

[L‑02] Missing Event for critical parameters change

Events help non-contract tools to track changes, and events prevent users from being surprised by changes.

There is 2 instance of this issue:

File: src/LiquidityPool.sol

    /// @notice Request decreasing the outstanding deposit orders. Will return the assets once the order
    ///         on Centrifuge is successfully decreased.
    function decreaseDepositRequest(uint256 assets, address owner) public withApproval(owner) {
        investmentManager.decreaseDepositRequest(assets, owner);
    }


    /// @notice Request decreasing the outstanding redemption orders. Will return the shares once the order
    ///         on Centrifuge is successfully decreased.
    function decreaseRedeemRequest(uint256 shares, address owner) public withApproval(owner) {
        investmentManager.decreaseRedeemRequest(shares, owner);
    }

GitHub: 245

File: src/PoolManager.sol

    function updateTrancheTokenMetadata(
        uint64 poolId,
        bytes16 trancheId,
        string memory tokenName,
        string memory tokenSymbol
    ) public onlyGateway {
        TrancheTokenLike trancheToken = TrancheTokenLike(getTrancheToken(poolId, trancheId));
        require(address(trancheToken) != address(0), "PoolManager/unknown-token");


        trancheToken.file("name", tokenName);
        trancheToken.file("symbol", tokenSymbol);
        // @audit no event
    }


    function updateMember(uint64 poolId, bytes16 trancheId, address user, uint64 validUntil) public onlyGateway {
        TrancheTokenLike trancheToken = TrancheTokenLike(getTrancheToken(poolId, trancheId));
        require(address(trancheToken) != address(0), "PoolManager/unknown-token");


        MemberlistLike memberlist = MemberlistLike(address(trancheToken.restrictionManager()));
        memberlist.updateMember(user, validUntil);
        // @audit no event
    }

GitHub: 214

File: src/token/RestrictionManager.sol

    function updateMember(address user, uint256 validUntil) public auth {
        require(block.timestamp <= validUntil, "RestrictionManager/invalid-valid-until");
        members[user] = validUntil;
    }


    function updateMembers(address[] memory users, uint256 validUntil) public auth {
        uint256 userLength = users.length;
        for (uint256 i = 0; i < userLength; i++) {
            updateMember(users[i], validUntil);

GitHub: 57

[L‑03] delay can unintentionally be set to an indefinite value.

The lack of a check to ensure that the delay parameter is less than or equal to MAX_DELAY in the constructor of src/Root.sol can result in a delay that may not be reached in the near future.

File: src/Root.sol

34:    constructor(address _escrow, uint256 _delay) {
35:        escrow = _escrow; 
36:        delay = _delay; // @audit not checking _delay <= MAX_DELAY

GitHub: 34

Recommendation

use require(data <= MAX_DELAY, "Root/delay-too-long"); in the constructor before setting the delay.

Non-critical Issues

[N‑01] Remove commented code

"Please remove the unnecessary commented-out code from src/LiquidityPool.sol."

There are 1 instances of this issue:

File: src/LiquidityPool.sol

150:   // require(receiver == msg.sender, LiquidityPool/not-authorized-to-mint");

GitHub: 150

[N‑02] require statement is not needed in src/PoolManager.sol#deployLiquidityPool

File: src/PoolManager.sol

310: require(isAllowedAsPoolCurrency(poolId, currency), "PoolManager/currency-not-supported"); // Currency must be supported by pool

https://github.com/code-423n4/2023-09-centrifuge/blob/512e7a71ebd9ae76384f837204216f26380c9f91/src/PoolManager.sol#L310

The above require statement will never evaluate to false because isAllowedAsPoolCurrency() always returns true, and it reverts otherwise. Therefore, the require statement can be replaced with a direct call to isAllowedAsPoolCurrency().

     function deployLiquidityPool(uint64 poolId, bytes16 trancheId, address currency) public returns (address) {
         Tranche storage tranche = pools[poolId].tranches[trancheId];
         require(tranche.token != address(0), "PoolManager/tranche-does-not-exist"); // Tranche must have been added
-        require(isAllowedAsPoolCurrency(poolId, currency), "PoolManager/currency-not-supported"); // Currency must be supported by pool
+        isAllowedAsPoolCurrency(poolId, currency); // Currency must be supported by pool 

[N‑03] Adding events within the incoming and outgoing functions in src/gateway/routers/axelar/Router.sol enhances transparency

Adding events within the incoming and outgoing functions of src/gateway/routers/axelar/Router.sol enhances transparency for external parties and provides more comprehensive logging.

Please add events to the individual functions in the code below.

File: src/gateway/routers/axelar/Router.sol

    // --- Incoming ---
    function execute(
        bytes32 commandId,
        string calldata sourceChain,
        string calldata sourceAddress,
        bytes calldata payload
    ) public onlyCentrifugeChainOrigin(sourceChain, sourceAddress) {
        bytes32 payloadHash = keccak256(payload);
        require(
            axelarGateway.validateContractCall(commandId, sourceChain, sourceAddress, payloadHash),
            "Router/not-approved-by-gateway"
        );


        gateway.handle(payload);
    }


    // --- Outgoing ---
    function send(bytes calldata message) public onlyGateway {
        axelarGateway.callContract(axelarCentrifugeChainId, centrifugeGatewayPrecompileAddress, message);
    }

https://github.com/code-423n4/2023-09-centrifuge/blob/512e7a71ebd9ae76384f837204216f26380c9f91/src/gateway/routers/axelar/Router.sol#L72C1-L91C6

#0 - c4-pre-sort

2023-09-17T01:38:05Z

raymondfam marked the issue as sufficient quality report

#1 - c4-judge

2023-09-26T17:39:55Z

gzeon-c4 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