Holograph contest - RaymondFam's results

Omnichain protocol for deploying, minting, & bridging NFTs between blockchains.

General Information

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

Holograph

Findings Distribution

Researcher Performance

Rank: 18/144

Findings: 2

Award: $771.29

QA:
grade-a
Gas:
grade-a

🌟 Selected for report: 0

🚀 Solo Findings: 0

constant Variables Should be Capitalized

Constants 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

OpenZeppelin's Initializable

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.

Inadequately Commented Assembly Block

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

Use of ecrecover is Susceptible to Signature Malleability

The 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.

Non-Assembly Method Alternative

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

Lack of Events for Critical Operations

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

Add a Timelock to Critical Parameter Change

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

Contract Owner Has Too Many Privileges

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:

  1. splitting privileges (e.g. via the multisig option) to ensure that no one address has excessive ownership of the system,
  2. clearly documenting the functions and implementations the owner can change,
  3. documenting the risks associated with privileged users and single points of failure, and
  4. ensuring that users are aware of all the risks associated with the system.

Function Calls in Loop Could Lead to Denial of Service

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.

Commented Code

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

Expression for Calculated Values

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.

Require Statement at the Top of Code Block

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

Use delete to Clear Variables

delete 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 Unreliable

The 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

Inadequate NatSpec

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

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

Not Completely Using OpenZeppelin Contracts

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

Minimization of Truncation

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 Too Long

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

No Withdraw Function for ETH

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

Use Fixed-size 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:

https://ethereum.stackexchange.com/questions/3795/why-do-solidity-examples-use-bytes32-type-instead-of-string

Unused Constructor

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

Private/Internal Function Embedded Modifier Reduces Contract Size

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(); _; }

Function Order Affects Gas Consumption

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:

https://medium.com/joyso/solidity-how-does-function-name-affect-gas-consumption-in-smart-contract-47d270d8ac92

Activate the Optimizer

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.

Non-strict inequalities are cheaper than strict ones

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");

Unchecked SafeMath Saves Gas

"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; } }

Split Require Statements Using &&

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 Size

When 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

+= and -= Costs More Gas

+= 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;

Payable Access Control Functions Costs Less Gas

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

Use Storage Instead of Memory for Structs/Arrays

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

Ternary Over 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); }

External costs less gas than public visibility

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

AuditHub

A portfolio for auditors, a security profile for protocols, a hub for web3 security.

Built bymalatrax © 2024

Auditors

Browse

Contests

Browse

Get in touch

ContactTwitter