Platform: Code4rena
Start Date: 17/03/2023
Pot Size: $36,500 USDC
Total HM: 10
Participants: 98
Period: 3 days
Judge: leastwood
Total Solo HM: 5
Id: 223
League: ETH
Rank: 86/98
Findings: 1
Award: $12.03
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: 0xSmartContract
Also found by: 0xdaydream, 0xnev, Aymen0909, Deekshith99, Diana, EvanW, Fanz, JCN, Jerry0x, K42, Kresh, Madalad, MiniGlome, Polaris_tow, Rageur, ReyAdmirado, Rolezn, SAAJ, SaeedAlipoor01988, Sathish9098, Shubham, Udsen, Viktor_Cortess, Walter, anodaram, arialblack14, atharvasama, caspersolangii, codeslide, descharre, fatherOfBlocks, felipe, ginlee, igingu, lukris02, nadin, slvDev, tnevler, turvy_fuzz, viking71
12.034 USDC - $12.03
Issue | Instances | |
---|---|---|
[G-01] | abi.encodePacked is more gas efficient than abi.encode | 1 |
[G-02] | Functions guaranteed to revert when called by normal users can be marked payable | 5 |
[G-03] | Use assembly to calculate hashes | 1 |
[G-04] | Division/Multiplication by 2 should use bit shifting | 5 |
[G-05] | Use constant , immutable where applicable | 13 |
[G-06] | Use unchecked for operations that cannot overflow/underflow | 16 |
[G-07] | Change public functions to external | 3 |
[G-08] | Use a more recent version of Solidity | 4 |
[G-09] | Use named return values | 8 |
Total issues: 9
Total instances: 56
Â
abi.encodePacked
is more gas efficient than abi.encode
abi.encode
pads all elementary types to 32 bytes, whereas abi.encodePacked
will only use the minimal required memory to encode the data. See here for more info.
Instances: 1
File: canto-namespace-protocol/src/Tray.sol 162: lastHash = keccak256(abi.encode(lastHash));
Â
payable
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.
The extra opcodes avoided are CALLVALUE(2), DUP1(3), ISZERO(3), PUSH2(3), JUMPI(10), PUSH1(3), DUP1(3), REVERT(0), JUMPDEST(1), POP(2), which costs an average of about 21 gas per call to the function, in addition to the extra deployment cost (2400 per instance).
Instances: 5
File: canto-namespace-protocol/src/Namespace.sol 196: function changeNoteAddress(address _newNoteAddress) external onlyOwner { 204: function changeRevenueAddress(address _newRevenueAddress) external onlyOwner {
File: canto-namespace-protocol/src/Tray.sol 210: function changeNoteAddress(address _newNoteAddress) external onlyOwner { 218: function changeRevenueAddress(address _newRevenueAddress) external onlyOwner { 225: function endPrelaunchPhase() external onlyOwner {
Â
Saves 5000 deployment gas per instance and 80 runtime gas per instance.
function solidityHash(uint256 a, uint256 b) public view { //unoptimized keccak256(abi.encodePacked(a, b)); }
function assemblyHash(uint256 a, uint256 b) public view { //optimized assembly { mstore(0x00, a) mstore(0x20, b) let hashedVal := keccak256(0x00, 0x40) } }
Instances: 1
File: canto-namespace-protocol/src/Tray.sol 162: lastHash = keccak256(abi.encode(lastHash));
Â
<x> * 2
is equivalent to <x> << 1
and <x> / 2
is the same as <x> >> 1
. The MUL
and DIV
opcodes cost 5 gas, whereas SHL
and SHR
only cost 3 gas.
Instances: 5
File: canto-namespace-protocol/src/Tray.sol 263: tileData.fontClass = 7 + uint8((res - 104) / 2);
File: canto-namespace-protocol/src/Utils.sol 148: uint256 characterIndex = (_characterModifier % ZALGO_NUM_ABOVE) * 2; 157: uint256 characterIndex = (_characterModifier % ZALGO_NUM_OVER) * 2; 166: uint256 characterIndex = (_characterModifier % ZALGO_NUM_BELOW) * 2; 201: uint256 offset = (_characterIndex - 2) * 2;
Â
constant
, immutable
where applicableUse immutable if you want to assign a permanent value at construction. Use constants if you already know the permanent value. Both get directly embedded in bytecode, saving SLOAD. Variables only set in the constructor and never edited afterwards should be marked as immutable, as it would avoid the expensive storage-writing operation in the constructor (around 20 000 gas per variable) and replace the expensive storage-reading operations (around 2100 gas per reading) to a less expensive value reading (3 gas).
Instances: 13
File: canto-pfp-protocol/src/ProfilePicture.sol 35: string public subprotocolName; 41: address indexed minter, 42: uint256 indexed pfpNftID, 43: address indexed referencedContract, 44: uint256 referencedNftId
File: canto-namespace-protocol/src/Namespace.sol 74: address _tray, 75: address _note, 76: address _revenueAddress
File: canto-namespace-protocol/src/Tray.sol 99: bytes32 _initHash, 100: uint256 _trayPrice, 101: address _revenueAddress, 102: address _note, 103: address _namespaceNFT
Â
unchecked
for operations that cannot overflow/underflowBy bypassing Solidity's built in overflow/underflow checks using unchecked
, we can save gas. This is especially beneficial for the index variable within for loops (saves 120 gas per iteration).
Instances: 16
File: canto-bio-protocol/src/Bio.sol 56: for (uint i; i < lengthInBytes; ++i) { 59: bytesOffset++; 100: for (uint i; i < lines; ++i) {
File: canto-namespace-protocol/src/Namespace.sol 122: for (uint256 i; i < numCharacters; ++i) { 127: for (uint256 j = i + 1; j < numCharacters; ++j) { 147: for (uint256 j; j < numBytesChar; ++j) { 150: numBytes += numBytesChar; 174: for (uint256 i; i < numUniqueTrays; ++i) {
File: canto-namespace-protocol/src/Tray.sol 129: for (uint256 i; i < TILES_PER_TRAY; ++i) { 159: for (uint256 i; i < _amount; ++i) { 161: for (uint256 j; j < TILES_PER_TRAY; ++j) {
File: canto-namespace-protocol/src/Utils.sol 109: for (uint256 i = 3; i < numBytes; ++i) { 146: for (uint256 i; i < numAbove; ++i) { 155: for (uint256 i; i < numMiddle; ++i) { 164: for (uint256 i; i < numBelow; ++i) { 225: for (uint256 i; i < _tiles.length; ++i) {
Â
public
functions to external
Functions marked as public
that are not called internally should be set to external
to save gas and improve code quality. External call cost is less expensive than of public functions.
Instances: 3
File: canto-bio-protocol/src/Bio.sol 43: function tokenURI(uint256 _id) public view override returns (string memory) {
File: canto-namespace-protocol/src/Namespace.sol 90: function tokenURI(uint256 _id) public view override returns (string memory) {
File: canto-namespace-protocol/src/Tray.sol 119: function tokenURI(uint256 _id) public view override returns (string memory) {
Â
Use a Solidity version of at least 0.8.2 to get simple compiler automatic inlining.
Use a Solidity version of at least 0.8.3 to get better struct packing and cheaper multiple storage reads.
Use a Solidity version of at least 0.8.4 to get custom errors, which are cheaper at deployment than revert()
/require()
strings.
Use a Solidity version of at least 0.8.10 to have external calls skip contract existence checks if the external call has a return value.
Use a Solidity version of at least 0.8.12 to get string.concat() to be used instead of abi.encodePacked(<str>,<str>).
Use a solidity version of at least 0.8.13 to get the ability to use using for with a list of free functions.
Instances: 4
File: canto-pfp-protocol/src/ProfilePicture.sol 2: pragma solidity >=0.8.0;
File: canto-bio-protocol/src/Bio.sol 2: pragma solidity >=0.8.0;
File: canto-namespace-protocol/src/Namespace.sol 2: pragma solidity >=0.8.0;
File: canto-namespace-protocol/src/Tray.sol 2: pragma solidity >=0.8.0;
Â
Using named return values instead of explicitly calling return
saves gas.
Instances: 8
File: canto-pfp-protocol/src/ProfilePicture.sol 70: function tokenURI(uint256 _id) public view override returns (string memory) {
File: canto-bio-protocol/src/Bio.sol 43: function tokenURI(uint256 _id) public view override returns (string memory) {
File: canto-namespace-protocol/src/Namespace.sol 90: function tokenURI(uint256 _id) public view override returns (string memory) {
File: canto-namespace-protocol/src/Tray.sol 119: function tokenURI(uint256 _id) public view override returns (string memory) { 276: function _startTokenId() internal pure override returns (uint256) {
File: canto-namespace-protocol/src/Utils.sol 77: ) internal pure returns (bytes memory) { 222: function generateSVG(Tray.TileData[] memory _tiles, bool _isTray) internal pure returns (string memory) { 270: returns (bytes memory)
Â
#0 - c4-judge
2023-04-11T05:29:43Z
0xleastwood marked the issue as grade-b