Skip to content

Instantly share code, notes, and snippets.

@ahmedovv123
Created March 17, 2023 20:30
Show Gist options
  • Save ahmedovv123/ac550b389bcbe21043c613e6c6c1b563 to your computer and use it in GitHub Desktop.
Save ahmedovv123/ac550b389bcbe21043c613e6c6c1b563 to your computer and use it in GitHub Desktop.

Report

Gas Optimizations

Issue Instances
GAS-1 Use assembly to check for address(0) 5
GAS-2 Cache array length outside of loop 1
GAS-3 State variables should be cached in stack variables rather than re-reading them from storage 1
GAS-4 Use calldata instead of memory for function arguments that do not get mutated 1
GAS-5 Pre-increments and pre-decrements are cheaper than post-increments and post-decrements 3
GAS-6 Use shift Right/Left instead of division/multiplication if possible 14
GAS-7 Use storage instead of memory for structs/arrays 18
GAS-8 Increments can be unchecked in for-loops 14
GAS-9 Use != 0 instead of > 0 for unsigned integer comparison 6
GAS-10 internal functions not called by the contract should be removed 1

[GAS-1] Use assembly to check for address(0)

Saves 6 gas per instance

Instances (5):

File: canto-bio-protocol/src/Bio.sol

44:         if (_ownerOf[_id] == address(0)) revert TokenNotMinted(_id);
File: canto-namespace-protocol/src/Namespace.sol

91:         if (_ownerOf[_id] == address(0)) revert TokenNotMinted(_id);
File: canto-namespace-protocol/src/Tray.sol

240:             if (startTokenId <= numPrelaunchMinted && to != address(0))
File: canto-pfp-protocol/src/ProfilePicture.sol

72:         if (nftContract == address(0)) revert PFPNoLongerOwnedByOriginalOwner(_id);

95:         if (_ownerOf[_pfpID] == address(0)) revert TokenNotMinted(_pfpID);

[GAS-2] Cache array length outside of loop

If not cached, the solidity compiler will always read the length of the array during each iteration. That is, if it is a storage array, this is an extra sload operation (100 additional extra gas for each iteration except for the first) and if it is a memory array, this is an extra mload operation (3 additional gas for each iteration except for the first).

Instances (1):

File: canto-namespace-protocol/src/Utils.sol

225:         for (uint256 i; i < _tiles.length; ++i) {

[GAS-3] State variables should be cached in stack variables rather than re-reading them from storage

The instances below point to the second+ access of a state variable within a function. Caching of a state variable replaces each Gwarmaccess (100 gas) with a much cheaper stack read. Other less obvious fixes/optimizations include having local memory caches of state variable structs, or having local caches of state variable contracts/addresses.

Saves 100 gas per instance

Instances (1):

File: canto-namespace-protocol/src/Tray.sol

163:                 trayTiledata[j] = _drawing(uint256(lastHash));

[GAS-4] Use calldata instead of memory for function arguments that do not get mutated

Mark data types as calldata instead of memory where possible. This makes it so that the data is not automatically loaded into memory. If the data passed into the function does not need to be changed (like updating values in an array), it can be passed in as calldata. The one exception to this is if the argument must later be passed into another function that takes an argument that specifies memory storage.

Instances (1):

File: canto-pfp-protocol/src/ProfilePicture.sol

57:     constructor(address _cidNFT, string memory _subprotocolName) ERC721("Profile Picture", "PFP") {

[GAS-5] Pre-increments and pre-decrements are cheaper than post-increments and post-decrements

Saves 5 gas per iteration

Instances (3):

File: canto-bio-protocol/src/Bio.sol

59:             bytesOffset++;

90:                     strLines[insertedLines++] = string(bytesLines);
File: canto-namespace-protocol/src/Namespace.sol

154:                 uniqueTrays[numUniqueTrays++] = trayID;

[GAS-6] Use shift Right/Left instead of division/multiplication if possible

Shifting left by N is like multiplying by 2^N and shifting right by N is like dividing by 2^N

Instances (14):

File: canto-namespace-protocol/src/Tray.sol

260:             } else if (res < 104) {

262:             } else if (res < 108) {

264:                 if (tileData.fontClass == 7) {
File: canto-namespace-protocol/src/Utils.sol

65:     uint256 private constant EMOJIS_BYTE_OFFSET_SEVEN_BYTES = 1671; // 17 * 3 + 366 * 4 + 26 * 6

66:     uint256 private constant EMOJIS_BYTE_OFFSET_EIGHT_BYTES = 1720; // 17 * 3 + 366 * 4 + 26 * 6 + 7 * 7

67:     uint256 private constant EMOJIS_BYTE_OFFSET_FOURTEEN_BYTES = 1744; // 17 * 3 + 366 * 4 + 26 * 6 + 7 * 7 + 3 * 8

68: 

68: 

90:                 supportsSkinToneModifier = _characterIndex >= EMOJIS_LE_FOUR_BYTES - EMOJIS_MOD_SKIN_TONE_FOUR_BYTES;

100:             } else {

149:                 character = abi.encodePacked(

158:                 character = abi.encodePacked(

167:                 character = abi.encodePacked(

202:                     return abi.encodePacked(FONT_SQUIGGLE[5 + offset], FONT_SQUIGGLE[6 + offset]);

[GAS-7] Use storage instead of memory for structs/arrays

Using memory copies the struct or array in memory. Use storage to save the location in storage and have cheaper reads:

Instances (18):

File: canto-bio-protocol/src/Bio.sol

46:         bytes memory bioTextBytes = bytes(bioText);

47:         uint lengthInBytes = bioTextBytes.length;

51:         bool prevByteWasContinuation;

55:         uint bytesOffset;

99:         string memory text = '<text x="50%" y="50%" dominant-baseline="middle" text-anchor="middle">';

100:         for (uint i; i < lines; ++i) {

104:             bytes(
File: canto-namespace-protocol/src/Namespace.sol

93:             bytes(

118:         uint256 numBytes;

122:         for (uint256 i; i < numCharacters; ++i) {

134:             uint8 characterModifier = tileData.characterModifier;

145:             tileData.characterModifier = characterModifier;

169:         uint256 currentRegisteredID = nameToToken[nameToRegister];

189:         delete tokenToName[_id];
File: canto-namespace-protocol/src/Tray.sol

129:         for (uint256 i; i < TILES_PER_TRAY; ++i) {

133:             bytes(
File: canto-namespace-protocol/src/Utils.sol

105:                 EMOJIS[byteOffset],

146:             for (uint256 i; i < numAbove; ++i) {

[GAS-8] Increments can be unchecked in for-loops

Instances (14):

File: canto-bio-protocol/src/Bio.sol

56:         for (uint i; i < lengthInBytes; ++i) {

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) {

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) {

[GAS-9] Use != 0 instead of > 0 for unsigned integer comparison

Instances (6):

File: canto-bio-protocol/src/Bio.sol

2: pragma solidity >=0.8.0;

60:             if ((i > 0 && (i + 1) % 40 == 0) || prevByteWasContinuation || i == lengthInBytes - 1) {
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;
File: canto-namespace-protocol/src/Utils.sol

2: pragma solidity >=0.8.0;
File: canto-pfp-protocol/src/ProfilePicture.sol

2: pragma solidity >=0.8.0;

[GAS-10] internal functions not called by the contract should be removed

If the functions are required by an interface, the contract should inherit from that interface and use the override keyword

Instances (1):

File: canto-namespace-protocol/src/Utils.sol

222:     function generateSVG(Tray.TileData[] memory _tiles, bool _isTray) internal pure returns (string memory) {

Non Critical Issues

Issue Instances
NC-1 Missing checks for address(0) when assigning values to address state variables 4
NC-2 Constants should be defined rather than using magic numbers 3
NC-3 Functions not used internally could be marked external 4

[NC-1] Missing checks for address(0) when assigning values to address state variables

Instances (4):

File: canto-namespace-protocol/src/Namespace.sol

80:         revenueAddress = _revenueAddress;

206:         revenueAddress = _newRevenueAddress;
File: canto-namespace-protocol/src/Tray.sol

107:         revenueAddress = _revenueAddress;

220:         revenueAddress = _newRevenueAddress;

[NC-2] Constants should be defined rather than using magic numbers

Instances (3):

File: canto-bio-protocol/src/Bio.sol

54:         bytes memory bytesLines = new bytes(80);

91:                     bytesLines = new bytes(80);
File: canto-namespace-protocol/src/Namespace.sol

113:         uint256 fusingCosts = 2**(13 - numCharacters) * 1e18;

[NC-3] Functions not used internally could be marked external

Instances (4):

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) {
File: canto-pfp-protocol/src/ProfilePicture.sol

70:     function tokenURI(uint256 _id) public view override returns (string memory) {

Low Issues

Issue Instances
L-1 Unspecific compiler version pragma 5

[L-1] Unspecific compiler version pragma

Instances (5):

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;
File: canto-namespace-protocol/src/Utils.sol

2: pragma solidity >=0.8.0;
File: canto-pfp-protocol/src/ProfilePicture.sol

2: pragma solidity >=0.8.0;

Medium Issues

Issue Instances
M-1 Centralization Risk for trusted owners 9

[M-1] Centralization Risk for trusted owners

Impact:

Contracts have owners with privileged rights to perform admin tasks and need to be trusted to not perform malicious updates or drain funds.

Instances (9):

File: canto-namespace-protocol/src/Namespace.sol

11: contract Namespace is ERC721, Owned {

77:     ) ERC721("Namespace", "NS") Owned(msg.sender) {

196:     function changeNoteAddress(address _newNoteAddress) external onlyOwner {

204:     function changeRevenueAddress(address _newRevenueAddress) external onlyOwner {
File: canto-namespace-protocol/src/Tray.sol

13: contract Tray is ERC721A, Owned {

104:     ) ERC721A("Namespace Tray", "NSTRAY") Owned(msg.sender) {

210:     function changeNoteAddress(address _newNoteAddress) external onlyOwner {

218:     function changeRevenueAddress(address _newRevenueAddress) external onlyOwner {

225:     function endPrelaunchPhase() external onlyOwner {
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment