xanoutas/solidity-security-findings huggingface dataset:
The executeScheduledRely function in the code allows any external caller to execute it without proper access control checks. This means that anyone can make themselves a ward on a contract without authorization, which poses a security risk.
Audit: 2023-09-centrifuge-findings
Severity: MEDIUM

# Lines of code

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


# Vulnerability details

### Issue:
The `executeScheduledRely` function in the code allows any external caller to execute it without proper access control checks. This means that anyone can make themselves a ward on a contract without authorization, which poses a security risk.

### Proof of Concept:
Without proper access control, an attacker can call the `executeScheduledRely` function as follows:

```solidity
rootContract.executeScheduledRely(targetContract);
```

This allows the attacker to become a ward on the `targetContract` without authorization.

### Code Block Examples:

**Current Implementation:**
```solidity
function executeScheduledRely(address target) public {
    require(schedule[target] != 0, "Root/target-not-scheduled");
    require(schedule[target] < block.timestamp, "Root/target-not-ready");

    wards[target] = 1;
    emit Rely(target);

    schedule[target] = 0;
}
```

**Recommended Mitigation:**
```solidity
// Add a modifier to restrict access to authorized users (e.g., the contract owner).
modifier onlyAuthorized() {
    require(wards[msg.sender] == 1, "Root/not-authorized");
    _;
}

function executeScheduledRely(address target) public onlyAuthorized {
    require(schedule[target] != 0, "Root/target-not-scheduled");
    require(schedule[target] < block.timestamp, "Root/target-not-ready");

    wards[target] = 1;
    emit Rely(target);

    schedule[target] = 0;
}
```

**Explanation:**

1. We've introduced an `onlyAuthorized` modifier to restrict access to authorized users.
2. The `onlyAuthorized` modifier checks if the sender (caller) is authorized based on the `wards` mapping.
3. In the `executeScheduledRely` function, we've applied the `onlyAuthorized` modifier to ensure that only authorized users can execute it.
   


## Assessed type

Access Control
The maxMint check should be cumulatively applied to ensure it's effectiveness
Audit: 2023-09-centrifuge-findings
Severity: MEDIUM

# Lines of code

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


# Vulnerability details


## Impact

Circumvention of the maximum minting restriction, since all a receiver need to do is not specify the whole amount of tokens in one attempt and then claim more than the maximum amount

## Proof of Concept

Take a look at [LiquidityPool.sol#L145-L157](https://github.com/code-423n4/2023-09-centrifuge/blob/512e7a71ebd9ae76384f837204216f26380c9f91/src/LiquidityPool.sol#L145-L157)

```solidity
 /// @notice Collect shares for deposited assets after Centrifuge epoch execution.
    ///         maxMint is the max amount of shares that can be collected.
    function mint(uint256 shares, address receiver) public returns (uint256 assets) {
        assets = investmentManager.processMint(receiver, shares);
        emit Deposit(address(this), receiver, assets, shares);
    }

    /// @notice Maximum amount of shares that can be claimed by the receiver after the epoch has been executed on the Centrifuge side.
    function maxMint(address receiver) external view returns (uint256 maxShares) {
        maxShares = investmentManager.maxMint(receiver, address(this));
    }
```

As seen, the below is queried

```solidity
    /// @notice Processes user's currency deposit / investment after the epoch has been executed on Centrifuge.
    ///         In case user's invest order was fulfilled on Centrifuge during epoch execution MaxDeposit and MaxMint are increased
    ///         and trancheTokens can be transferred to user's wallet on calling processDeposit or processMint.
    ///         Note: The currency amount required to fulfil the invest order is already locked in escrow upon calling requestDeposit.
    ///         Note: The tranche tokens are already minted on collectInvest and are deposited to the escrow account until the users calls mint, or deposit.
    /// @dev    currencyAmount return value is type of uint256 to be compliant with EIP4626 LiquidityPool interface
    /// @return currencyAmount the amount of liquidityPool assets invested and locked in escrow in order
    ///         for the amount of tranche tokens received after successful investment into the pool.
    function processMint(address user, uint256 trancheTokenAmount) public auth returns (uint256 currencyAmount) {
        address liquidityPool = msg.sender;
        uint128 _trancheTokenAmount = _toUint128(trancheTokenAmount);
        require(
        //@audit
            (_trancheTokenAmount <= orderbook[user][liquidityPool].maxMint && _trancheTokenAmount != 0),
            "InvestmentManager/amount-exceeds-mint-limits"
        );

        uint256 depositPrice = calculateDepositPrice(user, liquidityPool);
        require(depositPrice != 0, "LiquidityPool/deposit-token-price-0");

        uint128 _currencyAmount = _calculateCurrencyAmount(_trancheTokenAmount, liquidityPool, depositPrice);
        _deposit(_trancheTokenAmo
The current axelar router's implementation can't interact with any calls that requires native tokens
Audit: 2023-09-centrifuge-findings
Severity: MEDIUM

# Lines of code

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


# Vulnerability details



## Impact

Medium... this completely blocks off users that would need to call a contract and want to pass value to them, as they can't do this since the functions are not payable

## Proof of Concept

See summary

Additionally take a look at the Axelar's `Router.sol` and see that none of the functions have `payable` attached to them, i.e even when sending a request to call a contract or executing an incoming call which completely blocks interactions/calls that require native tokens to/fro Axelar's gateway

<details>

<summary>Click to see full code reference</summary>

```solidity
interface AxelarGatewayLike {
    function callContract(string calldata destinationChain, string calldata contractAddress, bytes calldata payload)
        external;

    function validateContractCall(
        bytes32 commandId,
        string calldata sourceChain,
        string calldata sourceAddress,
        bytes32 payloadHash
    ) external returns (bool);
}

contract AxelarRouter is Auth {

    constructor(address axelarGateway_) {
        axelarGateway = AxelarGatewayLike(axelarGateway_);

        wards[msg.sender] = 1;
        emit Rely(msg.sender);
    }

    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);
    }

    }

```

</details>

## Tool used

Manual Review

## Recommended Mitigation Steps

Executions that have potential of being passed native tokens should be wrapped with a `payable`, if calls are to be made with value, this could be specified, otherwise, they can pass no value and executions still go as currently implemented.



## Assessed type

Payable
lack of an ownership transfer mechanism
Audit: 2023-09-centrifuge-findings
Severity: MEDIUM

# Lines of code

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


# Vulnerability details

### Issue:
The code does not have a mechanism to transfer ownership of the contract. In the current implementation, once deployed, the contract owner's address cannot be changed. This can be problematic for contract maintenance and security, as it restricts the ability to update or manage the contract.

### Code Block Examples:

**Current Implementation:**
```solidity
constructor(address _escrow, uint256 _delay) {
    escrow = _escrow;
    delay = _delay;

    wards[msg.sender] = 1;
    emit Rely(msg.sender);
}
```

**Recommended Mitigation:**
```solidity
address public owner; // Add an owner variable to track the contract owner.

constructor(address _escrow, uint256 _delay) {
    escrow = _escrow;
    delay = _delay;
    owner = msg.sender; // Set the initial contract owner.

    wards[msg.sender] = 1;
    emit Rely(msg.sender);
}

// Add a modifier to restrict access to only the contract owner.
modifier onlyOwner() {
    require(msg.sender == owner, "Root/not-owner");
    _;
}

// Add a function to transfer ownership to a different address.
function transferOwnership(address newOwner) external onlyOwner {
    owner = newOwner;
}
```

**Explanation:**

1. We've introduced an `owner` variable to track the contract owner's address.
2. In the constructor, we set the initial contract owner to the address that deploys the contract (`msg.sender`).
3. We've added a `onlyOwner` modifier to restrict access to functions only to the contract owner.
4. We've introduced a `transferOwnership` function that allows the current owner to transfer ownership to a different address.
   


## Assessed type

Token-Transfer
Allowing price updates once in an epoch is extremely risky and open windows to a lot of issues
Audit: 2023-09-centrifuge-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-09-centrifuge/blob/512e7a71ebd9ae76384f837204216f26380c9f91/src/interfaces/IERC4626.sol#L76-L90


# Vulnerability details


## Impact

Protocol currently knows about how this could be an effect, since the comments to both `previewDeposit()` and `convertToShares()` suggest that any difference attached to this should be considered slippage, but measures are not taken to ensure that this _slippage_ is not massive, say an abrupt crash like that of UST were to happen, this would immensely affect the protocol and even be way worse since prices are only updated per epoch and users can unknowingly/knowingly position themselves in a way to benefit the most from this

## Proof of Concept

See _Impact_

Do note that the developers are _somewhat_ aware of how this implementation is risky, since the docs clearly state how sharp price declines can come up for markets of securitized products

Additionally note that the prices are updated once an epoch (_duration of epoch has currently not being decided upon_), as has been stated here: https://docs.centrifuge.io/getting-started/securitization/#epoch-mechanism

Lastly, whatever duration is even later on set, this could still be an issue, it just depends on how volatile the underlying assets are.

## Tool used

Manual Review

## Recommended Mitigation Steps

A deviation threshold could be set and rewards attached to anyone that can raise an alert whenever an asset falls over the accepted deviation mark, if price movement does not pass this deviaition mark, then prices can then be updated after an epoch, this is similar to widely accepted chainlink price updating mechanism, where heartbeats exist and deviation thresholds, do note that the heartbeats and deviation threshold if going to be applied shouldn't be the same for all tranche tokens but it should be dependent on what type of assets are in the RWA pool, if very active/volatile then smaller durations should be set, if not, the otherwise should be practised



## Assessed type

Context
No gas payment is done before calling the `callContract` in the `Router.sol`
Audit: 2023-09-centrifuge-findings
Severity: MEDIUM

# Lines of code

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


# Vulnerability details

## Impact
In the Axelar documentation it is specified that before calling `callContract` or `callContractWithToken` the user needs to first prepay the relayers gas fee on the source chain, but since the functions are not payable, nor does call `payNativeGasForContractCall` before calling `callContract`, the fees would not be paid and messages will be blocked.

## Proof of Concept
Axelar documentation specifies in the flow architecture, that before calling any function on the axelar network gas need to be paid, as can be seen here https://docs.axelar.dev/dev/general-message-passing/overview , but nowhere the functions to pay the gas for the relayers are called, and also no function is payable, to be able to receive ether to pay the gas, so Axelar is not implemented as it should.

## Tools Used
Manual review
## Recommended Mitigation Steps
Make the functions `send` function payable https://github.com/code-423n4/2023-09-centrifuge/blob/512e7a71ebd9ae76384f837204216f26380c9f91/src/gateway/routers/axelar/Router.sol#L88-L91
and call the relayers contract before calling `callContract`, to pay the gas necessary.


## Assessed type

Other
Potential Incorrect Domain Separator Generation in _calculateDomainSeparator Function
Audit: 2023-09-centrifuge-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-09-centrifuge/blob/512e7a71ebd9ae76384f837204216f26380c9f91/src/token/ERC20.sol#L71


# Vulnerability details

**The _calculateDomainSeparator function generates the EIP-712 domain separator using the contract's name and version. However, there is no explicit guarantee that the name has been set before calling this function, leading to an unreliable domain separator.**

## Impact
The domain separator is a fundamental component in verifying the source and integrity of off-chain signed messages in the EIP-712 standard. If the domain separator is not consistently and correctly generated, it could result in:

Users signing messages that they didn't intend to, because the domain they thought they were interacting with is different.
A loss of trust in the protocol's message verification system.
Potential vulnerabilities where malicious actors could exploit the inconsistency to impersonate the contract or deceive users.

## Proof of Concept
Consider a situation where Alice wants to sign a message to prove her actions off-chain for this contract. Alice fetches the domain separator to sign her message.

If the name hasn't been set before she calls _calculateDomainSeparator, she might sign a message with a domain that doesn't represent the intended contract. This could be problematic in scenarios where Bob, another user, uses the signed message to interact with the contract.

Without a correct domain separator, Alice's intent is misrepresented. Anyone using her signature might end up making decisions based on incorrect information.

## Tools Used

## Recommended Mitigation Steps
Add a modifier or check to ensure that name is set before the _calculateDomainSeparator function can be called.


## Assessed type

Access Control
wrong math in the `processDeposit` function lead to send zero TT to users or send less than their deserved TT
Audit: 2023-09-centrifuge-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-09-centrifuge/blob/512e7a71ebd9ae76384f837204216f26380c9f91/src/InvestmentManager.sol#L435-L438
https://github.com/code-423n4/2023-09-centrifuge/blob/512e7a71ebd9ae76384f837204216f26380c9f91/src/InvestmentManager.sol#L557
https://github.com/code-423n4/2023-09-centrifuge/blob/512e7a71ebd9ae76384f837204216f26380c9f91/src/InvestmentManager.sol#L569-L582
https://github.com/code-423n4/2023-09-centrifuge/blob/512e7a71ebd9ae76384f837204216f26380c9f91/src/InvestmentManager.sol#L591-L603
https://github.com/code-423n4/2023-09-centrifuge/blob/512e7a71ebd9ae76384f837204216f26380c9f91/src/InvestmentManager.sol#L676-L683
https://github.com/code-423n4/2023-09-centrifuge/blob/512e7a71ebd9ae76384f837204216f26380c9f91/src/InvestmentManager.sol#L691-L692


# Vulnerability details

## Impact

in the `processDeposit` function we calculate the deposit price then we use this price to calculate the tranche token amount that we should send to the caller/user, however the calculation is not done correctly as it results in calculating less amount TT for huge amount of currency to send to the caller. more details in poc

## Proof of Concept

in the `processDeposit` function we calculate deposit price and TT tokens amount as follow :

```solidity
 function processDeposit(
        address user,
        uint256 currencyAmount
    ) public auth returns (uint256 trancheTokenAmount) {
        //get the liquidity pool
        address liquidityPool = msg.sender;
        // the asset amount usdc
        uint128 _currencyAmount = _toUint128(currencyAmount);
        require(
            (_currencyAmount <= orderbook[user][liquidityPool].maxDeposit &&
                _currencyAmount != 0),
            "InvestmentManager/amount-exceeds-deposit-limits"
        );
        //get the depositPrice 1.25e21
        uint256 depositPrice = calculateDepositPrice(user, liquidityPool);
        require(depositPrice != 0, "LiquidityPool/deposit-token-price-0");
        // calculate how much tranch token we get
        //@audit wrong math 10000
        uint128 _trancheTokenAmount = _calculateTrancheTokenAmount(
            _currencyAmount,
            liquidityPool,
            depositPrice
        );
        _deposit(_trancheTokenAmount, _currencyAmount, liquidityPool, user);
        trancheTokenAmount = uint256(_trancheTokenAmount);
    }
```

lets first calculate the deposit price as the code implemented:
note// max deposit is 100 and max mint is 80

```solidity
function calculateDepositPrice(
        address user,
        address liquidityPool
    ) public view returns (uint256 depositPrice) {
        LPValues storage lpValues = orderbook[user][liquidityPool]; // get the user data in specific LP
        if (lpValues.maxMint == 0) {
            return 0;
        }
        // get the deposit price
        depositPrice = _calculatePrice(
            lpValues.maxDeposit, // max usdc that user deposit
            lpValues.maxMint, // max tranche token
Lack of Remapping/Remove Feature for Compromised Token Address
Audit: 2023-09-centrifuge-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-09-centrifuge/blob/main/src/PoolManager.sol#L235-L255


# Vulnerability details

## Impact
No way to update/remap compromised token addresses linked to L1 Centrifuge currencyId.

## Proof of Concept
Currently there is no way to change/remove the link between the L1 Centrifuge currencyId and the EVM one, in case the token contract gets hacked or anything happens to the official address there is no way to remap/remove the compromised address.

## Tools Used
manual

## Recommended Mitigation Steps
Implement option to remap/remove currency addresses.



## Assessed type

Other
decreaseDepositRequest may decrease more than the intended sum
Audit: 2023-09-centrifuge-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-09-centrifuge/blob/512e7a71ebd9ae76384f837204216f26380c9f91/src/InvestmentManager.sol#L174
https://github.com/code-423n4/2023-09-centrifuge/blob/512e7a71ebd9ae76384f837204216f26380c9f91/src/LiquidityPool.sol#L214-L217


# Vulnerability details


## Proof of Concept

When a user calls requestDeposit, the investmentManager contract will help the user log in a deposit request and will take the deposit amount from the user and deposit into the escrow contract. There are a few possible transaction that can happen next. Firstly, the deposit is cleared and the protocol converts the deposit amount into tranche tokens ready for collection. Secondly, the user decides to withdraw some of the deposit before the gateway clears the deposit. Thirdly, the user decides to cancel the deposit. In the second and third scenario, the user can freely specify how much deposit they want to withdraw. There is no check that the withdraw amount is greater than the deposit amount.

In the requestDeposit function, the deposit amount is moved to the escrow and increaseInvestOrder is called on the gateway, passing in the _currencyAmount. 

```
    function requestDeposit(uint256 currencyAmount, address user) public auth {
        address liquidityPool = msg.sender;
        SafeTransferLib.safeTransferFrom(currency, user, address(escrow), _currencyAmount);
        gateway.increaseInvestOrder(
            lPool.poolId(), lPool.trancheId(), user, poolManager.currencyAddressToId(lPool.asset()), _currencyAmount
        );
```

When requesting a decrease in deposit, the user can specify any amount they want and the function decreaseInvestOrder is invoked on the gateway with the currencyAmount. 

```
    function decreaseDepositRequest(uint256 _currencyAmount, address user) public auth {
        uint128 currencyAmount = _toUint128(_currencyAmount);
        LiquidityPoolLike liquidityPool = LiquidityPoolLike(msg.sender);
        require(liquidityPool.checkTransferRestriction(address(0), user, 0), "InvestmentManager/not-a-member");
        gateway.decreaseInvestOrder(
            liquidityPool.poolId(),
            liquidityPool.trancheId(),
            user,
            poolManager.currencyAddressToId(liquidityPool.asset()),
            currencyAmount
        );
    }
```

There is no logging of deposited amounts anywhere in the process, except until the deposit is cleared after calling handleExecutedCollectInvest. A user can deposit 100 USDC and requestWithdraw of 10000 USDC. If the gateway does not notice this discrepency, the user can walk away with a large sum of money at the expense of other people's deposits.

## Impact

User can potentially withdraw more than they deposit

## Tools Used

Manual Review

## Recommended Mitigation Steps

The moment requestDeposit is called, have a mapping to the user which states how much is being deposited. Whenever the user cancels the request or request to decrease deposit, update the mapping a
DOS for Crosschain transfers
Audit: 2023-09-centrifuge-findings
Severity: MEDIUM

# Lines of code

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


# Vulnerability details

## Impact
The Crosschain transfers are done via the PoolManager using ``transferTrancheTokensToCentrifuge`` and ``transferTrancheTokensToEVM``
The user inputs a set of data including the destination address 
```solidity
    function transferTrancheTokensToEVM(
        uint64 poolId,
        bytes16 trancheId,
        uint64 destinationChainId,
        address destinationAddress,
        uint128 amount
    ) public {
```
The destination address is never verified before the gateway call is made, hence a user can send tokens to another chain with a destination address of address(0), this would prevent the transfer of the tranche tokens on whatever chain it has been bridged to since transfers to address zero is blocked 

## Proof of Concept
Manual Review

## Tools Used
```solidity
    function transferTrancheTokensToEVM(
        uint64 poolId,
        bytes16 trancheId,
        uint64 destinationChainId,
        address destinationAddress,
        uint128 amount
    ) public {
        TrancheTokenLike trancheToken = TrancheTokenLike(getTrancheToken(poolId, trancheId));
        require(address(trancheToken) != address(0), "PoolManager/unknown-token");

        trancheToken.burn(msg.sender, amount);
        gateway.transferTrancheTokensToEVM(
            poolId, trancheId, msg.sender, destinationChainId, destinationAddress, amount
        );
    }
```
This would cause incoming calls from the gateway to revert and tokens would never be transferred
## Recommended Mitigation Steps
Make sure the destination address is never address zero


## Assessed type

ERC20