Number | Details | Instances |
---|---|---|
1 | x += y costs more gas than x = x + y for state variables |
1 |
2 | Can declare the variable outside the loop to save gas | 4 |
3 | Public functions not called by contract can be changed to external to save some gas | 31 |
4 | Don't compare boolean expressions to boolean literals | 3 |
5 | Internal functions only called once can be inlined to save gas | 1 |
6 | Multiple address/id mappings can be combined into a single mapping to a struct | 2 |
Using the addition operator instead of plus-equals saves some gas.
quest-protocol\contracts\Quest.sol:
114 _transferRewards(totalRedeemableRewards); 115: redeemedTokens += redeemableTokenCount; 116
Declaring the loop variable outside the loop saves gas.
quest-protocol\contracts\Quest.sol:
69 function _setClaimed(uint256[] memory tokenIds_) private { 70: for (uint i = 0; i < tokenIds_.length; i++) { 71 claimedList[tokenIds_[i]] = true; 103 uint256 redeemableTokenCount = 0; 104: for (uint i = 0; i < tokens.length; i++) { 105 if (!isClaimed(tokens[i])) {quest-protocol\contracts\RabbitHoleReceipt.sol:
116 117: for (uint i = 0; i < msgSenderBalance; i++) { 118 uint tokenId = tokenOfOwnerByIndex(claimingAddress_, i); 127 128: for (uint i = 0; i < msgSenderBalance; i++) { 129 if (tokenIdsForQuest[i] > 0) {
Contracts are allowed to override their parents’ functions and change the visibility from external to public and can save gas by doing so.
quest-protocol\contracts\Erc20Quest.sol
102: function withdrawFee() public onlyAdminWithdrawAfterEnd { 103: IERC20(rewardToken).safeTransfer(protocolFeeRecipient, protocolFee()); 104: }quest-protocol\contracts\Quest.sol
50: function start() public virtual onlyOwner { 51: isPaused = false; 52: hasStarted = true; 53: } 57: function pause() public onlyOwner onlyStarted { 58: isPaused = true; 59: } 63: function unPause() public onlyOwner onlyStarted { 64: isPaused = false; 65: } 96: function claim() public virtual onlyQuestActive { 140: function getRewardAmount() public view returns (uint256) { 141: return rewardAmountInWeiOrTokenId; 142: } 145: function getRewardToken() public view returns (address) { 146: return rewardToken; 147: }File: quest-protocol\contracts\QuestFactory.sol
37: function initialize( 38: address claimSignerAddress_, 39: address rabbitholeReceiptContract_, 40: address protocolFeeRecipient_ 41: ) public initializer { 61: function createQuest( 62: address rewardTokenAddress_, 63: uint256 endTime_, 64: uint256 startTime_, 65: uint256 totalParticipants_, 66: uint256 rewardAmountOrTokenId_, 67: string memory contractType_, 68: string memory questId_ 69: ) public onlyRole(CREATE_QUEST_ROLE) returns (address) { 142: function changeCreateQuestRole(address account_, bool canCreateQuest_) public onlyOwner { 159: function setClaimSignerAddress(address claimSignerAddress_) public onlyOwner { 160: claimSignerAddress = claimSignerAddress_; 161: } 172: function setRabbitHoleReceiptContract(address rabbitholeReceiptContract_) public onlyOwner { 173: rabbitholeReceiptContract = RabbitHoleReceipt(rabbitholeReceiptContract_); 174: } 179: function setRewardAllowlistAddress(address rewardAddress_, bool allowed_) public onlyOwner { 180: rewardAllowlist[rewardAddress_] = allowed_; 181: } 219: function mintReceipt(string memory questId_, bytes32 hash_, bytes memory signature_) public {quest-protocol\contracts\RabbitHoleReceipt.sol
43: function initialize( 44: address receiptRenderer_, 45: address royaltyRecipient_, 46: address minterAddress_, 47: uint royaltyFee_ 48: ) public initializer { 71: function setRoyaltyRecipient(address royaltyRecipient_) public onlyOwner { 72: royaltyRecipient = royaltyRecipient_; 73: } 77: function setQuestFactory(address questFactory_) public onlyOwner { 78: QuestFactoryContract = IQuestFactory(questFactory_); 79: } 83: function setMinterAddress(address minterAddress_) public onlyOwner { 84: minterAddress = minterAddress_; 85: emit MinterAddressSet(minterAddress_); 86: } 90: function setRoyaltyFee(uint256 royaltyFee_) public onlyOwner { 91: royaltyFee = royaltyFee_; 92: emit RoyaltyFeeSet(royaltyFee_); 93: } 098: function mint(address to_, string memory questId_) public onlyMinter { 099: _tokenIds.increment(); 100: uint newTokenID = _tokenIds.current(); 101: questIdForTokenId[newTokenID] = questId_; 102: timestampForTokenId[newTokenID] = block.timestamp; 103: _safeMint(to_, newTokenID); 104: } 109: function getOwnedTokenIdsOfQuest( 110: string memory questId_, 111: address claimingAddress_ 112: ) public view returns (uint[] memory) { 158: function tokenURI( 159: uint tokenId_ 160: ) public view virtual override(ERC721Upgradeable, ERC721URIStorageUpgradeable) returns (string memory) {quest-protocol\contracts\RabbitHoleTickets.sol
32: function initialize( 33: address ticketRenderer_, 34: address royaltyRecipient_, 35: address minterAddress_, 36: uint royaltyFee_ 37: ) public initializer { 54: function setTicketRenderer(address ticketRenderer_) public onlyOwner { 55: TicketRendererContract = TicketRenderer(ticketRenderer_); 56: } 60: function setRoyaltyRecipient(address royaltyRecipient_) public onlyOwner { 61: royaltyRecipient = royaltyRecipient_; 62: } 66: function setRoyaltyFee(uint256 royaltyFee_) public onlyOwner { 67: royaltyFee = royaltyFee_; 68: emit RoyaltyFeeSet(royaltyFee_); 69: } 73: function setMinterAddress(address minterAddress_) public onlyOwner { 74: minterAddress = minterAddress_; 75: emit MinterAddressSet(minterAddress_); 76: } 83: function mint(address to_, uint256 id_, uint256 amount_, bytes memory data_) public onlyMinter { 84: _mint(to_, id_, amount_, data_); 85: } 92: function mintBatch( 93: address to_, 94: uint256[] memory ids_, 95: uint256[] memory amounts_, 96: bytes memory data_ 97: ) public onlyMinter { 98: _mintBatch(to_, ids_, amounts_, data_); 99: } 102: function uri(uint tokenId_) public view virtual override(ERC1155Upgradeable) returns (string memory) { 103: return TicketRendererContract.generateTokenURI(tokenId_); 104: }quest-protocol\contracts\ReceiptRenderer.sol
21: function generateTokenURI( 22: uint tokenId_, 23: string memory questId_, 24: uint totalParticipants_, 25: bool claimed_, 26: uint rewardAmount_, 27: address rewardAddress_ 28: ) public view virtual returns (string memory) {quest-protocol\contracts\TicketRenderer.sol
16: function generateTokenURI( 17: uint tokenId_ 18: ) public pure returns (string memory) {
It is not required to check for equality between boolean expressions and boolean literals and can be ommitted to save gas.
quest-protocol\contracts\Quest.sol:
136: return claimedList[tokenId_] == true;quest-protocol\contracts\QuestFactory.sol:
221: if (quests[questId_].addressMinted[msg.sender] == true) revert AddressAlreadyMinted(); 73: if (rewardAllowlist[rewardTokenAddress_] == false) revert RewardNotAllowed();
quest-protocol\contracts\QuestFactory.sol:
151 /// @param account_ The account to grant admin and create quest roles
152: function grantDefaultAdminAndCreateQuestRole(address account_) internal {
153 _grantRole(DEFAULT_ADMIN_ROLE, account_);
If both fields are accessed in the same function, can save ~42 gas per access due to not having to recalculate the key’s keccak256 hash (Gkeccak256 - 30 gas) and that calculation’s associated stack operations. Depending on the circumstances and sizes of types, can avoid a Gsset per mapping combined.
quest-protocol\contracts\RabbitHoleReceipt.sol:
30: mapping(uint => string) public questIdForTokenId; 34: mapping(uint => uint) public timestampForTokenId;This can be refactored to
30: struct QuestData{ 31: string questIdForTokenId; 32: uint256 timestampForTokenId; 33: } 34: mapping(uint256 => QuestData) public questData;