Issue | Instances | |
---|---|---|
GAS-1 | Using bools for storage incurs overhead | 1 |
GAS-2 | For Operations that will not overflow, you could use unchecked | 64 |
GAS-3 | Use Custom Errors | 12 |
GAS-4 | Don't initialize variables with default value | 2 |
GAS-5 | Functions guaranteed to revert when called by normal users can be marked payable |
8 |
GAS-6 | ++i costs less gas than i++ , especially when it's used in for -loops (--i /i-- too) |
2 |
GAS-7 | Use != 0 instead of > 0 for unsigned integer comparison | 6 |
Use uint256(1) and uint256(2) for true/false to avoid a Gwarmaccess (100 gas), and to avoid Gsset (20000 gas) when changing from ‘false’ to ‘true’, after having been ‘true’ in the past. See source.
Instances (1):
File: PaycrestSettingManager.sol
23: mapping(address => bool) internal _isTokenSupported;
Instances (64):
File: Paycrest.sol
4: import "@openzeppelin/contracts-upgradeable/utils/cryptography/ECDSAUpgradeable.sol";
4: import "@openzeppelin/contracts-upgradeable/utils/cryptography/ECDSAUpgradeable.sol";
4: import "@openzeppelin/contracts-upgradeable/utils/cryptography/ECDSAUpgradeable.sol";
4: import "@openzeppelin/contracts-upgradeable/utils/cryptography/ECDSAUpgradeable.sol";
4: import "@openzeppelin/contracts-upgradeable/utils/cryptography/ECDSAUpgradeable.sol";
5: import "@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol";
5: import "@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol";
5: import "@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol";
5: import "@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol";
6: import "@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol";
6: import "@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol";
6: import "@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol";
6: import "@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol";
6: import "@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol";
6: import "@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol";
8: import {PaycrestSettingManager} from "./PaycrestSettingManager.sol";
9: import {IPaycrest, IERC20} from "./interfaces/IPaycrest.sol";
9: import {IPaycrest, IERC20} from "./interfaces/IPaycrest.sol";
10: import {SharedStructs} from "./libraries/SharedStructs.sol";
10: import {SharedStructs} from "./libraries/SharedStructs.sol";
53: ################################################################## */
53: ################################################################## */
70: ################################################################## */
70: ################################################################## */
90: IERC20(_token).transferFrom(msg.sender, address(this), _amount + _senderFee);
93: _nonce[msg.sender] ++;
93: _nonce[msg.sender] ++;
100: uint256 _protocolFee = (_amount * protocolFeePercent) / MAX_BPS;
100: uint256 _protocolFee = (_amount * protocolFeePercent) / MAX_BPS;
111: amount: _amount - _protocolFee
140: ################################################################## */
140: ################################################################## */
156: order[_orderId].currentBPS -= _settlePercent;
200: uint256 refundAmount = order[_orderId].amount - _fee;
216: ################################################################## */
216: ################################################################## */
241: i++;
241: i++;
File: PaycrestSettingManager.sol
9: import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
9: import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
9: import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
9: import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
11: import {SharedStructs} from "./libraries/SharedStructs.sol";
11: import {SharedStructs} from "./libraries/SharedStructs.sol";
36: ################################################################## */
36: ################################################################## */
69: i++;
69: i++;
File: interfaces/IPaycrest.sol
4: import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
4: import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
4: import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
4: import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
6: import {SharedStructs} from "../libraries/SharedStructs.sol";
6: import {SharedStructs} from "../libraries/SharedStructs.sol";
16: ################################################################## */
16: ################################################################## */
56: ################################################################## */
56: ################################################################## */
101: ################################################################## */
101: ################################################################## */
File: mocks/MockUSDC.sol
4: import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
4: import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
4: import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
4: import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
Source Instead of using error strings, to reduce deployment and runtime cost, you should use Custom Errors. This would save both deployment and runtime cost.
Instances (12):
File: Paycrest.sol
47: require(msg.sender == _aggregatorAddress, "OnlyAggregator");
87: require(bytes(messageHash).length > 0, "InvalidMessageHash");
128: require(_isTokenSupported[_token], "TokenNotSupported");
129: require(_amount > 0, "AmountIsZero");
130: require(_refundAddress != address(0), "ThrowZeroAddress");
131: require(supportedInstitutionsByCode[_institutionCode].name != bytes32(0), "InvalidInstitutionCode");
134: require(_senderFeeRecipient != address(0), "InvalidSenderFeeRecipient");
150: require(!order[_orderId].isFulfilled, "OrderFulfilled");
197: require(!order[_orderId].isFulfilled, "OrderFulfilled");
File: PaycrestSettingManager.sol
47: require(value != address(0), "Paycrest: zero address");
91: require(value != address(0), "Paycrest: zero address");
File: mocks/MockUSDC.sol
22: require(_balanceOf > 0, "MockUSDT: Nothing to burn");
Instances (2):
File: Paycrest.sol
238: for (uint256 i = 0; i < length; ) {
File: PaycrestSettingManager.sol
63: for (uint i = 0; i < length; ) {
If a function modifier such as onlyOwner
is used, the function will revert if a normal user tries to pay the function. Marking the function as payable
will lower the gas cost for legitimate callers because the compiler will not include checks for whether a payment was provided.
Instances (8):
File: Paycrest.sol
57: function pause() external onlyOwner {
64: function unpause() external onlyOwner {
195: function refund(uint256 _fee, bytes32 _orderId, bytes32 _label) external onlyAggregator() returns(bool) {
File: PaycrestSettingManager.sol
46: function settingManagerBool(bytes32 what, address value, bool status) external onlyOwner {
59: function setSupportedInstitutions(bytes32 currency, SharedStructs.Institution[] memory institutions) external onlyOwner {
78: function updateProtocolFees(uint64 _protocolFeePercent) external onlyOwner {
90: function updateProtocolAddresses(bytes32 what, address value) external onlyOwner {
103: function updateProtocolAggregator(bytes calldata aggregator) external onlyOwner {
Saves 5 gas per loop
Instances (2):
File: Paycrest.sol
241: i++;
File: PaycrestSettingManager.sol
69: i++;
Instances (6):
File: Paycrest.sol
87: require(bytes(messageHash).length > 0, "InvalidMessageHash");
129: require(_amount > 0, "AmountIsZero");
133: if (_senderFee > 0) {
162: if (order[_orderId].senderFee > 0) {
167: if (order[_orderId].protocolFee > 0) {
File: mocks/MockUSDC.sol
22: require(_balanceOf > 0, "MockUSDT: Nothing to burn");
Issue | Instances | |
---|---|---|
L-1 | Initializers could be front-run | 4 |
L-2 | Unsafe ERC20 operation(s) | 6 |
Initializers could be front-run, allowing an attacker to either set their own values, take ownership of the contract, and in the best case forcing a re-deployment
Instances (4):
File: Paycrest.sol
37: function initialize() external initializer {
37: function initialize() external initializer {
39: __Ownable_init();
40: __Pausable_init();
Instances (6):
File: Paycrest.sol
90: IERC20(_token).transferFrom(msg.sender, address(this), _amount + _senderFee);
169: IERC20(token).transfer(treasuryAddress, order[_orderId].protocolFee);
174: IERC20(token).transfer(_liquidityProvider, order[_orderId].amount);
189: IERC20(order[_orderId].token).transfer(recipient, _fee);
201: IERC20(order[_orderId].token).transfer(treasuryAddress, _fee);
208: IERC20(order[_orderId].token).transfer(order[_orderId].refundAddress, refundAmount);
Issue | Instances | |
---|---|---|
M-1 | Centralization Risk for trusted owners | 7 |
Contracts have owners with privileged rights to perform admin tasks and need to be trusted to not perform malicious updates or drain funds.
Instances (7):
File: Paycrest.sol
57: function pause() external onlyOwner {
64: function unpause() external onlyOwner {
File: PaycrestSettingManager.sol
46: function settingManagerBool(bytes32 what, address value, bool status) external onlyOwner {
59: function setSupportedInstitutions(bytes32 currency, SharedStructs.Institution[] memory institutions) external onlyOwner {
78: function updateProtocolFees(uint64 _protocolFeePercent) external onlyOwner {
90: function updateProtocolAddresses(bytes32 what, address value) external onlyOwner {
103: function updateProtocolAggregator(bytes calldata aggregator) external onlyOwner {