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: 35/84
Findings: 1
Award: $132.86
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: Kow
Also found by: 0xRobsol, 0xfuje, 0xkazim, 0xpiken, Aymen0909, T1MOH, bin2chen, codegpt, gumgumzum, josephdara, lsaudit, nmirchev8, ravikiranweb3, rvierdiiev
132.8565 USDC - $132.86
https://github.com/code-423n4/2023-09-centrifuge/blob/512e7a71ebd9ae76384f837204216f26380c9f91/src/token/Tranche.sol#L42-L46 https://github.com/code-423n4/2023-09-centrifuge/blob/512e7a71ebd9ae76384f837204216f26380c9f91/src/util/Factory.sol#L98-L99
The newTrancheToken
function is intended for deploying new tranche tokens for a pool. However, this function calls the trancheToken.sol#file()
function to set the name, symbol, and restriction manager for the tranche token. The problem arises because the file()
function is designed to revert transactions if the string passed to it is not
The Factory#newTrancheToken
function invokes the file function to set the name and symbol of the tranche token by providing them as strings:
function newTrancheToken( uint64 poolId, bytes16 trancheId, string memory name, string memory symbol, uint8 decimals, address[] calldata trancheTokenWards, address[] calldata restrictionManagerWards ) public auth returns (address) { address restrictionManager = _newRestrictionManager( restrictionManagerWards ); // Salt is hash(poolId + trancheId) // same tranche token address on every evm chain bytes32 salt = keccak256(abi.encodePacked(poolId, trancheId)); TrancheToken token = new TrancheToken{salt: salt}(decimals); //@audit here we revert as the what is not restrictionManager ! token.file("name", name); token.file("symbol", symbol); token.file("restrictionManager", restrictionManager); //audit there is no function called relay in tranche token contract token.rely(root); for (uint256 i = 0; i < trancheTokenWards.length; i++) { token.rely(trancheTokenWards[i]); } token.deny(address(this)); return address(token); }
the file function implemented as follow:
function file(bytes32 what, address data) public auth { if (what == "restrictionManager") restrictionManager = ERC1404Like(data); else revert("TrancheToken/file-unrecognized-param"); emit File(what, data); }
If the what
parameter is not "restrictionManager"
the transaction will revert. Since we pass the name and symbol as the what parameter, the call to the newTrancheToken
function will always result in reverting.
You can observe this behavior in the contract I created in Remix, as the function will revert when calling call with "name" and "symbol" strings.
pragma solidity ^0.8.21; contract test{ address private owner; event File(bytes32 , address); constructor() public { owner = msg.sender; } function file(bytes32 what, address data) public returns(bool) { if (what == "restrictionManager") { return true; } else revert("ok we revert"); emit File(what, data); } function call(address _data) public { this.file("string", _data); } }
manual review / remix
It is recommended to either implement the file function correctly or create a specific function that meets the requirements of the newTrancheToken function (for passing the name and symbol).
Other
#0 - c4-pre-sort
2023-09-16T01:05:36Z
raymondfam marked the issue as sufficient quality report
#1 - c4-pre-sort
2023-09-16T01:06:27Z
raymondfam marked the issue as duplicate of #146
#2 - c4-judge
2023-09-26T18:07:17Z
gzeon-c4 marked the issue as satisfactory