Few vulnerabilities were found examining the contracts. The main concerns are with the unsafe use of IERC20 methods.
Function reverting for some tokens
PROBLEM
approve() reverts if called on a non-zero allowance without approving 0 first for some tokens (e.g: USDT). This means the logic in JBERC20PaymentTerminal will not work for these tokens
SEVERITY
Non-Critical
PROOF OF CONCEPT
Instances include:
JBERC20PaymentTerminal.sol
99: IERC20(token).approve(_to, _amount);
TOOLS USED
Manual Analysis
MITIGATION
Consider adding an extra step to approve for _amount = 0 before approving the new _amount
Immutable addresses lack zero-address check
IMPACT
constructors should check the address written in an immutable address variable is not the zero address
Some popular tokens do not implement the ERC20 standard properly. For example Tether 's transfer() and transferFrom() functions do not return booleans as the specification requires: when these tokens are cast to IERC20, their function signatures do not match and the function calls revert. This means these tokens cannot be used by the protocol's payment terminal.
Some ERC20 tokens do not revert upon failure in transfer()/transferFrom(), but simply return false. The function _transferFrom() in JBERC20PaymentTerminal.sol does not check the return value of IERC20.transfer and IERC20.transferFrom, meaning some token transfers may fail without getting noticed.
Not all IERC20 implementations revert when there's a failure in approve(). The function signature has a boolean return value and they indicate errors that way instead. When calling that function, it is hence recommended to check the return value.
Consider also this issue that can happen with the .approve() method.
Scientific notation
PROBLEM
For readability, it is best to use scientific notation (e.g 10e5) rather than decimal literals(100_000) or exponentiation(10**5). Underscores are used throughout the contracts and do improve readability too, so this is more of a suggestion.
It is good practice to add timelock to critical parameters changes, such as admin changes, to give users time to react. This is especially true for fee changes that directly affect protocol users. Merely a suggestion here as there is already a maximum value to which fee can be set, which can be deemed sufficient for users.
SEVERITY
Non-Critical
PROOF OF CONCEPT
Instances include:
JBPayoutRedemptionPaymentTerminal.sol
622: function setFee(uint256 _fee) external virtual override onlyOwner {
623: // The provided fee must be within the max.
624: if (_fee > _FEE_CAP) revert FEE_TOO_HIGH();
625:
626: // Store the new fee.
627: fee = _fee;
TOOLS USED
Manual Analysis
MITIGATION
Add a timelock to setFee
Transfer of ownership can be two-steps
PROBLEM
Consider using a 2-step ownership transfer for JBToken, so that the new owner must approve the ownership transfer, preventing issues if ownership was to be transferred to a random address.
QA Report
Table of Contents
transfer
not checkedsummary
Function reverting for some tokens
PROBLEM
approve()
reverts if called on a non-zero allowance without approving 0 first for some tokens (e.g: USDT). This means the logic inJBERC20PaymentTerminal
will not work for these tokensSEVERITY
Non-Critical
PROOF OF CONCEPT
Instances include:
JBERC20PaymentTerminal.sol
TOOLS USED
Manual Analysis
MITIGATION
Consider adding an extra step to approve for
_amount = 0
before approving the new_amount
Immutable addresses lack zero-address check
IMPACT
constructors should check the address written in an immutable address variable is not the zero address
SEVERITY
Low
PROOF OF CONCEPT
Instances include:
JBTokenStore.sol
JBSplitsStore.sol
JBDirectory.sol
JBController.sol
JBPayoutRedemptionPaymentTerminal.sol
JBSingleTokenPaymentTerminalStore.sol
TOOLS USED
Manual Analysis
Potential revert on transfer with IERC20
PROBLEM
Some popular tokens do not implement the ERC20 standard properly. For example Tether 's
transfer()
andtransferFrom()
functions do not return booleans as the specification requires: when these tokens are cast toIERC20
, their function signatures do not match and the function calls revert. This means these tokens cannot be used by the protocol's payment terminal.SEVERITY
Low
PROOF OF CONCEPT
JBERC20PaymentTerminal.sol
TOOLS USED
Manual Analysis
MITIGATION
Use OpenZeppelin’s SafeERC20's
safeTransfer()
Return value of
transfer
not checkedPROBLEM
Some ERC20 tokens do not revert upon failure in
transfer()
/transferFrom()
, but simply returnfalse
. The function_transferFrom()
inJBERC20PaymentTerminal.sol
does not check the return value ofIERC20.transfer
andIERC20.transferFrom
, meaning some token transfers may fail without getting noticed.SEVERITY
Low
PROOF OF CONCEPT
JBERC20PaymentTerminal.sol
TOOLS USED
Manual Analysis
MITIGATION
Check the return values of these function calls.
Comment Missing function parameter
PROBLEM
Some of the function comments are missing Natspec function parameters or returns
SEVERITY
Non-Critical
PROOF OF CONCEPT
Missing natspec comments include:
JBController.sol
TOOLS USED
Manual Analysis
MITIGATION
Add a Natspec comment for this parameter
Return statements
PROBLEM
Adding a return statement when the function defines a named return variable is redundant
SEVERITY
Non-Critical
PROOF OF CONCEPT
JBController.sol
TOOLS USED
Manual Analysis
MITIGATION
Return value of approve not checked
PROBLEM
Not all IERC20 implementations revert when there's a failure in
approve()
. The function signature has a boolean return value and they indicate errors that way instead. When calling that function, it is hence recommended to check the return value.SEVERITY
Non-Critical
PROOF OF CONCEPT
Instances include:
JBERC20PaymentTerminal.sol
TOOLS USED
Manual Analysis
MITIGATION
Consider also this issue that can happen with the
.approve()
method.Scientific notation
PROBLEM
For readability, it is best to use scientific notation (e.g
10e5
) rather than decimal literals(100_000
) or exponentiation(10**5
). Underscores are used throughout the contracts and do improve readability too, so this is more of a suggestion.SEVERITY
Non-Critical
PROOF OF CONCEPT
Instances include:
JBPayoutRedemptionPaymentTerminal.sol
TOOLS USED
Manual Analysis
Timelock for critical parameter change
PROBLEM
It is good practice to add timelock to critical parameters changes, such as admin changes, to give users time to react. This is especially true for fee changes that directly affect protocol users. Merely a suggestion here as there is already a maximum value to which
fee
can be set, which can be deemed sufficient for users.SEVERITY
Non-Critical
PROOF OF CONCEPT
Instances include:
JBPayoutRedemptionPaymentTerminal.sol
TOOLS USED
Manual Analysis
MITIGATION
Add a timelock to
setFee
Transfer of ownership can be two-steps
PROBLEM
Consider using a 2-step ownership transfer for
JBToken
, so that the new owner must approve the ownership transfer, preventing issues if ownership was to be transferred to a random address.SEVERITY
Non-Critical
PROOF OF CONCEPT
Instances include:
JBTokenStore.sol
TOOLS USED
Manual Analysis