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
Rank: 78/84
Findings: 1
Award: $12.79
š Selected for report: 0
š Solo Findings: 0
š Selected for report: castle_chain
Also found by: 0xAadi, 0xHelium, 0xLook, 0xblackskull, 0xfuje, 0xmystery, 0xnev, 0xpiken, 7ashraf, BARW, Bauchibred, Bughunter101, Ch_301, JP_Courses, Kaysoft, Krace, MohammedRizwan, SanketKogekar, Sathish9098, alexzoid, ast3ros, btk, catellatech, degensec, fatherOfBlocks, grearlake, imtybik, jkoppel, jolah1, klau5, lsaudit, m_Rassska, merlin, mrudenko, nobody2018, rokinot, rvierdiiev, sandy
12.7917 USDC - $12.79
Issue | Instances | |
---|---|---|
[Lā01] | Ward/Authorized admin can't able to execute functions in LiquidityPool.sol | 6 |
[Lā02] | Missing Event for critical parameters change | 2 |
[Lā03] | delay can unintentionally be set to an indefinite value | 1 |
Issue | Instances | |
---|---|---|
[Nā01] | Remove commented code | 1 |
[Nā02] | require statement is not needed in src/PoolManager.sol#deployLiquidityPool | 1 |
[Nā03] | Adding events within the incoming and outgoing functions enhances transparency | 1 |
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.
/// @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
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
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
use require(data <= MAX_DELAY, "Root/delay-too-long");
in the constructor before setting the delay.
"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
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
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
src/gateway/routers/axelar/Router.sol
enhances transparencyAdding 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); }
#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