Platform: Code4rena
Start Date: 01/07/2022
Pot Size: $75,000 USDC
Total HM: 17
Participants: 105
Period: 7 days
Judge: Jack the Pug
Total Solo HM: 5
Id: 143
League: ETH
Rank: 67/105
Findings: 2
Award: $92.68
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: horsefacts
Also found by: 0x1f8b, 0x29A, 0x52, 0xf15ers, AlleyCat, Ch_301, Chom, Franfran, IllIllI, Kaiziron, Limbooo, Meera, Ruhum, Sm4rty, apostle0x01, berndartmueller, cccz, cloudjunky, codexploder, cryptphi, delfin454000, durianSausage, fatherOfBlocks, hake, hansfriese, hyh, jonatascm, m_Rassska, oyc_109, peritoflores, rajatbeladiya, rbserver, svskaushik, zzzitron
3.4075 USDC - $3.41
JBPayoutRedemptionPaymentTerminal
is an abstract contract that places the onus of handling token transfer logic on it's child contracts. One such child contract is JBERC20PaymentTerminal
.
However, JBERC20PaymentTerminal
implements _transferFrom
logic in such a manner that it ignores return value by IERC20 tokens. Lack of return value check is error-prone and can lead to unexpected results for projects that receive funding denominated in certain types of ERC20 tokens.
The code in referred to is below:
function _transferFrom( address _from, address payable _to, uint256 _amount ) internal override { _from == address(this) ? IERC20(token).transfer(_to, _amount) : IERC20(token).transferFrom(_from, _to, _amount); }
This _transferFrom
implementation does not check for return values and hence can lead to errors when used with tokens that don't revert on transfer but just return false. ZRX and BAT are examples.
If such a token is used, it would be possible to successfully call functions like pay
and addToBalanceOf
even when the token returns false
. This would allow an attacker to manipulate funds even with insufficient balances.
Using SafeERC20
's safeTransferFrom
or implementing manual logic to check return value in JBPayoutRedemptionPaymentTerminal
's child contracts are possible solutions.
#0 - drgorillamd
2022-07-12T15:38:00Z
Duplicate of #281
#1 - jack-the-pug
2022-07-24T02:04:10Z
Duplicate of #242
🌟 Selected for report: IllIllI
Also found by: 0v3rf10w, 0x1f8b, 0x29A, 0xDjango, 0xNazgul, 0xNineDec, 0xdanial, 0xf15ers, Bnke0x0, Ch_301, Chandr, Chom, Funen, GimelSec, Hawkeye, JC, Kaiziron, Lambda, Meera, MiloTruck, Noah3o6, Picodes, ReyAdmirado, Rohan16, Sm4rty, TerrierLover, TomJ, Waze, _Adam, __141345__, asutorufos, aysha, berndartmueller, brgltd, cccz, codexploder, defsec, delfin454000, djxploit, durianSausage, fatherOfBlocks, hake, horsefacts, hubble, jayfromthe13th, joestakey, jonatascm, m_Rassska, oyc_109, pashov, rajatbeladiya, rbserver, robee, sach1r0, sahar, samruna, simon135, svskaushik, zzzitron
89.271 USDC - $89.27
There is a variety of styles and practices adhered to in different patches throughout the code. Such variations can affect code readability and perceived code quality. For instance, the implementation of FOR
loops is done in the following ways:
for (uint256 _i = 0; _i < _splits.length; _i++)
uint
is initialized with 0
value
for (uint256 _i; _i < _fundAccessConstraints.length; _i++)
uint
is not initialized with a value, default value is used
JBPayoutRedemptionPaymentTerminal.sol:L591-594
// Push array length in stack uint256 _heldFeeLength = _heldFees.length; // Process each fee. for (uint256 _i = 0; _i < _heldFeeLength; )
Array length is cached
for (uint256 _i = 0; _i < _splits.length; _i++)
Array length is not cached.
JBPayoutRedemptionPaymentTerminal.sol:L591-594
// Push array length in stack uint256 _heldFeeLength = _heldFees.length; // Process each fee. for (uint256 _i = 0; _i < _heldFeeLength; )
++i
and unchecked{}
is utilized later in L607:
unchecked { ++_i; }
for (uint256 _i = 0; _i < _splits.length; _i++)
No utilization of ++i
and unchecked{}