Platform: Code4rena
Start Date: 18/10/2022
Pot Size: $75,000 USDC
Total HM: 27
Participants: 144
Period: 7 days
Judge: gzeon
Total Solo HM: 13
Id: 170
League: ETH
Rank: 18/144
Findings: 2
Award: $771.29
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: Rolezn
Also found by: 0x1f8b, 0x52, 0x5rings, 0xNazgul, 0xSmartContract, 0xZaharina, 0xhunter, 0xzh, 8olidity, Amithuddar, Aymen0909, B2, Bnke0x0, Chom, Deivitto, Diana, Diraco, Dravee, Franfran, JC, Jeiwan, Josiah, JrNet, Jujic, KingNFT, KoKo, Lambda, Margaret, Migue, Ocean_Sky, PaludoX0, Picodes, Rahoz, RaoulSchaffranek, RaymondFam, RedOneN, ReyAdmirado, Shinchan, Tagir2003, Trust, Waze, Yiko, __141345__, a12jmx, adriro, ajtra, arcoun, aysha, ballx, bin2chen, bobirichman, brgltd, bulej93, catchup, catwhiskeys, caventa, cccz, cdahlheimer, ch0bu, chaduke, chrisdior4, cloudjunky, cryptostellar5, cryptphi, csanuragjain, cylzxje, d3e4, delfin454000, djxploit, durianSausage, erictee, fatherOfBlocks, francoHacker, gianganhnguyen, gogo, hansfriese, i_got_hacked, ignacio, imare, karanctf, kv, leosathya, louhk, lukris02, lyncurion, m_Rassska, malinariy, martin, mcwildy, mics, minhtrng, nicobevi, oyc_109, pashov, peanuts, pedr02b2, peiw, rbserver, ret2basic, rotcivegaf, rvierdiiev, ryshaw, sakman, sakshamguruji, saneryee, securerodd, seyni, sikorico, svskaushik, teawaterwire, tnevler, w0Lfrum
549.0408 USDC - $549.04
constant
Variables Should be CapitalizedConstants should be named with all capital letters with underscores separating words if need be. Here are some of the instances entailed:
https://github.com/code-423n4/2022-10-holograph/blob/main/src/HolographBridge.sol#L27-L43 https://github.com/code-423n4/2022-10-holograph/blob/main/src/HolographOperator.sol#L30-L54 https://github.com/code-423n4/2022-10-holograph/blob/main/contracts/HolographFactory.sol#L127-L131 https://github.com/code-423n4/2022-10-holograph/blob/main/contracts/module/LayerZeroModule.sol#L126-L146
Please visit the following style guide for more details:
https://docs.soliditylang.org/en/v0.8.14/style-guide.html
init()
of HolographBridge.sol
, HolographOperator.sol
, HolographFactory.sol
, and LayerZeroModule.sol
uses !_isInitialized()
to prevent re-initialization of the contract.
https://github.com/code-423n4/2022-10-holograph/blob/main/src/HolographBridge.sol#L64 https://github.com/code-423n4/2022-10-holograph/blob/main/src/HolographOperator.sol#L142 https://github.com/code-423n4/2022-10-holograph/blob/main/contracts/HolographFactory.sol#L144 https://github.com/code-423n4/2022-10-holograph/blob/main/contracts/module/LayerZeroModule.sol#L159
Consider using OpenZeppelin's Initializable which will have this scenario better taken care of. When a contract is initialized, its isInitialized
state variable is set to true.
Assembly block is used in numerous contracts of Holograph. While this does not pose a security risk per se, it is at the same time a complicated and critical part of the system. Moreover, as this is a low-level language that is harder to parse by readers, consider including extensive documentation regarding the rationale behind its use, clearly explaining what every single assembly instruction does. This will make it easier for users to trust the code, for reviewers to verify it, and for developers to build on top of it or update it. Note that the use of assembly discards several important safety features of Solidity, which may render the code less safe and more error-prone.
Here are some of the instances entailed:
https://github.com/code-423n4/2022-10-holograph/blob/main/src/HolographBridge.sol#L325-L336 https://github.com/code-423n4/2022-10-holograph/blob/main/src/HolographOperator.sol#L571-L582 https://github.com/code-423n4/2022-10-holograph/blob/main/contracts/enforcer/Holographer.sol#L230-L241
ecrecover
is Susceptible to Signature MalleabilityThe built-in EVM pre-compiled ecrecover
is susceptible to signature malleability due to non-unique v and s (which may not always be in the lower half of the modulo set) values, possibly leading to replay attacks. Devoid of the adoption of nonces, this could prove a vulnerability when not carefully used.
https://github.com/code-423n4/2022-10-holograph/blob/main/contracts/HolographFactory.sol#L333-L334
Consider using OpenZeppelin’s ECDSA library which has been time tested in preventing this malleability where possible.
Automated tools would typically flag a contract using inline-assembly as having high complexity, poor readability and error prone as far as security is concerned. As such, avoid using it where possible. For instance, the following block of codes could be replaced with:
https://github.com/code-423n4/2022-10-holograph/blob/main/contracts/HolographFactory.sol#L310-L313
bytes32 codehash = contractAddress.codehash;
Similarly, there is a newer syntax way to invoke create2 without assembly, you just need to pass salt and the contract constructor arguments just as in the following instance as an example:
https://github.com/code-423n4/2022-10-holograph/blob/main/contracts/HolographFactory.sol#L235-L246
address sourceContractAddress = address(new <ContractName>{salt: saltInt}(<constructor argument(s)>));
Please visit the following links for further details:
https://solidity-by-example.org/app/create2/ https://docs.soliditylang.org/en/latest/control-structures.html#salted-contract-creations-create2
Critical operations not triggering events will make it difficult to review the correct behavior of the deployed contracts. Users and blockchain monitoring systems will not be able to detect suspicious behaviors at ease without events. Consider adding events where appropriate for all critical operations for better support of off-chain logging API. Here are some of the instances entailed:
https://github.com/code-423n4/2022-10-holograph/blob/main/contracts/module/LayerZeroModule.sol#L320-L324 https://github.com/code-423n4/2022-10-holograph/blob/main/contracts/module/LayerZeroModule.sol#L340-L344 https://github.com/code-423n4/2022-10-holograph/blob/main/contracts/module/LayerZeroModule.sol#L360-L364 https://github.com/code-423n4/2022-10-holograph/blob/main/contracts/module/LayerZeroModule.sol#L380-L384 https://github.com/code-423n4/2022-10-holograph/blob/main/contracts/module/LayerZeroModule.sol#L441-L445 https://github.com/code-423n4/2022-10-holograph/blob/main/contracts/module/LayerZeroModule.sol#L470-L474
It is a good practice to give time for users to react and adjust to critical changes with a mandatory time window between them. The first step merely broadcasts to users that a particular change is coming, and the second step commits that change after a suitable waiting period. This allows users that do not accept the change to withdraw within the grace period. A timelock provides more guarantees and reduces the level of trust required, thus decreasing risk for users. It also indicates that the project is legitimate (less risk of a malicious Owner making any malicious or ulterior intention). Specifically, privileged roles could use front running to make malicious changes just ahead of incoming transactions, or purely accidental negative effects could occur due to the unfortunate timing of changes. Here are some of the instances entailed:
https://github.com/code-423n4/2022-10-holograph/blob/main/contracts/module/LayerZeroModule.sol#L320-L324 https://github.com/code-423n4/2022-10-holograph/blob/main/contracts/module/LayerZeroModule.sol#L340-L344 https://github.com/code-423n4/2022-10-holograph/blob/main/contracts/module/LayerZeroModule.sol#L360-L364 https://github.com/code-423n4/2022-10-holograph/blob/main/contracts/module/LayerZeroModule.sol#L380-L384 https://github.com/code-423n4/2022-10-holograph/blob/main/contracts/module/LayerZeroModule.sol#L441-L445 https://github.com/code-423n4/2022-10-holograph/blob/main/contracts/module/LayerZeroModule.sol#L470-L474
The owner of the contracts has too many privileges relative to standard users. The consequence is disastrous if the contract owner's private key has been compromised. And, in the event the key was lost or unrecoverable, no implementation upgrades and system parameter updates will ever be possible.
For a Holograph Project project this grand, it increases the likelihood that the owner will be targeted by an attacker, especially given the insufficient protection on sensitive owner private keys. The concentration of privileges creates a single point of failure; and, here are some of the incidents that could possibly transpire:
Change admin and mess up with all the setter functions, hijacking the entire protocol.
Consider:
Function calls made in unbounded loop are error-prone with potential resource exhaustion as it can trap the contract due to the gas limitations or failed transactions. Here are some of the instances entailed:
https://github.com/code-423n4/2022-10-holograph/blob/main/contracts/enforcer/PA1D.sol#L307 https://github.com/code-423n4/2022-10-holograph/blob/main/contracts/enforcer/PA1D.sol#L323 https://github.com/code-423n4/2022-10-holograph/blob/main/contracts/enforcer/PA1D.sol#L356
Consider bounding the loop where possible to avoid unnecessary gas wastage and denial of service.
Throughout the codebase HolographERC721
, there are lines of code that have been commented out with //. This can lead to confusion and is detrimental to overall code readability. Consider removing commented out lines of code that are no longer needed.
Here are some of the instances entailed:
https://github.com/code-423n4/2022-10-holograph/blob/main/contracts/enforcer/HolographERC721.sol#L527-L537 https://github.com/code-423n4/2022-10-holograph/blob/main/contracts/enforcer/HolographERC721.sol#L542-L552 https://github.com/code-423n4/2022-10-holograph/blob/main/contracts/enforcer/HolographERC721.sol#L557-L570
Long bytes of literal values assigned to constants may incur typo/human error. Considering assigning these constants with their corresponding expression. For instance, the following code line could be refactored to:
https://github.com/code-423n4/2022-10-holograph/blob/main/contracts/HolographBridge.sol#L126
bytes32 constant _factorySlot = bytes32(uint256(keccak256('eip1967.Holograph.factory')) - 1)
Note: The calculated byte value may be included as a comment instead.
It is a good practice to allow an anticipated failed transaction to transpire as early as possible to avoid any unnecessary wastage of gas. Among all operator inputted parameters, `doNotRevert' is a boolean which will directly determine whether or not the function call is going to revert. Consider placing the following require statement at the beginning of the code block:
https://github.com/code-423n4/2022-10-holograph/blob/main/contracts/HolographBridge.sol#L233
delete
to Clear Variablesdelete a
assigns the initial value for the type to a
. i.e. for integers it is equivalent to a = 0
, but it can also be used on arrays, where it assigns a dynamic array of length zero or a static array of the same length with all elements reset. For structs, it assigns a struct with all members reset. Similarly, it can also be used to set an address to zero address. It has no effect on whole mappings though (as the keys of mappings may be arbitrary and are generally unknown). However, individual keys and what they map to can be deleted: If a
is a mapping, then delete a[x]
will delete the value stored at x.
The delete key better conveys the intention and is also more idiomatic. Consider replacing assignments of zero with delete statements. Here are some of the instances entailed:
https://github.com/code-423n4/2022-10-holograph/blob/main/contracts/HolographOperator.sol#L399 https://github.com/code-423n4/2022-10-holograph/blob/main/contracts/HolographOperator.sol#L924 https://github.com/code-423n4/2022-10-holograph/blob/main/contracts/HolographOperator.sol#L1132-L1136
block.timestamp
UnreliableThe use of block.timestamp
as part of the time checks can be slightly altered by miners/validators to favor them in contracts that have logic strongly dependent on them.
Consider taking into account this issue and warning the users that such a scenario could happen. If the alteration of timestamps cannot affect the protocol in any way, consider documenting the reasoning and writing tests enforcing that these guarantees will be preserved even if the code changes in the future.
Here are some of the instances entailed:
https://github.com/code-423n4/2022-10-holograph/blob/main/contracts/HolographOperator.sol#L345 https://github.com/code-423n4/2022-10-holograph/blob/main/contracts/HolographOperator.sol#L531
The following instance entailing the generation of a random number is of particular concern although it has been documented that _jobNonce()
will have the risk taken care of:
https://github.com/code-423n4/2022-10-holograph/blob/main/contracts/HolographOperator.sol#L499
Solidity contracts can use a special form of comments, i.e., the Ethereum Natural Language Specification Format (NatSpec) to provide rich documentation for functions, return variables and more. Please visit the following link for further details:
https://docs.soliditylang.org/en/v0.8.16/natspec-format.html
Here are some of the functions lacking @param comments:
https://github.com/code-423n4/2022-10-holograph/blob/main/contracts/HolographOperator.sol#L445 https://github.com/code-423n4/2022-10-holograph/blob/main/contracts/HolographOperator.sol#L484 https://github.com/code-423n4/2022-10-holograph/blob/main/contracts/HolographOperator.sol#L1122 https://github.com/code-423n4/2022-10-holograph/blob/main/contracts/HolographOperator.sol#L1160 https://github.com/code-423n4/2022-10-holograph/blob/main/contracts/HolographOperator.sol#L1185
Open TODOs can point to architecture or programming issues that still need to be resolved. Consider resolving them before deploying.
Here is one of the instances entailed:
https://github.com/code-423n4/2022-10-holograph/blob/main/contracts/HolographOperator.sol#L274-L277
OpenZeppelin maintains a library of standard, audited, community-reviewed, and battle-tested smart contracts. Instead of always importing these contracts, Holograph project re-implements them in some cases. This increases the amount of code that the Holograph team will have to maintain and miss all the improvements and bug fixes that the OpenZeppelin team is constantly implementing with the help of the community.
Consider importing the OpenZeppelin contracts instead of re-implementing or copying them. These contracts can be extended to add the extra functionalities required by Holograph. Here are some of the instances entailed:
https://github.com/code-423n4/2022-10-holograph/blob/main/contracts/HolographOperator.sol#L104 https://github.com/code-423n4/2022-10-holograph/blob/main/contracts/HolographOperator.sol#L115
As an example, each respective code line below may be refactored to minimize the frequency of truncation:
https://github.com/code-423n4/2022-10-holograph/blob/main/contracts/module/LayerZeroModule.sol#L274
return (gasPrice * gasLimit * 11 * dstPriceRatio / 10**11, nativeFee);
https://github.com/code-423n4/2022-10-holograph/blob/main/contracts/module/LayerZeroModule.sol#L293
return gasPrice * gasLimit * 11 * dstPriceRatio / 10**11;
Note: The original code expression may be commented to augment the refactored expression.
Lines in source code are typically limited to 80 characters, but it’s reasonable to stretch beyond this limit when need be as monitor screens theses days are comparatively larger. Considering the files will most likely reside in GitHub that will have a scroll bar automatically kick in when the length is over 164 characters, all code lines and comments should be split when/before hitting this length. Keep line width to max 120 characters for better readability where possible. Here is one of the instances entailed:
https://github.com/code-423n4/2022-10-holograph/blob/main/contracts/enforcer/HolographERC20.sol#L289
Contracts receiving native token payments via receive() external payable {}
have no method to withdraw the Ether. Devoid of implementing a way to withdraw funds or send them to another account, there is no built in way to release the money. The Ether is stuck on the contract balance forever. Here are some some of the instances entailed:
https://github.com/code-423n4/2022-10-holograph/blob/main/contracts/enforcer/HolographERC20.sol#L251 https://github.com/code-423n4/2022-10-holograph/blob/main/contracts/enforcer/HolographERC721.sol#L962
🌟 Selected for report: oyc_109
Also found by: 0x040, 0x1f8b, 0x5rings, 0xNazgul, 0xSmartContract, 0xZaharina, 0xsam, 0xzh, 2997ms, Amithuddar, Aymen0909, B2, Bnke0x0, Deivitto, Diana, Dinesh11G, Franfran, JC, JrNet, Jujic, KingNFT, KoKo, Mathieu, Metatron, Mukund, Olivierdem, PaludoX0, Pheonix, Picodes, RaymondFam, RedOneN, ReyAdmirado, Rolezn, Saintcode_, Satyam_Sharma, Shinchan, Tagir2003, Tomio, Waze, Yiko, __141345__, adriro, ajtra, aysha, ballx, beardofginger, bobirichman, brgltd, bulej93, catchup, catwhiskeys, cdahlheimer, ch0bu, chaduke, chrisdior4, cryptostellar5, cylzxje, d3e4, delfin454000, dharma09, djxploit, durianSausage, emrekocak, erictee, exolorkistis, fatherOfBlocks, gianganhnguyen, gogo, halden, hxzy, i_got_hacked, iepathos, karanctf, leosathya, lucacez, lukris02, lyncurion, m_Rassska, martin, mcwildy, mics, nicobevi, peanuts, peiw, rbserver, ret2basic, rotcivegaf, ryshaw, sakman, sakshamguruji, saneryee, sikorico, skyle, svskaushik, tnevler, vv7, w0Lfrum, zishansami
222.2506 USDC - $222.25
bytes32
instead of string
Fitting your data in fixed-size 32 byte words is much cheaper than using arbitrary-length types (string in this case). Remember that bytes32 uses less gas because it fits in a single EVM word. Typically, any fixed size variable in solidity is cheaper than dynamically sized ones. Here are some of the instances entailed:
https://github.com/code-423n4/2022-10-holograph/blob/main/src/HolographBridge.sol#L225 https://github.com/code-423n4/2022-10-holograph/blob/main/contracts/enforcer/HolographERC20.sol#L171-L176 https://github.com/code-423n4/2022-10-holograph/blob/main/contracts/enforcer/PA1D.sol#L142-L143
Please visit the following link for more details on favoring bytes32 over string:
Empty constructor()
that is not used can be removed to save deployment gas. Here are some of the instances entailed:
https://github.com/code-423n4/2022-10-holograph/blob/main/src/HolographBridge.sol#L56 https://github.com/code-423n4/2022-10-holograph/blob/main/src/HolographOperator.sol#L134 https://github.com/code-423n4/2022-10-holograph/blob/main/contracts/HolographFactory.sol#L136 https://github.com/code-423n4/2022-10-holograph/blob/main/contracts/module/LayerZeroModule.sol#L151
Consider having the logic of a modifier embedded through an internal or private (if no contracts inheriting) function to reduce contract size if need be. For instance, the following instance of modifier may be rewritten as follows:
https://github.com/code-423n4/2022-10-holograph/blob/main/src/HolographBridge.sol#L48-L51
function _onlyOperator() private view { require(msg.sender == address(_operator()), "HOLOGRAPH: operator only call"); } modifier onlyOperator() { _onlyOperator(); _; }
The order of function will also have an impact on gas consumption. Because in smart contracts, there is a difference in the order of the functions. Each position will have an extra 22 gas. The order is dependent on method ID. So, if you rename the frequently accessed function to more early method ID, you can save gas cost. Please visit the following site for further information:
Before deploying your contract, activate the optimizer when compiling using “solc --optimize --bin sourceFile.sol”. By default, the optimizer will optimize the contract assuming it is called 200 times across its lifetime. If you want the initial contract deployment to be cheaper and the later function executions to be more expensive, set it to “ --optimize-runs=1”. Conversely, if you expect many transactions and do not care for higher deployment cost and output size, set “--optimize-runs” to a high number.
module.exports = { solidity: { version: "0.8.11", settings: { optimizer: { enabled: true, runs: 1000, }, }, }, };
Please visit the following site for further information:
https://docs.soliditylang.org/en/v0.5.4/using-the-compiler.html#using-the-commandline-compiler
Here's one example of instance on opcode comparison that delineates the gas saving mechanism:
for !=0 before optimization PUSH1 0x00 DUP2 EQ ISZERO PUSH1 [cont offset] JUMPI
after optimization DUP1 PUSH1 [revert offset] JUMPI
Disclaimer: There have been several bugs with security implications related to optimizations. For this reason, Solidity compiler optimizations are disabled by default, and it is unclear how many contracts in the wild actually use them. Therefore, it is unclear how well they are being tested and exercised. High-severity security issues due to optimization bugs have occurred in the past . A high-severity bug in the emscripten -generated solc-js compiler used by Truffle and Remix persisted until late 2018. The fix for this bug was not reported in the Solidity CHANGELOG. Another high-severity optimization bug resulting in incorrect bit shift results was patched in Solidity 0.5.6. Please measure the gas savings from optimizations, and carefully weigh them against the possibility of an optimization-related bug. Also, monitor the development and adoption of Solidity compiler optimizations to assess their maturity.
In the EVM, there is no opcode for non-strict inequalities (>=, <=) and two operations are performed (> + = or < + =). As an example, consider replacing >= with the strict counterpart > in the following line of code:
https://github.com/code-423n4/2022-10-holograph/blob/main/src/HolographOperator.sol#L1070
if (pod > _operatorPods.length - 1) {
Similarly, as an example, consider replacing <= with the strict counterpart < in the following line of code:
https://github.com/code-423n4/2022-10-holograph/blob/main/src/HolographOperator.sol#L764
require(current < amount + 1, "HOLOGRAPH: bond amount too small");
"Checked" math, which is default in ^0.8.0 is not free. The compiler will add some overflow checks, somehow similar to those implemented by SafeMath
. While it is reasonable to expect these checks to be less expensive than the current SafeMath
, one should keep in mind that these checks will increase the cost of "basic math operation" that were not previously covered. This particularly concerns variable increments in for loops. When no arithmetic overflow/underflow is going to happen, unchecked { ++i ;}
to use the previous wrapping behavior further saves gas just as in the for loop below as an example:
https://github.com/code-423n4/2022-10-holograph/blob/main/src/HolographOperator.sol#L682-L684
for (uint256 i = 0; i < length;) { operators[i] = _operatorPods[pod][index + i]; unchecked { ++i; } }
Instead of using the &&
operator in a single require statement to check multiple conditions, using multiple require statements with 1 condition per require statement will save 3 GAS per &&
. Here are some of the instances entailed:
https://github.com/code-423n4/2022-10-holograph/blob/main/src/HolographOperator.sol#L758 https://github.com/code-423n4/2022-10-holograph/blob/main/contracts/enforcer/HolographERC721.sol#L464-L470 https://github.com/code-423n4/2022-10-holograph/blob/main/contracts/enforcer/Holographer.sol#L166
||
Costs Less Gas Than Its Equivalent &&
Rule of thumb: (x && y)
is (!(!x || !y))
Even with the 10k Optimizer enabled: ||
, OR conditions cost less than their equivalent &&
, AND conditions.
As an example, the following code line may be rewritten as:
https://github.com/code-423n4/2022-10-holograph/blob/main/contracts/enforcer/HolographERC20.sol#L527
if (!(msg.sender == _holograph().getBridge() || msg.sender == _holograph().getOperator())) {
uint256
Can be Cheaper Than uint8
and Other Unsigned Integer Type of Smaller Bit SizeWhen dealing with function arguments or memory values, there is no inherent benefit because the compiler does not pack these values. Your contract’s gas usage may be higher because the EVM operates on 32 bytes at a time. Therefore, if the element is smaller than that, the EVM must use more operations in order to reduce the size of the element from 32 bytes to the desired size. The EVM needs to properly enforce the limits of this smaller type.
It is only more efficient when you can pack variables of uint8 into the same storage slot with other neighboring variables smaller than 32 bytes. Here are some of the instances entailed:
https://github.com/code-423n4/2022-10-holograph/blob/main/contracts/HolographFactory.sol#L323 https://github.com/code-423n4/2022-10-holograph/blob/main/contracts/HolographBridge.sol#L192 https://github.com/code-423n4/2022-10-holograph/blob/main/contracts/enforcer/HolographERC20.sol#L229
+=
generally costs 22 more gas than writing out the assigned equation explicitly. The amount of gas wasted can be quite sizable when repeatedly operated in a loop. As an example, the following line of code could be rewritten as:
https://github.com/code-423n4/2022-10-holograph/blob/main/contracts/HolographFactory.sol#L328
v = v + 27;
Similarly, the following code line should be refactored as:
https://github.com/code-423n4/2022-10-holograph/blob/main/contracts/enforcer/HolographERC20.sol#L633
_totalSupply = _totalSupply - amount;
Consider marking functions with access control as payable
. This will save 20 gas on each call by their respective permissible callers for not needing to have the compiler check for msg.value
. Here are some of the instances entailed:
https://github.com/code-423n4/2022-10-holograph/blob/main/contracts/module/LayerZeroModule.sol#L320-L324 https://github.com/code-423n4/2022-10-holograph/blob/main/contracts/module/LayerZeroModule.sol#L340-L344 https://github.com/code-423n4/2022-10-holograph/blob/main/contracts/module/LayerZeroModule.sol#L360-L364 https://github.com/code-423n4/2022-10-holograph/blob/main/contracts/module/LayerZeroModule.sol#L380-L384 https://github.com/code-423n4/2022-10-holograph/blob/main/contracts/module/LayerZeroModule.sol#L441-L445 https://github.com/code-423n4/2022-10-holograph/blob/main/contracts/module/LayerZeroModule.sol#L470-L474
Report contents changed: ## calldata and memory When running a function we could pass the function parameters as calldata or memory for variables such as strings, bytes, structs, arrays etc. If we are not modifying the passed parameter, we should pass it as calldata because calldata is more gas efficient than memory.
Calldata is a non-modifiable, non-persistent area where function arguments are stored, and behaves mostly like memory. However, it alleviates the compiler from the abi.decode()
step that copies each index of the calldata to the memory index, bypassing the cost of 60 gas on each iteration. Here are some of the instances entailed:
https://github.com/code-423n4/2022-10-holograph/blob/main/contracts/HolographFactory.sol#L193-L194 https://github.com/code-423n4/2022-10-holograph/blob/main/contracts/enforcer/PA1D.sol#L298
A storage pointer is cheaper since copying a state struct in memory would incur as many SLOADs and MSTOREs as there are slots. In another words, this causes all fields of the struct/array to be read from storage, incurring a Gcoldsload (2100 gas) for each field of the struct/array, and then further incurring an additional MLOAD rather than a cheap stack read. As such, declaring the variable with the storage keyword and caching any fields that need to be re-read in stack variables will be much cheaper, involving only Gcoldsload for all associated field reads. Read the whole struct/array into a memory variable only when it is being returned by the function, passed into a function that requires memory, or if the array/struct is being read from another memory array/struct. Here are some of the instances entailed:
https://github.com/code-423n4/2022-10-holograph/blob/main/contracts/HolographOperator.sol#L325
if ... else
Using ternary operator instead of the if else statement saves gas. For instance the following code block may be rewritten as:
https://github.com/code-423n4/2022-10-holograph/blob/main/contracts/enforcer/PA1D.sol#L560-L564
_getReceiver(tokenId) == address(0) ? { bps[0] = _getDefaultBp(); } : { bps[0] = _getBp(tokenId); }
Functions not internally called may have its visibility changed to external to save gas. Here are some of the instances entailed:
https://github.com/code-423n4/2022-10-holograph/blob/main/contracts/enforcer/PA1D.sol#L471 https://github.com/code-423n4/2022-10-holograph/blob/main/contracts/enforcer/PA1D.sol#L497 https://github.com/code-423n4/2022-10-holograph/blob/main/contracts/enforcer/PA1D.sol#L507 https://github.com/code-423n4/2022-10-holograph/blob/main/contracts/enforcer/PA1D.sol#L517