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: 59/84
Findings: 2
Award: $47.48
🌟 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
1, There might not enough currency token for Centrifuge chain for success call handleExecutedCollectRedeem function When Centrifuge chain send message to pool, Gateway#handle will handle message:
} else if (Messages.isExecutedCollectRedeem(message)) { ( uint64 poolId, bytes16 trancheId, address investor, uint128 currency, uint128 currencyPayout, uint128 trancheTokensPayout ) = Messages.parseExecutedCollectRedeem(message); investmentManager.handleExecutedCollectRedeem( poolId, trancheId, investor, currency, currencyPayout, trancheTokensPayout );
In InvestmentManager#handleExecutedCollectRedeem, it will try to transfer currency from escrow to recipient that request in previous escrow:
userEscrow.transferIn( _currency, address(escrow), recipient, currencyPayout );
But problem is there is no guardian that escrow will have enough, since ward in contract is able to taken out by ward, and ward can be set to any address by root because escrow is rely on root, as it is described in Deployer.sol:
Escrow(address(escrow)).rely(address(root));
Moreover, there even is a chance that all currency that inside escrow can be stolen because of malicious ward in escrow that set by root.
To mitigration, escrow should be independent, not able to be fully controlled by any address for safe, and make sure that escrow have enough tokens to transfer for user when epoch end
2, Should not use draft proposal RestrictionManager.sol use ERC1404, which is draft proposal (https://github.com/ethereum/eips/issues/1404), which is not recognized. Instead, can use others like (https://eips.ethereum.org/EIPS/eip-1450)
3, Ward in InvestmentManager can mint unlimited tranche token ERC20#mint() is used to mint token to address:
function mint(address to, uint256 value) public virtual auth { require( to != address(0) && to != address(this), "ERC20/invalid-address" ); unchecked { balanceOf[to] = balanceOf[to] + value; // note: we don't need an overflow check here b/c balanceOf[to] <= totalSupply and there is an overflow check below } totalSupply = totalSupply + value; emit Transfer(address(0), to, value); }
If any person that have ward that set by root in Root#relyContract(), they can call this function to mint unlimited tranche token to any address. To mitigrate this, make sure address called to this function is only contract address that will call to this function
5, Tranche token name only can up to 128 character, tranche token symbol only can up to 32 character When update tranche token metadata, Messages#formatUpdateTrancheTokenMetadata is called:
function formatUpdateTrancheTokenMetadata( uint64 poolId, bytes16 trancheId, string memory tokenName, string memory tokenSymbol ) internal pure returns (bytes memory) { // TODO(nuno): Now, we encode `tokenName` as a 128-bytearray by first encoding `tokenName` // to bytes32 and then we encode three empty bytes32's, which sum up to a total of 128 bytes. // Add support to actually encode `tokenName` fully as a 128 bytes string. return abi.encodePacked( uint8(Call.UpdateTrancheTokenMetadata), poolId, trancheId, _stringToBytes128(tokenName), _stringToBytes32(tokenSymbol) ); }
_stringToBytes128(tokenName)
mean only 128 characters is saved, if more than 128 character, the rest will be ignored, same with _stringToBytes32(tokenSymbol)
6, Strict checking condition in UserEscrow#transferOut In UserEscrow#transferOut(), there is a require check that explained by user:
/// @dev transferOut can only be initiated by the destination address or an authorized admin. /// The check is just an additional protection to secure destination funds in case of compromized auth. /// Since userEscrow is not able to decrease the allowance for the receiver, /// a transfer is only possible in case receiver has received the full allowance from destination address.
But this checking condition could also make user hard to directly transfer redeem to other user. To directly transfer, they have approve from outside of contract, which increase risk of losing token by approve() full balance to other address
#0 - c4-pre-sort
2023-09-17T01:26:35Z
raymondfam marked the issue as sufficient quality report
#1 - c4-judge
2023-09-26T17:45:28Z
gzeon-c4 marked the issue as grade-b
🌟 Selected for report: ciphermarco
Also found by: 0x3b, 0xbrett8571, 0xmystery, 0xnev, K42, Kral01, Sathish9098, castle_chain, catellatech, cats, emerald7017, fouzantanveer, foxb868, grearlake, hals, jaraxxus, kaveyjoe, lsaudit, rokinot
34.6879 USDC - $34.69
Centrifuge is a transparent market project on Ethereum, where allows borrowers and lenders to transact without unnecessary intermediaries. In liquidity pool, user can invest stablecoins and receive back shares. These shares can be be used to redeem to get stablecoins back to gain profit with diffirent rates at risks based on tranch
Since Wards can taken any token value out in Escrow anytime, functions that need to transfer token from escrow can be failed, which lead to break contract
Wards can call token/ERC20#mint() to mint unlimited tranche token to any address
Currently, contract does not support types of token like fee-on-transfer (USDT, USDC, ...), rebasing token, ... Contract should have improvement to support these types of tokens to have more choice for user to invest
Day 1: Have a overview about in-scope contracts Day 2: Understand workflow of contracts Day 3 + 4: Deep dive in 3 main contracts: LiquidityPool, PoolManager, InvestmentManager Day 5: Checking the rest of contracts, Finishing Analysis
45 hours
#0 - c4-pre-sort
2023-09-17T02:07:18Z
raymondfam marked the issue as sufficient quality report
#1 - c4-judge
2023-09-26T17:16:38Z
gzeon-c4 marked the issue as grade-b