Holograph contest - ch0bu'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: 45/144

Findings: 2

Award: $82.02

QA:
grade-b
Gas:
grade-b

🌟 Selected for report: 0

🚀 Solo Findings: 0

NON-CRITICAL

1. Use safeTransfer/safeTransferFrom consistently instead of transfer/transferFrom

It is good to add a require() statement that checks the return value of token transfers or to use something like OpenZeppelin’s safeTransfer/safeTransferFrom unless one is sure the given token reverts in case of a failure. Failure to do so will cause silent failures of transfers and affect token accounting in contract.

Reference: This similar medium-severity finding from Consensys Diligence Audit of Fei Protocol: https://consensys.net/diligence/audits/2021/01/fei-protocol/#unchecked-return-value-for-iweth-transfer-call

400 _utilityToken().transfer(job.operator, leftovers); 596 payable(hToken).transfer(hlgFee); 839 require(_utilityToken().transferFrom(msg.sender, address(this), amount), "HOLOGRAPH: token transfer failed"); 889 require(_utilityToken().transferFrom(msg.sender, address(this), amount), "HOLOGRAPH: token transfer failed"); 932 require(_utilityToken().transfer(recipient, amount), "HOLOGRAPH: token transfer failed");

https://github.com/code-423n4/2022-10-holograph/blob/main/contracts/HolographOperator.sol

396 addresses[i].transfer(sending); 416 require(erc20.transfer(addresses[i], sending), "PA1D: Couldn't transfer token"); 439 require(erc20.transfer(addresses[i], sending), "PA1D: Couldn't transfer token");

https://github.com/code-423n4/2022-10-holograph/blob/main/contracts/enforcer/PA1D.sol

2. Open TODOs

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

701 // TODO: move the bit-shifting around to have it be sequential

https://github.com/code-423n4/2022-10-holograph/blob/main/contracts/HolographOperator.sol

3. Consider adding checks for signature malleability

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

333 return (ecrecover(keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", hash)), v, r, s) == signer || 334 ecrecover(hash, v, r, s) == signer);

https://github.com/code-423n4/2022-10-holograph/blob/main/contracts/HolographFactory.sol

4. NatSpec is incomplete

Check here for NatSpec rules: https://docs.soliditylang.org/en/develop/natspec-format.html

missing @return 162 function init(bytes memory initPayload) external override returns (bytes4) { 301 ) external returns (string memory revertReason) { 402 function getFactory() external view returns (address factory) { 462 function getHolograph() external view returns (address holograph) { 482 function getJobNonce() external view returns (uint256 jobNonce) { 492 function getOperator() external view returns (address operator) { 512 function getRegistry() external view returns (address registry) { 531 function _factory() private view returns (HolographFactoryInterface factory) { 540 function _holograph() private view returns (HolographInterface holograph) { 549 function _jobNonce() private returns (uint256 jobNonce) { 559 function _operator() private view returns (HolographOperatorInterface operator) { 568 function _registry() private view returns (HolographRegistryInterface registry) {

https://github.com/code-423n4/2022-10-holograph/blob/main/contracts/HolographBridge.sol

missing @return 240 function init(bytes memory initPayload) external override returns (bytes4) { 717 function getTotalPods() external view returns (uint256 totalPods) { 939 function getBridge() external view returns (address bridge) { 959 function getHolograph() external view returns (address holograph) { 979 function getInterfaces() external view returns (address interfaces) { 999 function getMessagingModule() external view returns (address messagingModule) { 1019 function getRegistry() external view returns (address registry) { 1039 function getUtilityToken() external view returns (address utilityToken) { 1058 function _bridge() private view returns (address bridge) { 1112 function _jobNonce() private returns (uint256 jobNonce) { 1160 function _getBaseBondAmount(uint256 pod) private view returns (uint256) { 1167 function _getCurrentBondAmount(uint256 pod) private view returns (uint256) { 1189 ) private view returns (uint256) { 1198 function _isContract(address contractAddress) private view returns (bool) { missing @param bridgeInRequestPayload 484 function crossChainMessage(bytes calldata bridgeInRequestPayload) external payable {

https://github.com/code-423n4/2022-10-holograph/blob/main/contracts/HolographOperator.sol

missing @return 143 function init(bytes memory initPayload) external override returns (bytes4) { 270 function getHolograph() external view returns (address holograph) { 290 function getRegistry() external view returns (address registry) { 309 function _isContract(address contractAddress) private view returns (bool) { 326 ) private pure returns (bool) {

https://github.com/code-423n4/2022-10-holograph/blob/main/contracts/HolographFactory.sol

missing @return 158 function init(bytes memory initPayload) external override returns (bytes4) { 256 ) external view returns (uint256 hlgFee, uint256 msgFee) { 281 ) external view returns (uint256 hlgFee) { 299 returns (uint128 dstPriceRatio, uint128 dstGasPriceInWei) 310 function getBridge() external view returns (address bridge) { 330 function getInterfaces() external view returns (address interfaces) { 350 function getLZEndpoint() external view returns (address lZEndpoint) { 370 function getOperator() external view returns (address operator) { 389 function _bridge() private view returns (address bridge) { 431 function getBaseGas() external view returns (uint256 baseGas) { 450 function _baseGas() private view returns (uint256 baseGas) { 460 function getGasPerByte() external view returns (uint256 gasPerByte) { 479 function _gasPerByte() private view returns (uint256 gasPerByte) { missing @param for all variables 227 function send( 251 function getMessageFee( 277 function getHlgFee( 296 function _getPricing(LayerZeroOverrides lz, uint16 lzDestChain)

https://github.com/code-423n4/2022-10-holograph/blob/main/contracts/module/LayerZeroModule.sol

missing @title missing @author missing @return 147 function init(bytes memory initPayload) external override returns (bytes4) { 174 function getDeploymentBlock() external view returns (address holograph) { 183 function getHolograph() external view returns (address holograph) { 192 function getHolographEnforcer() public view returns (address) { 205 function getOriginChain() external view returns (uint32 originChain) { 214 function getSourceContract() external view returns (address sourceContract) {

https://github.com/code-423n4/2022-10-holograph/blob/main/contracts/enforcer/Holographer.sol

missing @return 173 function init(bytes memory initPayload) external override returns (bytes4) { 185 function initPA1D(bytes memory initPayload) external returns (bytes4) { 298 function _getPayoutAddresses() private view returns (address payable[] memory addresses) { 332 function _getPayoutBps() private view returns (uint256[] memory bps) { missing @param initPayload 185 function initPA1D(bytes memory initPayload) external returns (bytes4) { missing @param addresses 316 function _setPayoutAddresses(address payable[] memory addresses) private { missing @param bps 349 function _setPayoutBps(uint256[] memory bps) private { missing @param tokenName, @return 365 function _getTokenAddress(string memory tokenName) private view returns (address tokenAddress) { missing @param tokenName, @param tokenAddress 372 function _setTokenAddress(string memory tokenName, address tokenAddress) private { missing @param tokenId, @param value, @return, @return 549 function royaltyInfo(uint256 tokenId, uint256 value) public view returns (address, uint256) { missing @param tokenId, @return 558 function getFeeBps(uint256 tokenId) public view returns (uint256[] memory) { missing @param tokenId, @return 569 function getFeeRecipients(uint256 tokenId) public view returns (address payable[] memory) { missing @param tokenId, @return, @return 590 function getRoyalties(uint256 tokenId) public view returns (address payable[] memory, uint256[] memory) { missing @param tokenId, @return, @return 604 function getFees(uint256 tokenId) public view returns (address payable[] memory, uint256[] memory) {

https://github.com/code-423n4/2022-10-holograph/blob/main/contracts/enforcer/PA1D.sol

missing @return 218 function init(bytes memory initPayload) external override returns (bytes4) { 306 function DOMAIN_SEPARATOR() public view returns (bytes32) { 310 function name() public view returns (string memory) { 318 function symbol() public view returns (string memory) { 322 function totalSupply() public view returns (uint256) { missing @param interfaceId, @return 282 function supportsInterface(bytes4 interfaceId) external view returns (bool) { missing @param account, @param spender, @ return 297 function allowance(address account, address spender) public view returns (uint256) { missing @param account, @ return 301 function balanceOf(address account) public view returns (uint256) { missing @param account, @ return 314 function nonces(address account) public view returns (uint256) { missing @param spender, @param amount, @ return 326 function approve(address spender, uint256 amount) public returns (bool) { missing @param account, @param amount, @ return 347 function burnFrom(address account, uint256 amount) public returns (bool) { missing @param spender, @param subtractedValue, @ return 363 function decreaseAllowance(address spender, uint256 subtractedValue) public returns (bool) { missing @param fromChain, @param payload, @ return 380 function bridgeIn(uint32 fromChain, bytes calldata payload) external onlyBridge returns (bytes4) { missing @param for all variables 392 function bridgeOut( 415 function holographBridgeMint(address to, uint256 amount) external onlyBridge returns (bytes4) { 420 function increaseAllowance(address spender, uint256 addedValue) public returns (bool) { 439 function onERC20Received( 460 function permit( 492 function safeTransfer(address recipient, uint256 amount) public returns (bool) { 496 function safeTransfer( 512 function safeTransferFrom( 520 function safeTransferFrom( 549 function sourceBurn(address from, uint256 amount) external onlySource { 563 function sourceMintBatch(address[] calldata wallets, uint256[] calldata amounts) external onlySource { 572 unction sourceTransfer( 580 function transfer(address recipient, uint256 amount) public returns (bool) { 591 function transferFrom( 615 function _approve( 626 function _burn(address account, uint256 amount) private { 637 function _checkOnERC20Received( 690 function _transfer( 711 function _useNonce(address account) private returns (uint256 current) { 736 function _interfaces() private view returns (address) { 740 function owner() public view override returns (address) { 748 function _holograph() private view returns (HolographInterface holograph) { 754 function _isEventRegistered(HolographERC20Event _eventName) private view returns (bool) {

https://github.com/code-423n4/2022-10-holograph/blob/main/contracts/enforcer/HolographERC20.sol

missing @title missing @author

https://github.com/code-423n4/2022-10-holograph/blob/main/contracts/abstract/ERC721H.sol

missing @title missing @author

https://github.com/code-423n4/2022-10-holograph/blob/main/contracts/abstract/ERC20H.sol

5. Missing event for critical parameter change

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

452 function setFactory(address factory) external onlyAdmin { 472 function setHolograph(address holograph) external onlyAdmin { 502 function setOperator(address operator) external onlyAdmin {

https://github.com/code-423n4/2022-10-holograph/blob/main/contracts/HolographBridge.sol

949 function setBridge(address bridge) external onlyAdmin { 969 function setHolograph(address holograph) external onlyAdmin { 989 function setInterfaces(address interfaces) external onlyAdmin { 1009 function setMessagingModule(address messagingModule) external onlyAdmin { 1029 function setRegistry(address registry) external onlyAdmin { 1049 function setUtilityToken(address utilityToken) external onlyAdmin {

https://github.com/code-423n4/2022-10-holograph/blob/main/contracts/HolographOperator.sol

280 function setHolograph(address holograph) external onlyAdmin { 300 function setRegistry(address registry) external onlyAdmin {

https://github.com/code-423n4/2022-10-holograph/blob/main/contracts/HolographFactory.sol

320 function setBridge(address bridge) external onlyAdmin { 340 function setInterfaces(address interfaces) external onlyAdmin { 360 function setLZEndpoint(address lZEndpoint) external onlyAdmin { 380 function setOperator(address operator) external onlyAdmin { 470 function setGasPerByte(uint256 gasPerByte) external onlyAdmin {

https://github.com/code-423n4/2022-10-holograph/blob/main/contracts/module/LayerZeroModule.sol

6. Remove commented out code

438 //// _bondedOperators[msg.sender] += reward;

https://github.com/code-423n4/2022-10-holograph/blob/main/contracts/HolographOperator.sol

397 // sent = sent + sending; 417 // sent = sent + sending; 440 // sent = sent + sending; ... 580 // struct Part { 581 // address payable account; 582 // uint96 value; 583 // } 584 // function getRoyalties(uint256 tokenId) public view returns (Part[] memory) { 585 // return royalties[id]; 586 // }

https://github.com/code-423n4/2022-10-holograph/blob/main/contracts/enforcer/PA1D.sol

527 // function sourceMintBatch(address to, uint224[] calldata tokenIds) external onlySource { 528 // require(tokenIds.length < 1000, "ERC721: max batch size is 1000"); 529 // uint32 chain = _chain(); 530 // uint256 token; 531 // for (uint256 i = 0; i < tokenIds.length; i++) { 532 // require(!_burnedTokens[token], "ERC721: can't mint burned token"); 533 // token = uint256(bytes32(abi.encodePacked(chain, tokenIds[i]))); 534 // require(!_burnedTokens[token], "ERC721: can't mint burned token"); 535 // _mint(to, token); 536 // } 537 // } ... 542 // function sourceMintBatch(address[] calldata wallets, uint224[] calldata tokenIds) external onlySource { 543 // require(wallets.length == tokenIds.length, "ERC721: array length missmatch"); 544 // require(tokenIds.length < 1000, "ERC721: max batch size is 1000"); 545 // uint32 chain = _chain(); 546 // uint256 token; 547 // for (uint256 i = 0; i < tokenIds.length; i++) { 548 // token = uint256(bytes32(abi.encodePacked(chain, tokenIds[i]))); 549 // require(!_burnedTokens[token], "ERC721: can't mint burned token"); 550 // _mint(wallets[i], token); 551 // } 552 // } ... 557 // function sourceMintBatchIncremental( 558 // address to, 559 // uint224 startingTokenId, 560 // uint256 length 561 // ) external onlySource { 562 // uint32 chain = _chain(); 563 // uint256 token; 564 // for (uint256 i = 0; i < length; i++) { 565 // token = uint256(bytes32(abi.encodePacked(chain, startingTokenId))); 566 // require(!_burnedTokens[token], "ERC721: can't mint burned token"); 567 // _mint(to, token); 568 // startingTokenId++; 569 // } 570 // }

https://github.com/code-423n4/2022-10-holograph/blob/main/contracts/enforcer/HolographERC721.sol

7. Use scientific notation (e.g. 1e18) rather than exponentiation (e.g. 10**18)

Scientific notation should be used for better code readability

256 _baseBondAmount = 100 * (10**18); // one single token unit * 100

https://github.com/code-423n4/2022-10-holograph/blob/main/contracts/HolographOperator.sol

274 return (((gasPrice * (gasLimit + (gasLimit / 10))) * dstPriceRatio) / (10**10), nativeFee); 293 return ((gasPrice * (gasLimit + (gasLimit / 10))) * dstPriceRatio) / (10**10);

https://github.com/code-423n4/2022-10-holograph/blob/main/contracts/module/LayerZeroModule.sol

8. public functions not called by the contract should be declared external instead

Contracts are allowed to override their parents’ functions and change the visibility from external to public and can save gas by doing so

472 function configurePayouts(address payable[] memory addresses, uint256[] memory bps) public onlyOwner { 488 function getPayoutInfo() public view returns (address payable[] memory addresses, uint256[] memory bps) { 497 function getEthPayout() public { 507 function getTokenPayout(address tokenAddress) public { 517 function getTokensPayout(address[] memory tokenAddresses) public { 549 function royaltyInfo(uint256 tokenId, uint256 value) public view returns (address, uint256) { 558 function getFeeBps(uint256 tokenId) public view returns (uint256[] memory) { 569 function getFeeRecipients(uint256 tokenId) public view returns (address payable[] memory) { 590 function getRoyalties(uint256 tokenId) public view returns (address payable[] memory, uint256[] memory) { 604 function getFees(uint256 tokenId) public view returns (address payable[] memory, uint256[] memory) { 621 function tokenCreator( 634 function calculateRoyaltyFee( 649 function marketContract() public view returns (address) { 655 function tokenCreators(uint256 tokenId) public view returns (address) { 665 function bidSharesForToken(uint256 tokenId) public view returns (ZoraBidShares memory bidShares) { 683 function getTokenAddress(string memory tokenName) public view returns (address) {

https://github.com/code-423n4/2022-10-holograph/blob/main/contracts/enforcer/PA1D.sol

306 function DOMAIN_SEPARATOR() public view returns (bytes32) {

https://github.com/code-423n4/2022-10-holograph/blob/main/contracts/enforcer/HolographERC20.sol

9. require()/revert() statements should have descriptive reason strings

328 require(SourceERC20().beforeApprove(msg.sender, spender, amount)); 332 require(SourceERC20().afterApprove(msg.sender, spender, amount)); 339 require(SourceERC20().beforeBurn(msg.sender, amount)); 343 require(SourceERC20().afterBurn(msg.sender, amount)); 354 require(SourceERC20().beforeBurn(account, amount)); 358 require(SourceERC20().afterBurn(account, amount)); 371 require(SourceERC20().beforeApprove(msg.sender, spender, newAllowance)); 375 require(SourceERC20().afterApprove(msg.sender, spender, newAllowance)); 430 require(SourceERC20().beforeApprove(msg.sender, spender, newAllowance)); 434 require(SourceERC20().afterApprove(msg.sender, spender, newAllowance)); 447 require(SourceERC20().beforeOnERC20Received(account, sender, address(this), amount, data)); 455 require(SourceERC20().afterOnERC20Received(account, sender, address(this), amount, data)); 484 require(SourceERC20().beforeApprove(account, spender, amount)); 488 require(SourceERC20().afterApprove(account, spender, amount)); 502 require(SourceERC20().beforeSafeTransfer(msg.sender, recipient, amount, data)); 507 require(SourceERC20().afterSafeTransfer(msg.sender, recipient, amount, data)); 536 require(SourceERC20().beforeSafeTransfer(account, recipient, amount, data)); 541 require(SourceERC20().afterSafeTransfer(account, recipient, amount, data)); 582 require(SourceERC20().beforeTransfer(msg.sender, recipient, amount)); 586 require(SourceERC20().afterTransfer(msg.sender, recipient, amount)); 606 require(SourceERC20().beforeTransfer(account, recipient, amount)); 610 require(SourceERC20().afterTransfer(account, recipient, amount));

https://github.com/code-423n4/2022-10-holograph/blob/main/contracts/enforcer/HolographERC20.sol

10. Not using the named return variables anywhere in the function is confusing

Consider changing the variable to be an unnamed one

301 ) external returns (string memory revertReason) {

https://github.com/code-423n4/2022-10-holograph/blob/main/contracts/HolographBridge.sol

717 function getTotalPods() external view returns (uint256 totalPods) { 804 function getBondedAmount(address operator) external view returns (uint256 amount) { 814 function getBondedPod(address operator) external view returns (uint256 pod) {

https://github.com/code-423n4/2022-10-holograph/blob/main/contracts/HolographOperator.sol

256 ) external view returns (uint256 hlgFee, uint256 msgFee) { 281 ) external view returns (uint256 hlgFee) { 299 returns (uint128 dstPriceRatio, uint128 dstGasPriceInWei)

https://github.com/code-423n4/2022-10-holograph/blob/main/contracts/module/LayerZeroModule.sol

LOW

1. Unused/empty receive()/fallback() function

If the intention is for the Ether to be used, the function should call another function, otherwise it should revert (e.g. require(msg.sender == address(weth))). Having no access control on the function means that someone may send Ether to the contract, and have no way to get anything back out, which is a loss of funds

1209 receive() external payable {}

https://github.com/code-423n4/2022-10-holograph/blob/main/contracts/HolographOperator.sol

223 receive() external payable {}

https://github.com/code-423n4/2022-10-holograph/blob/main/contracts/enforcer/Holographer.sol

251 receive() external payable {}

https://github.com/code-423n4/2022-10-holograph/blob/main/contracts/enforcer/HolographERC20.sol

962 receive() external payable {}

https://github.com/code-423n4/2022-10-holograph/blob/main/contracts/enforcer/HolographERC721.sol

212 receive() external payable {}

https://github.com/code-423n4/2022-10-holograph/blob/main/contracts/abstract/ERC721H.sol

212 receive() external payable {}

https://github.com/code-423n4/2022-10-holograph/blob/main/contracts/abstract/ERC20H.sol

1. Increments can be unchecked

In Solidity 0.8+, there’s a default overflow check on unsigned integers. It’s possible to uncheck this in for-loops and save some gas at each iteration, but at the cost of some code readability, as this uncheck cannot be made inline. This saves 30-40 gas PER LOOP.

781 for (uint256 i = 0; i < length; i++) { 871 for (uint256 i = _operatorPods.length; i <= pod; i++) {

https://github.com/code-423n4/2022-10-holograph/blob/main/contracts/HolographOperator.sol

307 for (uint256 i = 0; i < length; i++) { 323 for (uint256 i = 0; i < length; i++) { 340 for (uint256 i = 0; i < length; i++) { 356 for (uint256 i = 0; i < length; i++) { 394 for (uint256 i = 0; i < length; i++) { 414 for (uint256 i = 0; i < length; i++) { 432 for (uint256 t = 0; t < tokenAddresses.length; t++) { 437 for (uint256 i = 0; i < addresses.length; i++) { 454 for (uint256 i = 0; i < addresses.length; i++) { 474 for (uint256 i = 0; i < addresses.length; i++) {

https://github.com/code-423n4/2022-10-holograph/blob/main/contracts/enforcer/PA1D.sol

357 for (uint256 i = 0; i < length; i++) { 716 for (uint256 i = 0; i < length; i++) {

https://github.com/code-423n4/2022-10-holograph/blob/main/contracts/enforcer/HolographERC721.sol

564 for (uint256 i = 0; i < wallets.length; i++) {

https://github.com/code-423n4/2022-10-holograph/blob/main/contracts/enforcer/HolographERC20.sol

2. require()/revert() strings longer than 32 bytes cost extra gas

Each extra memory word of bytes past the original 32 incurs an MSTORE which costs 3 gas

411 require(balance > 10000, "PA1D: Not enough tokens to transfer"); 435 require(balance > 10000, "PA1D: Not enough tokens to transfer");

https://github.com/code-423n4/2022-10-holograph/blob/main/contracts/enforcer/PA1D.sol

3. Splitting require() statements that use && saves gas

Instead of using the && operator in a single require statement to check multiple conditions,use multiple require statements with 1 condition per require statement.

See this issue which describes the fact that there is a larger deployment gas cost, but with enough runtime calls, the change ends up being cheaper

857 require(_bondedOperators[operator] == 0 && _bondedAmounts[operator] == 0, "HOLOGRAPH: operator is bonded");

https://github.com/code-423n4/2022-10-holograph/blob/main/contracts/HolographOperator.sol

166 require(success && selector == InitializableInterface.init.selector, "initialization failed");

https://github.com/code-423n4/2022-10-holograph/blob/main/contracts/enforcer/Holographer.sol

263 require(success && selector == InitializableInterface.init.selector, "ERC721: coud not init PA1D"); ... 464 require( 465 (ERC165(to).supportsInterface(ERC165.supportsInterface.selector) && 466 ERC165(to).supportsInterface(ERC721TokenReceiver.onERC721Received.selector) && 467 ERC721TokenReceiver(to).onERC721Received(address(this), from, tokenId, data) == 468 ERC721TokenReceiver.onERC721Received.selector), 469 "ERC721: onERC721Received fail" 470 );

https://github.com/code-423n4/2022-10-holograph/blob/main/contracts/enforcer/HolographERC721.sol

4. Usage of uint/int smaller than 32 bytes (256 bits) incurs overhead

When using elements that are smaller than 32 bytes, your contract’s gas usage may be higher. This is 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.

5. Empty blocks should be removed or emit something

The code should be refactored such that they no longer exist, or the block should do something useful, such as emitting an event or reverting. If the contract is meant to be extended, the contract should be abstract and the function signatures be added without any default implementation. If the block is an empty if-statement block to avoid doing subsequent checks in the else-if/else conditions, the else-if/else conditions should be nested under the negation of the if-statement, because they involve different classes of checks, which may lead to the introduction of errors when the code is later modified (if(x){}else if(y){...}else{...} => if(!x){if(y){...}else{...}}). Empty receive()/fallback() payable functions that are not used, can be removed to save deployment gas.

155 constructor() {}

https://github.com/code-423n4/2022-10-holograph/blob/main/contracts/HolographBridge.sol

233 constructor() {} 1209 receive() external payable {}

https://github.com/code-423n4/2022-10-holograph/blob/main/contracts/HolographOperator.sol

136 constructor() {}

https://github.com/code-423n4/2022-10-holograph/blob/main/contracts/HolographFactory.sol

151 constructor() {}

https://github.com/code-423n4/2022-10-holograph/blob/main/contracts/module/LayerZeroModule.sol

149 constructor() {} 223 receive() external payable {}

https://github.com/code-423n4/2022-10-holograph/blob/main/contracts/enforcer/Holographer.sol

166 constructor() {}

https://github.com/code-423n4/2022-10-holograph/blob/main/contracts/enforcer/PA1D.sol

231 constructor() {} 962 receive() external payable {}

https://github.com/code-423n4/2022-10-holograph/blob/main/contracts/enforcer/HolographERC721.sol

211 constructor() {} 251 receive() external payable {}

https://github.com/code-423n4/2022-10-holograph/blob/main/contracts/enforcer/HolographERC20.sol

133 constructor() {} 212 receive() external payable {}

https://github.com/code-423n4/2022-10-holograph/blob/main/contracts/abstract/ERC721H.sol

133 constructor() {} 212 receive() external payable {}

https://github.com/code-423n4/2022-10-holograph/blob/main/contracts/abstract/ERC20H.sol

6. Not using the named return variables when a function returns, wastes deployment gas

718 return _operatorPods.length; 729 return _operatorPods[pod - 1].length; 1191 return (random + uint256(blockhash(block.number - n))) % podSize; 1203 return (codehash != 0x0 && codehash != 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470);

https://github.com/code-423n4/2022-10-holograph/blob/main/contracts/HolographOperator.sol

152 return InitializableInterface.init.selector; 169 return Holographable.bridgeIn.selector; 314 return (codehash != 0x0 && codehash != 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470); 333 return (ecrecover(keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", hash)), v, r, s) == signer ||

https://github.com/code-423n4/2022-10-holograph/blob/main/contracts/HolographFactory.sol

274 return (((gasPrice * (gasLimit + (gasLimit / 10))) * dstPriceRatio) / (10**10), nativeFee); 293 return ((gasPrice * (gasLimit + (gasLimit / 10))) * dstPriceRatio) / (10**10);

https://github.com/code-423n4/2022-10-holograph/blob/main/contracts/module/LayerZeroModule.sol

206 return (msg.sender == getOwner() || 551 return (_getDefaultReceiver(), (_getDefaultBp() * value) / 10000); 553 return (_getReceiver(tokenId), (_getBp(tokenId) * value) / 10000); 641 return (_getDefaultBp() * amount) / 10000; 643 return (_getBp(tokenId) * amount) / 10000;

https://github.com/code-423n4/2022-10-holograph/blob/main/contracts/enforcer/PA1D.sol

328 return sourceContract.tokenURI(tokenId); 337 return _ownedTokens[wallet]; 521 return uint256(bytes32(abi.encodePacked(_chain(), uint224(0)))); 653 return 0; 678 return _operatorApprovals[wallet][operator]; 908 return (spender == tokenOwner || _tokenApprovals[tokenId] == spender || _operatorApprovals[tokenOwner][spender]); 916 return (codehash != 0x0 && codehash != 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470); 1003 return ((_eventConfig >> uint256(_eventName)) & uint256(1) == 1 ? true : false);

https://github.com/code-423n4/2022-10-holograph/blob/main/contracts/enforcer/HolographERC721.sol

262 returndatacopy(0, 0, returndatasize()) 298 return _allowances[account][spender]; 302 return _balances[account]; 315 return _nonces[account]; 334 return true; 360 return true; 377 return true; 409 return (Holographable.bridgeOut.selector, abi.encode(from, to, amount, data)); 417 return HolographERC20Interface.holographBridgeMint.selector; 436 return true; 493 return safeTransfer(recipient, amount, ""); 517 return safeTransferFrom(account, recipient, amount, ""); 543 return true; 721 return (codehash != 0x0 && codehash != 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470); 755 return ((_eventConfig >> uint256(_eventName)) & uint256(1) == 1 ? true : false);

https://github.com/code-423n4/2022-10-holograph/blob/main/contracts/enforcer/HolographERC20.sol

153 return InitializableInterface.init.selector; 175 return false;

https://github.com/code-423n4/2022-10-holograph/blob/main/contracts/abstract/ERC721H.sol

153 return InitializableInterface.init.selector; 175 return false;

https://github.com/code-423n4/2022-10-holograph/blob/main/contracts/abstract/ERC20H.sol

7. Bytes constants are more efficient than string constants

From the Solidity doc:

  • If you can limit the length to a certain number of bytes, always use one of bytes1 to bytes32 because they are much cheaper.

Why do Solidity examples use bytes32 type instead of string?

  • bytes32 uses less gas because it fits in a single word of the EVM, and string is a dynamically sized-type which has current limitations in Solidity (such as can’t be returned from a function to a contract).

If data can fit into 32 bytes, then you should use bytes32 datatype rather than bytes or strings as it is cheaper in solidity. Basically, any fixed size variable in solidity is cheaper than variable size. That will save gas on the contract.

Instances of string constant that can be replaced by bytes(1..32) constant

142 string constant _bpString = "eip1967.Holograph.PA1D.bp"; 143 string constant _receiverString = "eip1967.Holograph.PA1D.receiver"; 144 string constant _tokenAddressString = "eip1967.Holograph.PA1D.tokenAddress";

https://github.com/code-423n4/2022-10-holograph/blob/main/contracts/enforcer/PA1D.sol

8. Functions guaranteed to revert when called by normal users can be marked payable

If a function modifier such as onlyOwner is used, the function will revert if a normal user tries to pay the function. Marking the function as payable will lower the gas cost for legitimate callers because the compiler will not include checks for whether a payment was provided. The extra opcodes avoided are CALLVALUE(2),DUP1(3),ISZERO(3),PUSH2(3),JUMPI(10),PUSH1(3),DUP1(3),REVERT(0),JUMPDEST(1),POP(2), which costs an average of about 21 gas per call to the function, in addition to the extra deployment cost

452 function setFactory(address factory) external onlyAdmin { 472 function setHolograph(address holograph) external onlyAdmin { 502 function setOperator(address operator) external onlyAdmin { 522 function setRegistry(address registry) external onlyAdmin {

https://github.com/code-423n4/2022-10-holograph/blob/main/contracts/HolographBridge.sol

278 function resetOperator( 949 function setBridge(address bridge) external onlyAdmin { 969 function setHolograph(address holograph) external onlyAdmin { 989 function setInterfaces(address interfaces) external onlyAdmin { 1009 function setMessagingModule(address messagingModule) external onlyAdmin { 1029 function setRegistry(address registry) external onlyAdmin { 1049 function setUtilityToken(address utilityToken) external onlyAdmin {

https://github.com/code-423n4/2022-10-holograph/blob/main/contracts/HolographOperator.sol

280 function setHolograph(address holograph) external onlyAdmin { 300 function setRegistry(address registry) external onlyAdmin {

https://github.com/code-423n4/2022-10-holograph/blob/main/contracts/HolographFactory.sol

320 function setBridge(address bridge) external onlyAdmin { 340 function setInterfaces(address interfaces) external onlyAdmin { 360 function setLZEndpoint(address lZEndpoint) external onlyAdmin { 380 function setOperator(address operator) external onlyAdmin { 441 function setBaseGas(uint256 baseGas) external onlyAdmin { 470 function setGasPerByte(uint256 gasPerByte) external onlyAdmin {

https://github.com/code-423n4/2022-10-holograph/blob/main/contracts/module/LayerZeroModule.sol

##9. Multiple address/ID mappings can be combined into a single mapping of an address/ID 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. Finally, 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.

218 mapping(address => uint256) private _bondedOperators; 223 mapping(address => uint256) private _operatorPodIndex; 228 mapping(address => uint256) private _bondedAmounts;

https://github.com/code-423n4/2022-10-holograph/blob/main/contracts/HolographOperator.sol

156 mapping(address => uint256) private _balances; 161 mapping(address => mapping(address => uint256)) private _allowances; 186 mapping(address => uint256) private _nonces;

https://github.com/code-423n4/2022-10-holograph/blob/main/contracts/enforcer/HolographERC20.sol

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