Holograph contest - B2'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: 46/144

Findings: 2

Award: $82.02

QA:
grade-b
Gas:
grade-b

🌟 Selected for report: 0

🚀 Solo Findings: 0

UPGRADEABLE CONTRACT IS MISSING A __GAP[50] STORAGE VARIABLE TO ALLOW FOR NEW STORAGE VARIABLES IN LATER VERSIONS

While some contracts may not currently be sub-classed, adding the variable now protects against forgetting to add it in the future.

require()/revert() STATEMENTS SHOULD HAVE DESCRIPTIVE REASON STRINGS

OPEN TODOS

Code architecture, incentives, and error handling/reporting questions/issues should be resolved before deployment.

block.timestamp and block.number are not be used a source or reference of time. Instead use oracle or verifiable random numbers

MISSING EVENT AND OR TIMELOCK FOR CRITICAL PARAMETER CHANGE

Events help non-contract tools to track changes, and events prevent users from being surprised by changes.

Use of ecrecover() is deprecated. Consider adding checks for signature malleability.

Use OpenZeppelin’s ECDSA contract rather than calling ecrecover() directly.

Event is missing indexed fields

Index event fields make the field more quickly accessible to off-chain tools that parse events. However, note that each index field costs extra gas during emission, so it’s not necessarily best to index the maximum allowed per event (three fields). Each event should use three indexed fields if there are three or more fields, and gas usage is not particularly of concern for the events in question. If there are fewer than three fields, all of the fields should be indexed.

Set garbage value in mapping for deleting that

If there is a mapping data structure present inside struct, then deleting the struct doesn't delete the mapping. Instead one should use lock to lock that data structure from further use.

Missing initializermodifier on constructor

OpenZeppelin recommends that the initializer modifier be applied to constructors in order to avoid potential griefs, social engineering, or exploits. Ensure that the modifier is applied to the implementation contract. If the default constructor is currently being used, it should be changed to be an explicit one with the modifier applied.

All the contracts present inside scope

Using abi.decode(arg1,arg2) , is not safe that if arg1 or arg2 is null, or is user-controllable(returning null).

When using abi.decode(arg1,arg2) , one should keep in mind that if arg1 or arg2 is null, or is user-controllable(returning null), then the abi.decode() will revert. If such cases are not handled, then it may lead to revert the whole function where abi.decode was, and may lead to DOS-kind of situation, based on the logic of the function

Multiple mappings of same key can be combined into a single mapping to a struct, where appropriate

Saves a storage slot for the mapping. Depending on the circumstances and sizes of types, can avoid a Gsset (20000 gas) per mapping combined. Reads and subsequent writes can also be cheaper when a function requires both values and they both fit in the same storage slot

UNUSED NAMED RETURNS

Using both named returns and a return statement isn’t necessary. Removing one of those can improve code clarity:

Use calldata instead of memory for external functions where the function argument is read-only

The memory keyword in a function argument causes the underlying code to copy the argument into memory. Calldata causes the code to read the transaction without copying it directly, ultimately saves gases.

Splitting require() statements that use && saves gas

Instead of using the && operator in a single require statement to check multiple conditions, I suggest using multiple require statements with 1 condition per require statement (saving 3 gas per &):

internal functions not called by the contract should be removed to save deployment gas

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

internal functions only called once can be inlined to save gas

Not inlining costs 20 to 40 gas because of two extra JUMP instructions and additional stack operations needed for function calls.

Inline a modifier that's only used once

As onlyOperator() modifier is only used once in this contract (in function bridgeInRequest()), it should get inlined to save gas without losing readability:

Don’t use require() functions in loop .

It is better to break than to revert

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