Biconomy - Smart Contract Wallet contest - Rolezn's results

One-Stop solution to enable an effortless experience in your dApp to onboard new users and abstract away transaction complexities.

General Information

Platform: Code4rena

Start Date: 04/01/2023

Pot Size: $60,500 USDC

Total HM: 15

Participants: 105

Period: 5 days

Judge: gzeon

Total Solo HM: 1

Id: 200

League: ETH

Biconomy

Findings Distribution

Researcher Performance

Rank: 9/105

Findings: 3

Award: $940.23

QA:
grade-a
Gas:
grade-a

🌟 Selected for report: 0

🚀 Solo Findings: 0

Findings Information

Labels

bug
2 (Med Risk)
judge review requested
satisfactory
sponsor disputed
duplicate-261

Awards

44.8261 USDC - $44.83

External Links

Lines of code

https://github.com/code-423n4/2023-01-biconomy/blob/main/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol#L18 https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\base\FallbackManager.sol#L8 https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\base\ModuleManager.sol#L9 https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\handler\DefaultCallbackHandler.sol#L11 https://github.com/code-423n4/2023-01-biconomy/blob/main/scw-contracts/contracts/smart-contract-wallet/paymasters/verifying/singleton/VerifyingSingletonPaymaster.sol#L22

Vulnerability details

For upgradeable contracts, there must be storage gap to "allow developers to freely add new state variables in the future without compromising the storage compatibility with existing deployments". Otherwise it may be very difficult to write new implementation code. Without storage gap, the variable in child contract might be overwritten by the upgraded base contract if new variables are added to the base contract. This could have unintended and very serious consequences to the child contracts.

Refer to the bottom part of this article: https://docs.openzeppelin.com/upgrades-plugins/1.x/writing-upgradeable

<ins>Proof Of Concept</ins>

However, the contract doesn't contain a storage gap. The storage gap is essential for upgradeable contract because "It allows us to freely add new state variables in the future without compromising the storage compatibility with existing deployments". See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps for a description of this storage variable. While some contracts may not currently be sub-classed, adding the variable now protects against forgetting to add it in the future.

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\SmartAccount.sol#L18

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\base\FallbackManager.sol#L8

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\base\ModuleManager.sol#L9

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\handler\DefaultCallbackHandler.sol#L11

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\paymasters\verifying\singleton\VerifyingSingletonPaymaster.sol#L5

<ins>Recommended Mitigation Steps</ins>

Recommend adding appropriate storage gap at the end of upgradeable contracts such as the below. Please reference OpenZeppelin upgradeable contract templates.

    uint256[50] private __gap;

#0 - c4-judge

2023-01-17T15:53:31Z

gzeon-c4 marked the issue as duplicate of #352

#1 - livingrockrises

2023-01-19T17:42:41Z

gnosis safe is upgradeable contract and smart account uses the same proxy - implementation pattern.

https://github.com/safe-global/safe-contracts/blob/main/contracts/Safe.sol#L21

here the first inherited contract (with state) is Singleton.sol

where,

/// @title Singleton - Base for singleton contracts (should always be first super contract) /// This contract is tightly coupled to our proxy contract (see `proxies/GnosisSafeProxy.sol`) /// @author Richard Meissner - <richard@gnosis.io> contract Singleton { // singleton always needs to be first declared variable, to ensure that it is at the same location as in the Proxy contract. // It should also always be ensured that the address is stored alone (uses a full word) address private singleton; }``` now if the inherited contracts do not have the storage gaps, is this a problem?

#2 - c4-sponsor

2023-01-19T17:42:52Z

livingrockrises requested judge review

#3 - c4-sponsor

2023-01-19T17:43:00Z

livingrockrises marked the issue as sponsor disputed

#4 - c4-judge

2023-02-10T12:36:42Z

gzeon-c4 marked the issue as satisfactory

Awards

361.0144 USDC - $361.01

Labels

bug
grade-a
QA (Quality Assurance)
sponsor confirmed
edited-by-warden
Q-33

External Links

Summary<a name="Summary">

Low Risk Issues

IssueContexts
LOW‑1Avoid using tx.origin3
LOW‑2Use of ecrecover is susceptible to signature malleability2
LOW‑3Init functions are susceptible to front-running1
LOW‑4Low Level Calls With Solidity Version 0.8.14 Can Result In Optimiser Bug6
LOW‑5Missing parameter validation in constructor1
LOW‑6Missing Contract-existence Checks Before Low-level Calls10
LOW‑7Contracts are not using their OZ Upgradeable counterparts9
LOW‑8Remove unused code5
LOW‑9require() should be used instead of assert()1
LOW‑10Unused receive() Function Will Lock Ether In Contract1
LOW‑11Missing Non Reentrancy Modifiers6

Total: 45 contexts over 11 issues

Non-critical Issues

IssueContexts
NC‑1Critical Changes Should Use Two-step Procedure6
NC‑2Function creates dirty bits7
NC‑3Function writing that does not comply with the Solidity Style Guide37
NC‑4Use delete to Clear Variables4
NC‑5No need to initialize uints to zero11
NC‑6Lines are too long3
NC‑7Missing event for critical parameter change4
NC‑8Implementation contract may not be initialized6
NC‑9Non-usage of specific imports43
NC‑10Use a more recent version of Solidity37
NC‑11Open TODOs1
NC‑12Use bytes.concat()16
NC‑13Use of Block.Timestamp5
NC‑14Commented CodeAll in-scope contracts
NC‑15Missing/In-complete NATSPECAll in-scope contracts

Total: 252 contexts over 15 issues

Low Risk Issues

<a href="#Summary">[LOW‑1]</a><a name="LOW&#x2011;1"> Avoid using tx.origin

tx.origin is a global variable in Solidity that returns the address of the account that sent the transaction.

Using the variable could make a contract vulnerable if an authorized account calls a malicious contract. You can impersonate a user using a third party contract.

This can make it easier to create a vault on behalf of another user with an external administrator (by receiving it as an argument).

<ins>Proof Of Concept</ins>
257: address payable receiver = refundReceiver == address(0) ? payable(tx.origin) : refundReceiver;

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol#L257

281: address payable receiver = refundReceiver == address(0) ? payable(tx.origin) : refundReceiver;

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol#L281

511: require(owner == hash.recover(userOp.signature) || tx.origin == address(0), "account: wrong signature");

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol#L511

<a href="#Summary">[LOW‑2]</a><a name="LOW&#x2011;2"> Use of ecrecover is susceptible to signature malleability

The built-in EVM precompile ecrecover is susceptible to signature malleability, which could lead to replay attacks. References: https://swcregistry.io/docs/SWC-117, https://swcregistry.io/docs/SWC-121, and https://medium.com/cryptronics/signature-replay-vulnerabilities-in-smart-contracts-3b6f7596df57. While this is not immediately exploitable, this may become a vulnerability if used elsewhere.

<ins>Proof Of Concept</ins>
347: function checkSignatures(
        bytes32 dataHash,
        bytes memory data,
        bytes memory signatures
    ) public view virtual {
        uint8 v;
        bytes32 r;
        bytes32 s;
        uint256 i = 0;
        address _signer;
        (v, r, s) = signatureSplit(signatures, i);
        
        if(v == 0) {
            
            
            _signer = address(uint160(uint256(r)));

            
                
                
                require(uint256(s) >= uint256(1) * 65, "BSA021");

                
                require(uint256(s) + 32 <= signatures.length, "BSA022");

                
                uint256 contractSignatureLen;
                
                assembly {
                    contractSignatureLen := mload(add(add(signatures, s), 0x20))
                }
                require(uint256(s) + 32 + contractSignatureLen <= signatures.length, "BSA023");

                
                bytes memory contractSignature;
                
                assembly {
                    
                    contractSignature := add(add(signatures, s), 0x20)
                }
                require(ISignatureValidator(_signer).isValidSignature(data, contractSignature) == EIP1271_MAGIC_VALUE, "BSA024");
        }
        else if(v > 30) {
            
            
            _signer = ecrecover(keccak256(abi.encodePacked("/x19Ethereum Signed Message:/n32", dataHash)), v - 4, r, s);
            require(_signer == owner, "INVALID_SIGNATURE");
        } else {
            _signer = ecrecover(dataHash, v, r, s);
            require(_signer == owner, "INVALID_SIGNATURE");
        }
    }

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol#L302-L353

350: function checkSignatures(
        bytes32 dataHash,
        bytes memory data,
        bytes memory signatures
    ) public view virtual {
        uint8 v;
        bytes32 r;
        bytes32 s;
        uint256 i = 0;
        address _signer;
        (v, r, s) = signatureSplit(signatures, i);
        
        if(v == 0) {
            
            
            _signer = address(uint160(uint256(r)));

            
                
                
                require(uint256(s) >= uint256(1) * 65, "BSA021");

                
                require(uint256(s) + 32 <= signatures.length, "BSA022");

                
                uint256 contractSignatureLen;
                
                assembly {
                    contractSignatureLen := mload(add(add(signatures, s), 0x20))
                }
                require(uint256(s) + 32 + contractSignatureLen <= signatures.length, "BSA023");

                
                bytes memory contractSignature;
                
                assembly {
                    
                    contractSignature := add(add(signatures, s), 0x20)
                }
                require(ISignatureValidator(_signer).isValidSignature(data, contractSignature) == EIP1271_MAGIC_VALUE, "BSA024");
        }
        else if(v > 30) {
            
            
            _signer = ecrecover(keccak256(abi.encodePacked("/x19Ethereum Signed Message:/n32", dataHash)), v - 4, r, s);
            require(_signer == owner, "INVALID_SIGNATURE");
        } else {
            _signer = ecrecover(dataHash, v, r, s);
            require(_signer == owner, "INVALID_SIGNATURE");
        }
    }

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol#L350

Consider using OpenZeppelin’s ECDSA library (which prevents this malleability) instead of the built-in function. https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/cryptography/ECDSA.sol#L138-L149

<a href="#Summary">[LOW‑3]</a><a name="LOW&#x2011;3"> Init function is susceptible to front-running

Most contracts use an init pattern (instead of a constructor) to initialize contract parameters. Unless these are enforced to be atomic with contact deployment via deployment script or factory contracts, they are susceptible to front-running race conditions where an attacker/griefer can front-run (cannot access control because admin roles are not initialized) to initially with their own (malicious) parameters upon detecting (if an event is emitted) which the contract deployer has to redeploy wasting gas and risking other transactions from interacting with the attacker-initialized contract.

Many init functions do not have an explicit event emission which makes monitoring such scenarios harder. All of them have re-init checks; while many are explicit some (those in auction contracts) have implicit reinit checks in initAccessControls() which is better if converted to an explicit check in the main init function itself. (details credit to: code-423n4/2021-09-sushimiso-findings#64)

<ins>Proof Of Concept</ins>
166: function init(address _owner, address _entryPointAddress, address _handler) public override initializer { 

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol#L166

<ins>Recommended Mitigation Steps</ins>

Ensure atomic calls to init functions along with deployment via robust deployment scripts or factory contracts. Emit explicit events for initializations of contracts. Enforce prevention of re-initializations via explicit setting/checking of boolean initialized variables in the main init function instead of downstream function checks.

<a href="#Summary">[LOW‑4]</a><a name="LOW&#x2011;4"> Low Level Calls With Solidity Version Before 0.8.14 Can Result In Optimiser Bug

The project contracts in scope are using low level calls with solidity version before 0.8.14 which can result in optimizer bug. https://medium.com/certora/overly-optimistic-optimizer-certora-bug-disclosure-2101e3f7994d

Simliar findings in Code4rena contests for reference: https://code4rena.com/reports/2022-06-illuminate/#5-low-level-calls-with-solidity-version-0814-can-result-in-optimiser-bug

<ins>Proof Of Concept</ins>

POC can be found in the above medium reference url.

Functions that execute low level calls in contracts with solidity version under 0.8.14

512: assembly {mstore(0, number())}

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts/smart-contract-wallet/aa-4337/core/EntryPoint.sol#L512

63: assembly {
        let ofs := userOp
        let len := sub(sub(sig.offset, ofs), 32)
        ret := mload(0x40)
        mstore(0x40, add(ret, add(len, 32)))
        mstore(ret, len)
        calldatacopy(add(ret, 32), ofs, len)
    }

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts/smart-contract-wallet/aa-4337/interfaces/UserOperation.sol#L63-L70

41: assembly {
        let ptr := mload(0x40)
        mstore(0x40, add(ptr, add(returndatasize(), 0x20)))
        mstore(ptr, returndatasize())
        returndatacopy(add(ptr, 0x20), 0, returndatasize())
        returnData := ptr
    }

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts/smart-contract-wallet/aa-4337/utils/Exec.sol#L41-L47

35: assembly {
        let handler := sload(slot)
        if iszero(handler) {
            return(0, 0)
        }
        calldatacopy(0, 0, calldatasize())
        // The msg.sender address is shifted to the left by 12 bytes to remove the padding
        // Then the address without padding is stored right after the calldata
        mstore(calldatasize(), shl(96, caller()))
        // Add 20 bytes for the address appended add the end
        let success := call(gas(), handler, 0, 0, add(calldatasize(), 20), 0, 0)
        returndatacopy(0, 0, returndatasize())
        if iszero(success) {
            revert(0, returndatasize())
        }
        return(0, returndatasize())
    }

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts/smart-contract-wallet/base/FallbackManager.sol#L35-L51

88: assembly {
        // Load free memory location
        let ptr := mload(0x40)
        // We allocate memory for the return data by setting the free memory location to
        // current free memory location + data size + 32 bytes for data size value
        mstore(0x40, add(ptr, add(returndatasize(), 0x20)))
        // Store the size
        mstore(ptr, returndatasize())
        // Store the data
        returndatacopy(add(ptr, 0x20), 0, returndatasize())
        // Point the return data to the correct memory location
        returnData := ptr
    }

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts/smart-contract-wallet/base/ModuleManager.sol#L88-L100

129: assembly {
        mstore(array, moduleCount)
    }

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts/smart-contract-wallet/base/ModuleManager.sol#L129-L131

<ins>Recommended Mitigation Steps</ins>

Consider upgrading to at least solidity v0.8.15.

<a href="#Summary">[LOW‑5]</a><a name="LOW&#x2011;5"> Missing parameter validation in constructor

Some parameters of constructors are not checked for invalid values.

<ins>Proof Of Concept</ins>
15: address _implementation

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts/smart-contract-wallet/Proxy.sol#L15

<ins>Recommended Mitigation Steps</ins>

Validate the parameters.

<a href="#Summary">[LOW‑6]</a><a name="LOW&#x2011;6"> Missing Contract-existence Checks Before Low-level Calls

Low-level calls return success if there is no code present at the specified address.

<ins>Proof Of Concept</ins>
108: (bool success,) = payable(msg.sender).call{value : missingAccountFunds, gas : type(uint256).max}("");

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts/smart-contract-wallet/BaseSmartAccount.sol#L108

261: (bool success,) = receiver.call{value: payment}("");

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol#L261

285: (bool success,) = receiver.call{value: payment}("");

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol#L285

451: (bool success,) = dest.call{value:amount}("");

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol#L451

478: (bool success, bytes memory result) = target.call{value : value}(data);

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol#L478

527: (bool req,) = address(entryPoint()).call{value : msg.value}("");

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol#L527

37: (bool success,) = beneficiary.call{value : amount}("");

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts/smart-contract-wallet/aa-4337/core/EntryPoint.sol#L37

176: (bool success,bytes memory result) = address(mUserOp.sender).call{gas : mUserOp.callGasLimit}(callData);

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts/smart-contract-wallet/aa-4337/core/EntryPoint.sol#L176

106: (bool success,) = withdrawAddress.call{value : stake}("");

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts/smart-contract-wallet/aa-4337/core/StakeManager.sol#L106

120: (bool success,) = withdrawAddress.call{value : withdrawAmount}("");

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts/smart-contract-wallet/aa-4337/core/StakeManager.sol#L120

<ins>Recommended Mitigation Steps</ins>

In addition to the zero-address checks, add a check to verify that <address>.code.length > 0

<a href="#Summary">[LOW‑7]</a><a name="LOW&#x2011;7"> Contracts are not using their OZ Upgradeable counterparts

The non-upgradeable standard version of OpenZeppelin’s library are inherited / used by the contracts. It would be safer to use the upgradeable versions of the library contracts to avoid unexpected behaviour.

<ins>Proof of Concept</ins>
5: import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
6: import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
16: import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol#L5-L16

7: import "@openzeppelin/contracts/access/Ownable.sol";

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts/smart-contract-wallet/aa-4337/core/BasePaymaster.sol#L7

7: import "@openzeppelin/contracts/access/Ownable.sol";

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts/smart-contract-wallet/paymasters/BasePaymaster.sol#L7

4: import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts/smart-contract-wallet/paymasters/PaymasterHelpers.sol#L4

6: import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
7: import "@openzeppelin/contracts/proxy/utils/Initializable.sol";
8: import "@openzeppelin/contracts/utils/Address.sol";

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts/smart-contract-wallet/paymasters/verifying/singleton/VerifyingSingletonPaymaster.sol#L6-L8

<ins>Recommended Mitigation Steps</ins>

Where applicable, use the contracts from @openzeppelin/contracts-upgradeable instead of @openzeppelin/contracts.

<a href="#Summary">[LOW‑8]</a><a name="LOW&#x2011;8"> Remove unused code

This code is not used in the main project contract files, remove it or add event-emit. Code that is not in use, suggests that they should not be present and could potentially contain insecure functionalities.

<ins>Proof Of Concept</ins>
function delegateCall

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts/smart-contract-wallet/aa-4337/utils/Exec.sol#L29

function callAndRevert

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts/smart-contract-wallet/aa-4337/utils/Exec.sol#L57

function _getImplementation

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts/smart-contract-wallet/common/Singleton.sol#L20

function ceilDiv

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts/smart-contract-wallet/libs/Math.sol#L45

<a href="#Summary">[LOW‑9]</a><a name="LOW&#x2011;9"> require() Should Be Used Instead Of assert()

Prior to solidity version 0.8.0, hitting an assert consumes the remainder of the transaction's available gas rather than returning it, as require()/revert() do. assert() should be avoided even past solidity version 0.8.0 as its <a href="https://docs.soliditylang.org/en/v0.8.14/control-structures.html#panic-via-assert-and-error-via-require">documentation</a> states that "The assert function creates an error of type Panic(uint256). ... Properly functioning code should never create a Panic, not even on invalid external input. If this happens, then there is a bug in your contract which you should fix".

<ins>Proof Of Concept</ins>
13: assert(_IMPLEMENTATION_SLOT == bytes32(uint256(keccak256("biconomy.scw.proxy.implementation")) - 1));

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts/smart-contract-wallet/common/Singleton.sol#L13

<a href="#Summary">[LOW‑10]</a><a name="LOW&#x2011;10"> Unused receive() Function Will Lock Ether In Contract

If the intention is for the Ether to be used, the function should call another function, otherwise it should revert

<ins>Proof Of Concept</ins>
550: receive() external payable {}

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol#L550

<ins>Recommended Mitigation Steps</ins>

The function should call another function, otherwise it should revert

<a href="#Summary">[LOW‑11]</a><a name="LOW&#x2011;11"> Missing Non Reentrancy Modifiers

The following functions are missing reentrancy modifier although some other public/external functions does use reentrancy modifer. Even though I did not find a way to exploit it, it seems like those functions should have the nonReentrant modifier as the other functions have it as well.

<ins>Proof Of Concept</ins>
function execTransaction(
        Transaction memory _tx,
        uint256 batchId,
        FeeRefund memory refundInfo,
        bytes memory signatures
    ) public payable virtual override returns (bool success) {

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\SmartAccount.sol#L192

function handlePaymentRevert(
        uint256 gasUsed,
        uint256 baseGas,
        uint256 gasPrice,
        uint256 tokenGasPriceFactor,
        address gasToken,
        address payable refundReceiver
    ) external returns (uint256 payment) {

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\SmartAccount.sol#L271

function requiredTxGas(
        address to,
        uint256 value,
        bytes calldata data,
        Enum.Operation operation
    ) external returns (uint256) {

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\SmartAccount.sol#L363

function addDeposit() public payable {

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\SmartAccount.sol#L525

function withdrawDepositTo(address payable withdrawAddress, uint256 amount) public onlyOwner {

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\SmartAccount.sol#L536

<ins>Recommended Mitigation Steps</ins>

Add reentrancy modifiers

Non Critical Issues

<a href="#Summary">[NC‑1]</a><a name="NC&#x2011;1"> Critical Changes Should Use Two-step Procedure

The critical procedures should be two step process.

See similar findings in previous Code4rena contests for reference: https://code4rena.com/reports/2022-06-illuminate/#2-critical-changes-should-use-two-step-procedure

<ins>Proof Of Concept</ins>
109: function setOwner(address _newOwner) external mixedAuth {

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol#L109

24: function setEntryPoint(IEntryPoint _entryPoint) public onlyOwner {

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts/smart-contract-wallet/aa-4337/core/BasePaymaster.sol#L24

26: function setFallbackHandler(address handler) public authorized {

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts/smart-contract-wallet/base/FallbackManager.sol#L26

20: function setupModules(address to, bytes memory data) internal {

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts/smart-contract-wallet/base/ModuleManager.sol#L20

24: function setEntryPoint(IEntryPoint _entryPoint) public onlyOwner {

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts/smart-contract-wallet/paymasters/BasePaymaster.sol#L24

65: function setSigner( address _newVerifyingSigner) external onlyOwner{

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts/smart-contract-wallet/paymasters/verifying/singleton/VerifyingSingletonPaymaster.sol#L65

<ins>Recommended Mitigation Steps</ins>

Lack of two-step procedure for critical operations leaves them error-prone. Consider adding two step procedure on the critical functions.

<a href="#Summary">[NC‑2]</a><a name="NC&#x2011;2"> Function creates dirty bits

This explanation should be added in the NatSpec comments of this function that sends ether with call;

Note that this code probably isn’t secure or a good use case for assembly because a lot of memory management and security checks are bypassed. Use with caution! Some functions in this contract knowingly create dirty bits at the destination of the free memory pointer.

<ins>Proof Of Concept</ins>
21: function createSender(bytes calldata initCode) external returns (address sender) {
        address initAddress = address(bytes20(initCode[0 : 20]));
        bytes memory initCallData = initCode[20 :];
        bool success;
        
        assembly {
            success := call(gas(), initAddress, 0, add(initCallData, 0x20), mload(initCallData), 0, 32)
            sender := mload(0)
        }
        if (!success) {
            sender = address(0);
        }
    }
}

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts/smart-contract-wallet/aa-4337/core/SenderCreator.sol#L21

15: function call(
        address to,
        uint256 value,
        bytes memory data,
        uint256 txGas
    ) internal returns (bool success) {
        assembly {
            success := call(txGas, to, value, add(data, 0x20), mload(data), 0, 0)
        }
    }

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts/smart-contract-wallet/aa-4337/utils/Exec.sol#L15

28: function execute(
        address to,
        uint256 value,
        bytes memory data,
        Enum.Operation operation,
        uint256 txGas
    ) internal returns (bool success) {
        if (operation == Enum.Operation.DelegateCall) {
            
            assembly {
                success := delegatecall(txGas, to, add(data, 0x20), mload(data), 0, 0)
            }
        } else {
            
            assembly {
                success := call(txGas, to, value, add(data, 0x20), mload(data), 0, 0)
            }
        }
        
        if (success) emit ExecutionSuccess(to, value, data, operation, txGas);
        else emit ExecutionFailure(to, value, data, operation, txGas);
    }
    
}

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts/smart-contract-wallet/base/Executor.sol#L28

45: function setFallbackHandler(address handler) public authorized {
        internalSetFallbackHandler(handler);
        emit ChangedFallbackHandler(handler);
    }

    
    fallback() external {
        bytes32 slot = FALLBACK_HANDLER_STORAGE_SLOT;
        
        assembly {
            let handler := sload(slot)
            if iszero(handler) {
                return(0, 0)
            }
            calldatacopy(0, 0, calldatasize())
            
            
            mstore(calldatasize(), shl(96, caller()))
            
            let success := call(gas(), handler, 0, 0, add(calldatasize(), 20), 0, 0)
            returndatacopy(0, 0, returndatasize())
            if iszero(success) {
                revert(0, returndatasize())
            }
            return(0, returndatasize())
        }
    }
}

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts/smart-contract-wallet/base/FallbackManager.sol#L45

22: function transferToken(
        address token,
        address receiver,
        uint256 amount
    ) internal returns (bool transferred) {
        
        
        bytes memory data = abi.encodeWithSelector(0xa9059cbb, receiver, amount);
        
        assembly {
            
            
            let success := call(sub(gas(), 10000), token, 0, add(data, 0x20), mload(data), 0, 0x20)
            switch returndatasize()
                case 0 {
                    transferred := success
                }
                case 0x20 {
                    transferred := iszero(or(iszero(success), iszero(mload(0))))
                }
                default {
                    transferred := 0
                }
        }
    }
}

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts/smart-contract-wallet/common/SecuredTokenTransfer.sol#L22

53: function multiSend(bytes memory transactions) public payable {
        require(address(this) != multisendSingleton, "MultiSend should only be called via delegatecall");
        
        assembly {
            let length := mload(transactions)
            let i := 0x20
            for {
                
            } lt(i, length) {
                
            } {
                
                
                
                let operation := shr(0xf8, mload(add(transactions, i)))
                
                
                let to := shr(0x60, mload(add(transactions, add(i, 0x01))))
                
                let value := mload(add(transactions, add(i, 0x15)))
                
                let dataLength := mload(add(transactions, add(i, 0x35)))
                
                let data := add(transactions, add(i, 0x55))
                let success := 0
                switch operation
                    case 0 {
                        success := call(gas(), to, value, data, dataLength, 0, 0)
                    }
                    case 1 {
                        success := delegatecall(gas(), to, data, dataLength, 0, 0)
                    }
                if eq(success, 0) {
                    revert(0, 0)
                }
                
                i := add(i, add(0x55, dataLength))
            }
        }
    }
}

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts/smart-contract-wallet/libs/MultiSend.sol#L53

47: function multiSend(bytes memory transactions) public payable {
        
        assembly {
            let length := mload(transactions)
            let i := 0x20
            for {
                
            } lt(i, length) {
                
            } {
                
                
                
                let operation := shr(0xf8, mload(add(transactions, i)))
                
                
                let to := shr(0x60, mload(add(transactions, add(i, 0x01))))
                
                let value := mload(add(transactions, add(i, 0x15)))
                
                let dataLength := mload(add(transactions, add(i, 0x35)))
                
                let data := add(transactions, add(i, 0x55))
                let success := 0
                switch operation
                    case 0 {
                        success := call(gas(), to, value, data, dataLength, 0, 0)
                    }
                    
                    case 1 {
                        revert(0, 0)
                    }
                if eq(success, 0) {
                    revert(0, 0)
                }
                
                i := add(i, add(0x55, dataLength))
            }
        }
    }
}

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts/smart-contract-wallet/libs/MultiSendCallOnly.sol#L47

<ins>Recommended Mitigation Steps</ins>

Add this comment to the function:

/// @dev Use with caution! Some functions in this contract knowingly create dirty bits at the destination of the free memory pointer. Note that this code probably isn’t secure or a good use case for assembly because a lot of memory management and security checks are bypassed.

<a href="#Summary">[NC‑3]</a><a name="NC&#x2011;3"> Function writing that does not comply with the Solidity Style Guide

Order of Functions; ordering helps readers identify which functions they can call and to find the constructor and fallback definitions easier. But there are contracts in the project that do not comply with this.

https://docs.soliditylang.org/en/v0.8.17/style-guide.html

Functions should be grouped according to their visibility and ordered:

  • constructor
  • receive function (if exists)
  • fallback function (if exists)
  • external
  • public
  • internal
  • private
  • within a grouping, place the view and pure functions last
<ins>Proof Of Concept</ins>

All in-scope contracts

<a href="#Summary">[NC‑4]</a><a name="NC&#x2011;4"> 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.

<ins>Proof Of Concept</ins>
106: opIndex = 0;

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts/smart-contract-wallet/aa-4337/core/EntryPoint.sol#L106

102: info.unstakeDelaySec = 0;
103: info.withdrawTime = 0;
104: info.stake = 0;

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts/smart-contract-wallet/aa-4337/core/StakeManager.sol#L102-L104

<a href="#Summary">[NC‑5]</a><a name="NC&#x2011;5"> No need to initialize uints to zero

There is no need to initialize uint variables to zero as their default value is 0

<ins>Proof Of Concept</ins>
234: uint256 payment = 0;

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol#L234

310: uint256 i = 0;

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol#L310

78: uint256 collected = 0;

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts/smart-contract-wallet/aa-4337/core/EntryPoint.sol#L78

99: uint256 totalOps = 0;
106: uint256 opIndex = 0;
126: uint256 collected = 0;

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts/smart-contract-wallet/aa-4337/core/EntryPoint.sol#L99

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts/smart-contract-wallet/aa-4337/core/EntryPoint.sol#L106

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts/smart-contract-wallet/aa-4337/core/EntryPoint.sol#L126

313: uint256 missingAccountFunds = 0;

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts/smart-contract-wallet/aa-4337/core/EntryPoint.sol#L313

119: uint256 moduleCount = 0;

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts/smart-contract-wallet/base/ModuleManager.sol#L119

259: uint256 result = 0;

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts/smart-contract-wallet/libs/Math.sol#L259

310: uint256 result = 0;

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts/smart-contract-wallet/libs/Math.sol#L310

310: uint256 result = 0;

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts/smart-contract-wallet/libs/Math.sol#L310

<a href="#Summary">[NC‑6]</a><a name="NC&#x2011;6"> Lines are too long

Usually lines in source code are limited to 80 characters. Today's screens are much larger so it's reasonable to stretch this in some cases. Since the files will most likely reside in GitHub, and GitHub starts using a scroll bar in all cases when the length is over 164 characters, the lines below should be split when they reach that length Reference: https://docs.soliditylang.org/en/v0.8.10/style-guide.html#maximum-line-length

<ins>Proof Of Concept</ins>
46: //     "AccountTx(address to,uint256 value,bytes data,uint8 operation,uint256 targetTxGas,uint256 baseGas,uint256 gasPrice,address gasToken,address refundReceiver,uint256 nonce)"

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol#L46

186: /// @dev Allows to execute a Safe transaction confirmed by required number of owners and then pays the account that submitted the transaction.

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol#L186

357: ///      Since the `estimateGas` function includes refunds, call this method to get an estimated of the costs that are deducted from the safe with `execTransaction`

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol#L357

<a href="#Summary">[NC‑7]</a><a name="NC&#x2011;7"> Missing event for critical parameter change

When changing state variables events are not emitted. Emitting events allows monitoring activities with off-chain monitoring tools.

<ins>Proof Of Concept</ins>
24: function setEntryPoint(IEntryPoint _entryPoint) public onlyOwner {

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts/smart-contract-wallet/aa-4337/core/BasePaymaster.sol#L24

20: function setupModules(address to, bytes memory data) internal {

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts/smart-contract-wallet/base/ModuleManager.sol#L20

24: function setEntryPoint(IEntryPoint _entryPoint) public onlyOwner {

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts/smart-contract-wallet/paymasters/BasePaymaster.sol#L24

65: function setSigner( address _newVerifyingSigner) external onlyOwner{

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts/smart-contract-wallet/paymasters/verifying/singleton/VerifyingSingletonPaymaster.sol#L65

<a href="#Summary">[NC‑8]</a><a name="NC&#x2011;8"> Implementation contract may not be initialized

OpenZeppelin recommends that the initializer modifier be applied to constructors. Per OZs Post implementation contract should be initialized to avoid potential griefs or exploits. https://forum.openzeppelin.com/t/uupsupgradeable-vulnerability-post-mortem/15680/5

<ins>Proof Of Concept</ins>
15: constructor(address _implementation)

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts/smart-contract-wallet/Proxy.sol#L15

17: constructor(address _baseImpl)

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts/smart-contract-wallet/SmartAccountFactory.sol#L17

20: constructor(IEntryPoint _entryPoint)

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts/smart-contract-wallet/aa-4337/core/BasePaymaster.sol#L20

12: constructor()

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts/smart-contract-wallet/libs/MultiSend.sol#L12

20: constructor(IEntryPoint _entryPoint)

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts/smart-contract-wallet/paymasters/BasePaymaster.sol#L20

35: constructor(IEntryPoint _entryPoint, address _verifyingSigner) BasePaymaster(_entryPoint)

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts/smart-contract-wallet/paymasters/verifying/singleton/VerifyingSingletonPaymaster.sol#L35

<a href="#Summary">[NC‑9]</a><a name="NC&#x2011;9"> Non-usage of specific imports

The current form of relative path import is not recommended for use because it can unpredictably pollute the namespace. Instead, the Solidity docs recommend specifying imported symbols explicitly. https://docs.soliditylang.org/en/v0.8.15/layout-of-source-files.html#importing-other-source-files

A good example:

import {OwnableUpgradeable} from "openzeppelin-contracts-upgradeable/contracts/access/OwnableUpgradeable.sol";
import {SafeTransferLib} from "solmate/utils/SafeTransferLib.sol";
import {SafeCastLib} from "solmate/utils/SafeCastLib.sol";
import {ERC20} from "solmate/tokens/ERC20.sol";
import {IProducer} from "src/interfaces/IProducer.sol";
import {GlobalState, UserState} from "src/Common.sol";
<ins>Proof Of Concept</ins>
10: import "./common/Enum.sol";

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts/smart-contract-wallet/BaseSmartAccount.sol#L10

4: import "./libs/LibAddress.sol";
8: import "./BaseSmartAccount.sol";
9: import "./common/Singleton.sol";
10: import "./base/ModuleManager.sol";
11: import "./base/FallbackManager.sol";
12: import "./common/SignatureDecoder.sol";
13: import "./common/SecuredTokenTransfer.sol";
14: import "./interfaces/ISignatureValidator.sol";
15: import "./interfaces/IERC165.sol";

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol#L4-L15

4: import "./Proxy.sol";
5: import "./BaseSmartAccount.sol";

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts/smart-contract-wallet/SmartAccountFactory.sol#L4-L5

8: import "../interfaces/IPaymaster.sol";
9: import "../interfaces/IEntryPoint.sol";

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts/smart-contract-wallet/aa-4337/core/BasePaymaster.sol#L8-L9

12: import "../interfaces/IAccount.sol";
13: import "../interfaces/IPaymaster.sol";
15: import "../interfaces/IAggregatedAccount.sol";
16: import "../interfaces/IEntryPoint.sol";
17: import "../utils/Exec.sol";
18: import "./StakeManager.sol";
19: import "./SenderCreator.sol";

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts/smart-contract-wallet/aa-4337/core/EntryPoint.sol#L12-L19

4: import "../interfaces/IStakeManager.sol";

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts/smart-contract-wallet/aa-4337/core/StakeManager.sol#L4

4: import "./UserOperation.sol";

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts/smart-contract-wallet/aa-4337/interfaces/IAccount.sol#L4

4: import "./UserOperation.sol";
5: import "./IAccount.sol";
6: import "./IAggregator.sol";

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts/smart-contract-wallet/aa-4337/interfaces/IAggregatedAccount.sol#L4-L6

4: import "./UserOperation.sol";

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts/smart-contract-wallet/aa-4337/interfaces/IAggregator.sol#L4

12: import "./UserOperation.sol";
13: import "./IStakeManager.sol";
14: import "./IAggregator.sol";

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts/smart-contract-wallet/aa-4337/interfaces/IEntryPoint.sol#L12-L14

4: import "./UserOperation.sol";

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts/smart-contract-wallet/aa-4337/interfaces/IPaymaster.sol#L4

4: import "../common/Enum.sol";

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts/smart-contract-wallet/base/Executor.sol#L4

4: import "../common/SelfAuthorized.sol";

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts/smart-contract-wallet/base/FallbackManager.sol#L4

4: import "../common/Enum.sol";
5: import "../common/SelfAuthorized.sol";
6: import "./Executor.sol";

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts/smart-contract-wallet/base/ModuleManager.sol#L4-L6

4: import "../interfaces/ERC1155TokenReceiver.sol";
5: import "../interfaces/ERC721TokenReceiver.sol";
6: import "../interfaces/ERC777TokensRecipient.sol";
7: import "../interfaces/IERC165.sol";

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts/smart-contract-wallet/handler/DefaultCallbackHandler.sol#L4-L7

5: import "../../BasePaymaster.sol";
9: import "../../PaymasterHelpers.sol";
10: import "../samples/Signatures.sol";

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts/smart-contract-wallet/paymasters/verifying/singleton/VerifyingSingletonPaymaster.sol#L5-L10

<ins>Recommended Mitigation Steps</ins>

Use specific imports syntax per solidity docs recommendation.

<a href="#Summary">[NC‑10]</a><a name="NC&#x2011;10"> Use a more recent version of Solidity

<a href="https://blog.soliditylang.org/2022/03/16/solidity-0.8.13-release-announcement/">0.8.13</a>: Ability to use using for with a list of free functions

<a href="https://blog.soliditylang.org/2022/05/18/solidity-0.8.14-release-announcement/">0.8.14</a>:

ABI Encoder: When ABI-encoding values from calldata that contain nested arrays, correctly validate the nested array length against calldatasize() in all cases. Override Checker: Allow changing data location for parameters only when overriding external functions.

<a href="https://blog.soliditylang.org/2022/06/15/solidity-0.8.15-release-announcement/">0.8.15</a>:

Code Generation: Avoid writing dirty bytes to storage when copying bytes arrays. Yul Optimizer: Keep all memory side-effects of inline assembly blocks.

<a href="https://blog.soliditylang.org/2022/08/08/solidity-0.8.16-release-announcement/">0.8.16</a>:

Code Generation: Fix data corruption that affected ABI-encoding of calldata values represented by tuples: structs at any nesting level; argument lists of external functions, events and errors; return value lists of external functions. The 32 leading bytes of the first dynamically-encoded value in the tuple would get zeroed when the last component contained a statically-encoded array.

<a href="https://blog.soliditylang.org/2022/09/08/solidity-0.8.17-release-announcement/">0.8.17</a>:

Yul Optimizer: Prevent the incorrect removal of storage writes before calls to Yul functions that conditionally terminate the external EVM call.

<ins>Proof Of Concept</ins>

All in-scope contracts

<ins>Recommended Mitigation Steps</ins>

Consider updating to a more recent solidity version.

<a href="#Summary">[NC‑11]</a><a name="NC&#x2011;11"> Open TODOs

An open TODO is present. It is recommended to avoid open TODOs as they may indicate programming errors that still need to be fixed.

<ins>Proof Of Concept</ins>
255: // TODO: copy logic of gasPrice?

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts/smart-contract-wallet/aa-4337/core/EntryPoint.sol#L255

<a href="#Summary">[NC‑12]</a><a name="NC&#x2011;12"> Use bytes.concat()

Solidity version 0.8.4 introduces bytes.concat() (vs abi.encodePacked(<bytes>,<bytes>))

<ins>Proof Of Concept</ins>
294: revert(string(abi.encodePacked(requiredGas)
374: revert(string(abi.encodePacked(requiredGas)
347: _signer = ecrecover(keccak256(abi.encodePacked("/x19Ethereum Signed Message:/n32", dataHash)
294: revert(string(abi.encodePacked(requiredGas)
374: revert(string(abi.encodePacked(requiredGas)
445: return abi.encodePacked(bytes1(0x19)

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol#L294

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol#L374

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol#L347

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol#L294

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol#L374

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol#L445

34: bytes32 salt = keccak256(abi.encodePacked(_owner, address(uint160(_index)
70: bytes32 salt = keccak256(abi.encodePacked(_owner, address(uint160(_index)
35: bytes memory deploymentData = abi.encodePacked(type(Proxy)
54: bytes memory deploymentData = abi.encodePacked(type(Proxy)
35: bytes memory deploymentData = abi.encodePacked(type(Proxy)
54: bytes memory deploymentData = abi.encodePacked(type(Proxy)
69: bytes memory code = abi.encodePacked(type(Proxy)
34: bytes32 salt = keccak256(abi.encodePacked(_owner, address(uint160(_index)
70: bytes32 salt = keccak256(abi.encodePacked(_owner, address(uint160(_index)
71: bytes32 hash = keccak256(abi.encodePacked(bytes1(0xff)

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts/smart-contract-wallet/SmartAccountFactory.sol#L34

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts/smart-contract-wallet/SmartAccountFactory.sol#L70

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts/smart-contract-wallet/SmartAccountFactory.sol#L35

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts/smart-contract-wallet/SmartAccountFactory.sol#L54

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts/smart-contract-wallet/SmartAccountFactory.sol#L35

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts/smart-contract-wallet/SmartAccountFactory.sol#L54

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts/smart-contract-wallet/SmartAccountFactory.sol#L69

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts/smart-contract-wallet/SmartAccountFactory.sol#L34

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts/smart-contract-wallet/SmartAccountFactory.sol#L70

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts/smart-contract-wallet/SmartAccountFactory.sol#L71

<ins>Recommended Mitigation Steps</ins>

Use bytes.concat() and upgrade to at least Solidity version 0.8.4 if required.

<a href="#Summary">[NC‑13]</a><a name="NC&#x2011;13"> Use of Block.Timestamp

Block timestamps have historically been used for a variety of applications, such as entropy for random numbers (see the Entropy Illusion for further details), locking funds for periods of time, and various state-changing conditional statements that are time-dependent. Miners have the ability to adjust timestamps slightly, which can prove to be dangerous if block timestamps are used incorrectly in smart contracts. References: SWC ID: 116

<ins>Proof Of Concept</ins>
321: if (_deadline != 0 && _deadline < block.timestamp) {

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts/smart-contract-wallet/aa-4337/core/EntryPoint.sol#L321

365: if (_deadline != 0 && _deadline < block.timestamp) {

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts/smart-contract-wallet/aa-4337/core/EntryPoint.sol#L365

101: require(info.withdrawTime <= block.timestamp, "Stake withdrawal is not due");

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts/smart-contract-wallet/aa-4337/core/StakeManager.sol#L101

<ins>Recommended Mitigation Steps</ins>

Block timestamps should not be used for entropy or generating random numbers—i.e., they should not be the deciding factor (either directly or through some derivation) for winning a game or changing an important state.

Time-sensitive logic is sometimes required; e.g., for unlocking contracts (time-locking), completing an ICO after a few weeks, or enforcing expiry dates. It is sometimes recommended to use block.number and an average block time to estimate times; with a 10 second block time, 1 week equates to approximately, 60480 blocks. Thus, specifying a block number at which to change a contract state can be more secure, as miners are unable to easily manipulate the block number.

<a href="#Summary">[NC‑14]</a><a name="NC&#x2011;14"> Commented Code

Commented code is exposed throughout all in-scope contracts which suggest unfinished code. It is recommended to remove commented code for finalized contracts.

<ins>Proof Of Concept</ins>

All in-scope contracts.

i.e. Such as SmartAccount.sol

192: function execTransaction(
        Transaction memory _tx,
        uint256 batchId,
        FeeRefund memory refundInfo,
        bytes memory signatures
    ) public payable virtual override returns (bool success) {
        // initial gas = 21k + non_zero_bytes * 16 + zero_bytes * 4
        //            ~= 21k + calldata.length * [1/3 * 16 + 2/3 * 4]
        uint256 startGas = gasleft() + 21000 + msg.data.length * 8;
        //console.log("init %s", 21000 + msg.data.length * 8);
        bytes32 txHash;
        // Use scope here to limit variable lifetime and prevent `stack too deep` errors
        {
            bytes memory txHashData =
                encodeTransactionData(
                    // Transaction info
                    _tx,
                    // Payment info
                    refundInfo,
                    // Signature info
                    nonces[batchId]
                );
            // Increase nonce and execute transaction.
            // Default space aka batchId is 0
            nonces[batchId]++;
            txHash = keccak256(txHashData);
            checkSignatures(txHash, txHashData, signatures);
        }


        // We require some gas to emit the events (at least 2500) after the execution and some to perform code until the execution (500)
        // We also include the 1/64 in the check that is not send along with a call to counteract potential shortings because of EIP-150
        require(gasleft() >= max((_tx.targetTxGas * 64) / 63,_tx.targetTxGas + 2500) + 500, "BSA010");
        // Use scope here to limit variable lifetime and prevent `stack too deep` errors
        {
            // If the gasPrice is 0 we assume that nearly all available gas can be used (it is always more than targetTxGas)
            // We only substract 2500 (compared to the 3000 before) to ensure that the amount passed is still higher than targetTxGas
            success = execute(_tx.to, _tx.value, _tx.data, _tx.operation, refundInfo.gasPrice == 0 ? (gasleft() - 2500) : _tx.targetTxGas);
            // If no targetTxGas and no gasPrice was set (e.g. both are 0), then the internal tx is required to be successful
            // This makes it possible to use `estimateGas` without issues, as it searches for the minimum gas where the tx doesn't revert
            require(success || _tx.targetTxGas != 0 || refundInfo.gasPrice != 0, "BSA013");
            // We transfer the calculated tx costs to the tx.origin to avoid sending it to intermediate contracts that have made calls
            uint256 payment = 0;
            // uint256 extraGas;
            if (refundInfo.gasPrice > 0) {
                //console.log("sent %s", startGas - gasleft());
                // extraGas = gasleft();
                payment = handlePayment(startGas - gasleft(), refundInfo.baseGas, refundInfo.gasPrice, refundInfo.tokenGasPriceFactor, refundInfo.gasToken, refundInfo.refundReceiver);
                emit WalletHandlePayment(txHash, payment);
            }
            // extraGas = extraGas - gasleft();
            //console.log("extra gas %s ", extraGas);
        }
    }

<a href="#Summary">[NC‑15]</a><a name="NC&#x2011;15"> Missing/In-complete NATSPEC

Missing NATSPEC documentation through many of the in-scope contracts

<ins>Proof Of Concept</ins>

All in-scope contracts.

#0 - c4-judge

2023-01-22T15:37:42Z

gzeon-c4 marked the issue as grade-a

#1 - c4-sponsor

2023-02-09T12:36:15Z

livingrockrises marked the issue as sponsor confirmed

Awards

534.3927 USDC - $534.39

Labels

bug
G (Gas Optimization)
grade-a
sponsor confirmed
G-13

External Links

Summary<a name="Summary">

Gas Optimizations

IssueContextsEstimated Gas Saved
GAS‑1abi.encode() is less efficient than abi.encodepacked()5500
GAS‑2State variables only set in the constructor should be declared immutable1-
GAS‑3Setting the constructor to payable678
GAS‑4Duplicated require()/revert() Checks Should Be Refactored To A Modifier Or Function8532
GAS‑5Empty Blocks Should Be Removed Or Emit Something3-
GAS‑6Using fixed bytes is cheaper than using string4-
GAS‑7Functions guaranteed to revert when called by normal users can be marked payable18378
GAS‑8internal functions only called once can be inlined to save gas44-
GAS‑9Multiplication/division By Two Should Use Bit Shifting3-
GAS‑10Optimize names to save gas20440
GAS‑11<x> += <y> Costs More Gas Than <x> = <x> + <y> For State Variables6-
GAS‑12Public Functions To External45-
GAS‑13require() Should Be Used Instead Of assert()1-
GAS‑14require()/revert() Strings Longer Than 32 Bytes Cost Extra Gas19-
GAS‑15Splitting require() Statements That Use && Saves Gas327
GAS‑16Help The Optimizer By Saving A Storage Variable’s Reference Instead Of Repeatedly Fetching It2-
GAS‑17Usage of uints/ints smaller than 32 bytes (256 bits) incurs overhead11-
GAS‑18++i/i++ Should Be unchecked{++i}/unchecked{i++} When It Is Not Possible For Them To Overflow, As Is The Case When Used In For- And While-loops5175
GAS‑19Using unchecked blocks to save gas187344
GAS‑20Use solidity version 0.8.17 to gain some gas boost373256
GAS‑21Using storage instead of memory saves gas1350
GAS‑22Struct packing3-

Total: 263 contexts over 22 issues

Gas Optimizations

<a href="#Summary">[GAS‑1]</a><a name="GAS&#x2011;1"> abi.encode() is less efficient than abi.encodepacked()

See for more information: https://github.com/ConnorBlockchain/Solidity-Encode-Gas-Comparison

<ins>Proof Of Concept</ins>
136: return keccak256(abi.encode(DOMAIN_SEPARATOR_TYPEHASH, getChainId(), this));

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\SmartAccount.sol#L136

431: abi.encode(

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\SmartAccount.sol#L431

197: return keccak256(abi.encode(userOp.hash(), address(this), block.chainid));

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\aa-4337\core\EntryPoint.sol#L197

28: return abi.encode(data.paymasterId);

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\paymasters\PaymasterHelpers.sol#L28

80: return keccak256(abi.encode(

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\paymasters\verifying\singleton\VerifyingSingletonPaymaster.sol#L80

<a href="#Summary">[GAS‑2]</a><a name="GAS&#x2011;2"> State variables only set in the constructor should be declared immutable

Avoids a Gsset (20000 gas) in the constructor, and replaces each Gwarmacces (100 gas) with a PUSH32 (3 gas).

<ins>Proof Of Concept</ins>
19: _defaultImpl = _baseImpl

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\SmartAccountFactory.sol#L19

<a href="#Summary">[GAS‑3]</a><a name="GAS&#x2011;3"> Setting the constructor to payable

Saves ~13 gas per instance

<ins>Proof Of Concept</ins>
15: constructor(address _implementation)

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\Proxy.sol#L15

17: constructor(address _baseImpl)

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\SmartAccountFactory.sol#L17

20: constructor(IEntryPoint _entryPoint)

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\aa-4337\core\BasePaymaster.sol#L20

12: constructor()

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\libs\MultiSend.sol#L12

20: constructor(IEntryPoint _entryPoint)

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\paymasters\BasePaymaster.sol#L20

35: constructor(IEntryPoint _entryPoint, address _verifyingSigner) BasePaymaster(_entryPoint)

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\paymasters\verifying\singleton\VerifyingSingletonPaymaster.sol#L35

<a href="#Summary">[GAS‑4]</a><a name="GAS&#x2011;4"> Duplicated require()/revert() Checks Should Be Refactored To A Modifier Or Function

Saves deployment costs

<ins>Proof Of Concept</ins>
262: require(success, "BSA011");
286: require(success, "BSA011");
265: require(transferToken(gasToken, receiver, payment), "BSA012");
289: require(transferToken(gasToken, receiver, payment), "BSA012");
348: require(_signer == owner, "INVALID_SIGNATURE");
351: require(_signer == owner, "INVALID_SIGNATURE");

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\SmartAccount.sol#L262

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\SmartAccount.sol#L286

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\SmartAccount.sol#L265

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\SmartAccount.sol#L289

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\SmartAccount.sol#L348

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\SmartAccount.sol#L351

34: require(module != address(0) && module != SENTINEL_MODULES, "BSA101");
49: require(module != address(0) && module != SENTINEL_MODULES, "BSA101");

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\base\ModuleManager.sol#L34

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\base\ModuleManager.sol#L49

<a href="#Summary">[GAS‑5]</a><a name="GAS&#x2011;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{...}})

<ins>Proof Of Concept</ins>
550: receive() external payable {}

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\SmartAccount.sol#L550

119: try aggregator.validateSignatures(ops, opa.signature) {}

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\aa-4337\core\EntryPoint.sol#L119

458: try IPaymaster(paymaster).postOp{gas : mUserOp.verificationGasLimit}(mode, context, actualGasCost) {}

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\aa-4337\core\EntryPoint.sol#L458

<a href="#Summary">[GAS‑6]</a><a name="GAS&#x2011;6"> Using fixed bytes is cheaper than using string

As a rule of thumb, use bytes for arbitrary-length raw byte data and string for arbitrary-length string (UTF-8) data. If you can limit the length to a certain number of bytes, always use one of bytes1 to bytes32 because they are much cheaper.

<ins>Proof Of Concept</ins>
36: string public constant VERSION = "1.0.2"; // using AA 0.3.0

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\SmartAccount.sol#L36

11: string public constant VERSION = "1.0.2";

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\SmartAccountFactory.sol#L11

12: string public constant NAME = "Default Callback Handler";

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\handler\DefaultCallbackHandler.sol#L12

13: string public constant VERSION = "1.0.0";

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\handler\DefaultCallbackHandler.sol#L13

<a href="#Summary">[GAS‑7]</a><a name="GAS&#x2011;7"> Functions guaranteed to revert when called by normal users can be marked payable

If a function modifier or require such as onlyOwner/onlyX 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.

<ins>Proof Of Concept</ins>
449: function transfer(address payable dest, uint amount) external nonReentrant onlyOwner {

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\SmartAccount.sol#L449

455: function pullTokens(address token, address dest, uint256 amount) external onlyOwner {

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\SmartAccount.sol#L455

460: function execute(address dest, uint value, bytes calldata func) external onlyOwner{
465: function execute(address dest, uint value, bytes calldata func) external onlyOwner{

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\SmartAccount.sol#L460

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\SmartAccount.sol#L465

465: function executeBatch(address[] calldata dest, bytes[] calldata func) external onlyOwner{

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\SmartAccount.sol#L465

489: function execFromEntryPoint(address dest, uint value, bytes calldata func, Enum.Operation operation, uint256 gasLimit) external onlyEntryPoint returns (bool success) {

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\SmartAccount.sol#L489

536: function withdrawDepositTo(address payable withdrawAddress, uint256 amount) public onlyOwner {

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\SmartAccount.sol#L536

24: function setEntryPoint(IEntryPoint _entryPoint) public onlyOwner {

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\aa-4337\core\BasePaymaster.sol#L24

67: function withdrawTo(address payable withdrawAddress, uint256 amount) public onlyOwner {

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\aa-4337\core\BasePaymaster.sol#L67

75: function addStake(uint32 unstakeDelaySec) external payable onlyOwner {

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\aa-4337\core\BasePaymaster.sol#L75

90: function unlockStake() external onlyOwner {

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\aa-4337\core\BasePaymaster.sol#L90

99: function withdrawStake(address payable withdrawAddress) external onlyOwner {

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\aa-4337\core\BasePaymaster.sol#L99

24: function setEntryPoint(IEntryPoint _entryPoint) public onlyOwner {

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\paymasters\BasePaymaster.sol#L24

67: function withdrawTo(address payable withdrawAddress, uint256 amount) public virtual onlyOwner {

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\paymasters\BasePaymaster.sol#L67

75: function addStake(uint32 unstakeDelaySec) external payable onlyOwner {

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\paymasters\BasePaymaster.sol#L75

90: function unlockStake() external onlyOwner {

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\paymasters\BasePaymaster.sol#L90

99: function withdrawStake(address payable withdrawAddress) external onlyOwner {

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\paymasters\BasePaymaster.sol#L99

65: function setSigner( address _newVerifyingSigner) external onlyOwner{

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\paymasters\verifying\singleton\VerifyingSingletonPaymaster.sol#L65

<ins>Recommended Mitigation Steps</ins>

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

<a href="#Summary">[GAS‑8]</a><a name="GAS&#x2011;8"> internal functions only called once can be inlined to save gas

<ins>Proof Of Concept</ins>
73: function _requireFromEntryPoint

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\BaseSmartAccount.sol#L73

87: function _validateSignature

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\BaseSmartAccount.sol#L87

181: function max

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\SmartAccount.sol#L181

501: function _validateAndUpdateNonce

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\SmartAccount.sol#L501

506: function _validateSignature

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\SmartAccount.sol#L506

48: function _postOp

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\aa-4337\core\BasePaymaster.sol#L48

104: function _requireFromEntryPoint

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\aa-4337\core\BasePaymaster.sol#L104

203: function _copyUserOpToMemory

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\aa-4337\core\EntryPoint.sol#L203

248: function _getRequiredPrefund

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\aa-4337\core\EntryPoint.sol#L248

261: function _createSenderIfNeeded

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\aa-4337\core\EntryPoint.sol#L261

289: function _validateAccountPrepayment

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\aa-4337\core\EntryPoint.sol#L289

349: function _validatePaymasterPrepayment

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\aa-4337\core\EntryPoint.sol#L349

496: function min

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\aa-4337\core\EntryPoint.sol#L496

500: function getOffsetOfMemoryBytes

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\aa-4337\core\EntryPoint.sol#L500

504: function getMemoryBytesFromOffset

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\aa-4337\core\EntryPoint.sol#L504

23: function getStakeInfo

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\aa-4337\core\StakeManager.sol#L23

38: function internalIncrementDeposit

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\aa-4337\core\StakeManager.sol#L38

36: function getSender

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\aa-4337\interfaces\UserOperation.sol#L36

45: function gasPrice

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\aa-4337\interfaces\UserOperation.sol#L45

57: function pack

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\aa-4337\interfaces\UserOperation.sol#L57

73: function hash

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\aa-4337\interfaces\UserOperation.sol#L73

77: function min

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\aa-4337\interfaces\UserOperation.sol#L77

19: function staticcall

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\aa-4337\utils\Exec.sol#L19

29: function delegateCall

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\aa-4337\utils\Exec.sol#L29

40: function getReturnData

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\aa-4337\utils\Exec.sol#L40

51: function revertWithData

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\aa-4337\utils\Exec.sol#L51

57: function callAndRevert

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\aa-4337\utils\Exec.sol#L57

13: function execute

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\base\Executor.sol#L13

14: function internalSetFallbackHandler

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\base\FallbackManager.sol#L14

20: function setupModules

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\base\ModuleManager.sol#L20

10: function transferToken

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\common\SecuredTokenTransfer.sol#L10

12: function _setImplementation

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\common\Singleton.sol#L12

20: function _getImplementation

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\common\Singleton.sol#L20

11: function isContract

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\libs\LibAddress.sol#L11

19: function max

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\libs\Math.sol#L19

26: function min

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\libs\Math.sol#L26

34: function average

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\libs\Math.sol#L34

45: function ceilDiv

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\libs\Math.sol#L45

48: function _postOp

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\paymasters\BasePaymaster.sol#L48

104: function _requireFromEntryPoint

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\paymasters\BasePaymaster.sol#L104

24: function paymasterContext

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\paymasters\PaymasterHelpers.sol#L24

34: function decodePaymasterData

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\paymasters\PaymasterHelpers.sol#L34

43: function decodePaymasterContext

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\paymasters\PaymasterHelpers.sol#L43

119: function _postOp

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\paymasters\verifying\singleton\VerifyingSingletonPaymaster.sol#L119

<a href="#Summary">[GAS‑9]</a><a name="GAS&#x2011;9"> Multiplication/division By Two Should Use Bit Shifting

<x> * 2 is equivalent to <x> << 1 and <x> / 2 is the same as <x> >> 1. The MUL and DIV opcodes cost 5 gas, whereas SHL and SHR only cost 3 gas

<ins>Proof Of Concept</ins>
36: return (a & b) + (a ^ b) / 2;

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\libs\Math.sol#L36

281: if (value >= 10**2) {

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\libs\Math.sol#L281

282: value /= 10**2;

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\libs\Math.sol#L282

<a href="#Summary">[GAS‑10]</a><a name="GAS&#x2011;10"> Optimize names to save gas

Contracts most called functions could simply save gas by function ordering via Method ID. Calling a function at runtime will be cheaper if the function is positioned earlier in the order (has a relatively lower Method ID) because 22 gas are added to the cost of a function for every position that came before it. The caller can save on gas if you prioritize most called functions.

See more <a href="https://medium.com/joyso/solidity-how-does-function-name-affect-gas-consumption-in-smart-contract-47d270d8ac92">here</a>

<ins>Proof Of Concept</ins>

All in-scope contracts

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\paymasters\verifying\singleton\VerifyingSingletonPaymaster.sol

<ins>Recommended Mitigation Steps</ins>

Find a lower method ID name for the most called functions for example Call() vs. Call1() is cheaper by 22 gas For example, the function IDs in the Gauge.sol contract will be the most used; A lower method ID may be given.

<a href="#Summary">[GAS‑11]</a><a name="GAS&#x2011;11"> <x> += <y> Costs More Gas Than <x> = <x> + <y> For State Variables

<ins>Proof Of Concept</ins>
81: collected += _executeUserOp(i, ops[i], opInfos[i]);

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\aa-4337\core\EntryPoint.sol#L81

101: totalOps += opsPerAggregator[i].userOps.length;
135: collected += _executeUserOp(opIndex, ops[i], opInfos[opIndex]);

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\aa-4337\core\EntryPoint.sol#L101

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\aa-4337\core\EntryPoint.sol#L135

468: actualGas += preGas - gasleft();

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\aa-4337\core\EntryPoint.sol#L468

148: result += 1;

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\libs\Math.sol#L148

51: paymasterIdBalances[paymasterId] += msg.value;

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\paymasters\verifying\singleton\VerifyingSingletonPaymaster.sol#L51

58: paymasterIdBalances[msg.sender] -= amount;

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\paymasters\verifying\singleton\VerifyingSingletonPaymaster.sol#L58

128: paymasterIdBalances[extractedPaymasterId] -= actualGasCost;

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\paymasters\verifying\singleton\VerifyingSingletonPaymaster.sol#L128

<a href="#Summary">[GAS‑12]</a><a name="GAS&#x2011;12"> Public Functions To External

The following functions could be set external to save gas and improve code quality. External call cost is less expensive than of public functions.

<ins>Proof Of Concept</ins>
function nonce() public view virtual returns (uint256);

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\BaseSmartAccount.sol#L41

function nonce() public view virtual override returns (uint256) {

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\SmartAccount.sol#L93

function nonce(uint256 _batchId) public view virtual override returns (uint256) {

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\SmartAccount.sol#L93

function entryPoint() public view virtual override returns (IEntryPoint) {

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\SmartAccount.sol#L101

function domainSeparator() public view returns (bytes32) {

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\SmartAccount.sol#L135

function getChainId() public view returns (uint256) {

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\SmartAccount.sol#L140

function getNonce(uint256 batchId)
    public view
    returns (uint256) {

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\SmartAccount.sol#L155

function init(address _owner, address _entryPointAddress, address _handler) public override initializer {

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\SmartAccount.sol#L166

function execTransaction(
        Transaction memory _tx,
        uint256 batchId,
        FeeRefund memory refundInfo,
        bytes memory signatures
    ) public payable virtual override returns (bool success) {

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\SmartAccount.sol#L192

function checkSignatures(
        bytes32 dataHash,
        bytes memory data,
        bytes memory signatures
    ) public view virtual {

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\SmartAccount.sol#L302

function getTransactionHash(
        address to,
        uint256 value,
        bytes calldata data,
        Enum.Operation operation,
        uint256 targetTxGas,
        uint256 baseGas,
        uint256 gasPrice,
        uint256 tokenGasPriceFactor,
        address gasToken,
        address payable refundReceiver,
        uint256 _nonce
    ) public view returns (bytes32) {

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\SmartAccount.sol#L389

function encodeTransactionData(
        Transaction memory _tx,
        FeeRefund memory refundInfo,
        uint256 _nonce
    ) public view returns (bytes memory) {

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\SmartAccount.sol#L424

function getDeposit() public view returns (uint256) {

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\SmartAccount.sol#L518

function addDeposit() public payable {

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\SmartAccount.sol#L525

function withdrawDepositTo(address payable withdrawAddress, uint256 amount) public onlyOwner {

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\SmartAccount.sol#L536

function deployCounterFactualWallet(address _owner, address _entryPoint, address _handler, uint _index) public returns(address proxy){

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\SmartAccountFactory.sol#L33

function deployWallet(address _owner, address _entryPoint, address _handler) public returns(address proxy){

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\SmartAccountFactory.sol#L53

function setEntryPoint(IEntryPoint _entryPoint) public onlyOwner {

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\aa-4337\core\BasePaymaster.sol#L24

function deposit() public payable {

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\aa-4337\core\BasePaymaster.sol#L58

function withdrawTo(address payable withdrawAddress, uint256 amount) public onlyOwner {

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\aa-4337\core\BasePaymaster.sol#L67

function getDeposit() public view returns (uint256) {

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\aa-4337\core\BasePaymaster.sol#L82

function handleOps(UserOperation[] calldata ops, address payable beneficiary) public {

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\aa-4337\core\EntryPoint.sol#L68

function handleAggregatedOps(
        UserOpsPerAggregator[] calldata opsPerAggregator,
        address payable beneficiary
    ) public {

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\aa-4337\core\EntryPoint.sol#L93

function getUserOpHash(UserOperation calldata userOp) public view returns (bytes32) {

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\aa-4337\core\EntryPoint.sol#L196

function getSenderAddress(bytes calldata initCode) public {

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\aa-4337\core\EntryPoint.sol#L280

function getDepositInfo(address account) public view returns (DepositInfo memory info) {

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\aa-4337\core\StakeManager.sol#L18

function balanceOf(address account) public view returns (uint256) {

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\aa-4337\core\StakeManager.sol#L30

function depositTo(address account) public payable {

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\aa-4337\core\StakeManager.sol#L48

function addStake(uint32 _unstakeDelaySec) public payable {

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\aa-4337\core\StakeManager.sol#L59

function setFallbackHandler(address handler) public authorized {

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\base\FallbackManager.sol#L26

function enableModule(address module) public authorized {

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\base\ModuleManager.sol#L32

function disableModule(address prevModule, address module) public authorized {

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\base\ModuleManager.sol#L47

function execTransactionFromModule(
        address to,
        uint256 value,
        bytes memory data,
        Enum.Operation operation
    ) public virtual returns (bool success) {

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\base\ModuleManager.sol#L61

function execTransactionFromModuleReturnData(
        address to,
        uint256 value,
        bytes memory data,
        Enum.Operation operation
    ) public returns (bool success, bytes memory returnData) {

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\base\ModuleManager.sol#L80

function isModuleEnabled(address module) public view returns (bool) {

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\base\ModuleManager.sol#L105

function multiSend(bytes memory transactions) public payable {

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\libs\MultiSend.sol#L26

function multiSend(bytes memory transactions) public payable {

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\libs\MultiSendCallOnly.sol#L21

function setEntryPoint(IEntryPoint _entryPoint) public onlyOwner {

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\paymasters\BasePaymaster.sol#L24

function deposit() public virtual payable {

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\paymasters\BasePaymaster.sol#L58

function withdrawTo(address payable withdrawAddress, uint256 amount) public virtual onlyOwner {

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\paymasters\BasePaymaster.sol#L67

function getDeposit() public view returns (uint256) {

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\paymasters\BasePaymaster.sol#L82

function deposit() public virtual override payable {

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\paymasters\verifying\singleton\VerifyingSingletonPaymaster.sol#L41

function depositFor(address paymasterId) public payable {

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\paymasters\verifying\singleton\VerifyingSingletonPaymaster.sol#L48

function withdrawTo(address payable withdrawAddress, uint256 amount) public override {

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\paymasters\verifying\singleton\VerifyingSingletonPaymaster.sol#L55

function getHash(UserOperation calldata userOp)
    public pure returns (bytes32) {

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\paymasters\verifying\singleton\VerifyingSingletonPaymaster.sol#L77

<a href="#Summary">[GAS‑13]</a><a name="GAS&#x2011;13"> require() Should Be Used Instead Of assert()

<ins>Proof Of Concept</ins>
13: assert(_IMPLEMENTATION_SLOT == bytes32(uint256(keccak256("biconomy.scw.proxy.implementation")) - 1));

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\common\Singleton.sol#L13

<a href="#Summary">[GAS‑14]</a><a name="GAS&#x2011;14"> require()/revert() Strings Longer Than 32 Bytes Cost Extra Gas

<ins>Proof Of Concept</ins>
74: require(msg.sender == address(entryPoint()), "account: not from EntryPoint");

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\BaseSmartAccount.sol#L74

110: require(_newOwner != address(0), "Smart Account:: new Signatory address cannot be zero");

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\SmartAccount.sol#L110

128: require(_newEntryPoint != address(0), "Smart Account:: new entry point address cannot be zero");

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\SmartAccount.sol#L128

342: require(ISignatureValidator(_signer).isValidSignature(data, contractSignature) == EIP1271_MAGIC_VALUE, "BSA024");

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\SmartAccount.sol#L342

450: require(dest != address(0), "this action will burn your funds");

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\SmartAccount.sol#L450

495: require(msg.sender == address(entryPoint()) || msg.sender == owner, "account: not Owner or EntryPoint");

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\SmartAccount.sol#L495

38: require(success, "AA91 failed send to beneficiary");

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\aa-4337\core\EntryPoint.sol#L38

213: require(paymasterAndData.length >= 20, "AA93 invalid paymasterAndData");

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\aa-4337\core\EntryPoint.sol#L213

353: require(verificationGasLimit > gasUsedByValidateAccountPrepayment, "AA41 too little verificationGas");

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\aa-4337\core\EntryPoint.sol#L353

62: require(_unstakeDelaySec >= info.unstakeDelaySec, "cannot decrease unstake time");

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\aa-4337\core\StakeManager.sol#L62

100: require(info.withdrawTime > 0, "must call unlockStake() first");

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\aa-4337\core\StakeManager.sol#L100

27: require(address(this) != multisendSingleton, "MultiSend should only be called via delegatecall");

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\libs\MultiSend.sol#L27

49: require(!Address.isContract(paymasterId), "Paymaster Id can not be smart contract address");

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\paymasters\verifying\singleton\VerifyingSingletonPaymaster.sol#L49

50: require(paymasterId != address(0), "Paymaster Id can not be zero address");

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\paymasters\verifying\singleton\VerifyingSingletonPaymaster.sol#L50

57: require(amount <= currentBalance, "Insufficient amount to withdraw");

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\paymasters\verifying\singleton\VerifyingSingletonPaymaster.sol#L57

66: require(_newVerifyingSigner != address(0), "VerifyingPaymaster: new signer can not be zero address");

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\paymasters\verifying\singleton\VerifyingSingletonPaymaster.sol#L66

107: require(sigLength == 64 || sigLength == 65, "VerifyingPaymaster: invalid signature length in paymasterAndData");

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\paymasters\verifying\singleton\VerifyingSingletonPaymaster.sol#L107

108: require(verifyingSigner == hash.toEthSignedMessageHash().recover(paymasterData.signature), "VerifyingPaymaster: wrong signature");

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\paymasters\verifying\singleton\VerifyingSingletonPaymaster.sol#L108

109: require(requiredPreFund <= paymasterIdBalances[paymasterData.paymasterId], "Insufficient balance for paymaster id");

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\paymasters\verifying\singleton\VerifyingSingletonPaymaster.sol#L109

<a href="#Summary">[GAS‑15]</a><a name="GAS&#x2011;15"> Splitting require() statements that use && saves gas

Instead of using operator && on a single require. Using a two require can save more gas.

i.e. for require(version == 1 && _bytecodeHash[1] == bytes1(0), "zf"); use:

require(version == 1); require(_bytecodeHash[1] == bytes1(0));
<ins>Proof Of Concept</ins>
34: require(module != address(0) && module != SENTINEL_MODULES, "BSA101");

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\base\ModuleManager.sol#L34

49: require(module != address(0) && module != SENTINEL_MODULES, "BSA101");

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\base\ModuleManager.sol#L49

68: require(msg.sender != SENTINEL_MODULES && modules[msg.sender] != address(0), "BSA104");

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\base\ModuleManager.sol#L68

<a href="#Summary">[GAS‑16]</a><a name="GAS&#x2011;16"> Help The Optimizer By Saving A Storage Variable's Reference Instead Of Repeatedly Fetching It

To help the optimizer, declare a storage type variable and use it instead of repeatedly fetching the reference in a map or an array. The effect can be quite significant. As an example, instead of repeatedly calling someMap[someIndex], save its reference like this: SomeStruct storage someStruct = someMap[someIndex] and use it.

<ins>Proof Of Concept</ins>
113: _validatePrepayment(opIndex, ops[i], opInfos[opIndex], address(aggregator));

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\aa-4337\core\EntryPoint.sol#L113

135: collected += _executeUserOp(opIndex, ops[i], opInfos[opIndex]);

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\aa-4337\core\EntryPoint.sol#L135

<a href="#Summary">[GAS‑17]</a><a name="GAS&#x2011;17"> Usage of uints/ints 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.

https://docs.soliditylang.org/en/v0.8.11/internals/layout_in_storage.html Each operation involving a uint8 costs an extra 22-28 gas (depending on whether the other operand is also a variable of type uint8) as compared to ones involving uint256, due to the compiler having to clear the higher bits of the memory word before operating on the uint8, as well as the associated stack operations of doing so. Use a larger size then downcast where needed

<ins>Proof Of Concept</ins>
88: internal virtual returns (uint256 deadline);

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\BaseSmartAccount.sol#L88

307: uint8 v;

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\SmartAccount.sol#L307

474: internalIncrementDeposit(refundAddress, refund);

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\aa-4337\core\EntryPoint.sol#L474

49: internalIncrementDeposit(account, msg.value);

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\aa-4337\core\StakeManager.sol#L49

84: uint64 withdrawTime = uint64(block.timestamp) + info.unstakeDelaySec;

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\aa-4337\core\StakeManager.sol#L84

54: uint112 deposit;

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\aa-4337\interfaces\IStakeManager.sol#L54

56: uint112 stake;

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\aa-4337\interfaces\IStakeManager.sol#L56

57: uint32 unstakeDelaySec;

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\aa-4337\interfaces\IStakeManager.sol#L57

58: uint64 withdrawTime;

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\aa-4337\interfaces\IStakeManager.sol#L58

27: internalSetFallbackHandler(handler);

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\base\FallbackManager.sol#L27

59: interfaceId == type(IERC165).interfaceId;

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\handler\DefaultCallbackHandler.sol#L59

<a href="#Summary">[GAS‑18]</a><a name="GAS&#x2011;18"> ++i/i++ Should Be unchecked{++i}/unchecked{i++} When It Is Not Possible For Them To Overflow, As Is The Case When Used In For- And While-loops

The unchecked keyword is new in solidity version 0.8.0, so this only applies to that version or higher, which these instances are. This saves 30-40 gas PER LOOP

<ins>Proof Of Concept</ins>
100: for (uint256 i = 0; i < opasLen; i++) {

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\aa-4337\core\EntryPoint.sol#L100

107: for (uint256 a = 0; a < opasLen; a++) {

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\aa-4337\core\EntryPoint.sol#L107

112: for (uint256 i = 0; i < opslen; i++) {

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\aa-4337\core\EntryPoint.sol#L112

<a href="#Summary">[GAS‑19]</a><a name="GAS&#x2011;19"> Using unchecked blocks to save gas

Solidity version 0.8+ comes with implicit overflow and underflow checks on unsigned integers. When an overflow or an underflow isn’t possible (as an example, when a comparison is made before the arithmetic operation), some gas can be saved by using an unchecked block

<ins>Proof Of Concept</ins>
239: payment = handlePayment(startGas - gasleft(), refundInfo.baseGas, refundInfo.gasPrice, refundInfo.tokenGasPriceFactor, refundInfo.gasToken, refundInfo.refundReceiver);

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\SmartAccount.sol#L239

267: uint256 requiredGas = startGas - gasleft();
291: uint256 requiredGas = startGas - gasleft();
372: uint256 requiredGas = startGas - gasleft();

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\SmartAccount.sol#L267

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\SmartAccount.sol#L291

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\SmartAccount.sol#L372

347: _signer = ecrecover(keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", dataHash)), v - 4, r, s);

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\SmartAccount.sol#L347

267: uint256 requiredGas = startGas - gasleft();
291: uint256 requiredGas = startGas - gasleft();
372: uint256 requiredGas = startGas - gasleft();

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\SmartAccount.sol#L267

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\SmartAccount.sol#L291

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\SmartAccount.sol#L372

55: uint256 actualGas = preGas - gasleft() + opInfo.preOpGas;
186: uint256 actualGas = preGas - gasleft() + opInfo.preOpGas;

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\aa-4337\core\EntryPoint.sol#L55

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\aa-4337\core\EntryPoint.sol#L186

317: missingAccountFunds = bal > requiredPrefund ? 0 : requiredPrefund - bal;
336: senderInfo.deposit = uint112(deposit - requiredPrefund);
338: gasUsedByValidateAccountPrepayment = preGas - gasleft();

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\aa-4337\core\EntryPoint.sol#L317

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\aa-4337\core\EntryPoint.sol#L336

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\aa-4337\core\EntryPoint.sol#L338

354: uint256 gas = verificationGasLimit - gasUsedByValidateAccountPrepayment;
362: paymasterInfo.deposit = uint112(deposit - requiredPreFund);

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\aa-4337\core\EntryPoint.sol#L354

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\aa-4337\core\EntryPoint.sol#L362

418: uint256 gasUsed = preGas - gasleft();
425: outOpInfo.preOpGas = preGas - gasleft() + userOp.preVerificationGas;

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\aa-4337\core\EntryPoint.sol#L418

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\aa-4337\core\EntryPoint.sol#L425

118: info.deposit = uint112(info.deposit - withdrawAmount);

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\aa-4337\core\StakeManager.sol#L118

<a href="#Summary">[GAS‑20]</a><a name="GAS&#x2011;20"> Use solidity version 0.8.17 to gain some gas boost

Upgrade to the latest solidity version 0.8.17 to get additional gas savings.

<ins>Proof Of Concept</ins>

All in-scope contracts

<a href="#Summary">[GAS‑21]</a><a name="GAS&#x2011;21"> Using storage instead of memory saves gas

When fetching data from a storage location, assigning the data to a memory variable causes all fields of the struct/array to be read from storage, which incurs a Gcoldsload (2100 gas) for each field of the struct/array. If the fields are read from the new memory variable, they incur an additional MLOAD rather than a cheap stack read. Instead of declearing the variable with the memory keyword, declaring the variable with the storage keyword and caching any fields that need to be re-read in stack variables, will be much cheaper, only incuring the Gcoldsload for the fields actually read. The only time it makes sense to read the whole struct/array into a memory variable, is if the full struct/array is being returned by the function, is being passed to a function that requires memory, or if the array/struct is being read from another memory array/struct

<ins>Proof Of Concept</ins>
17: bytes memory initCallData = initCode[20 :];

https://github.com/code-423n4/2023-01-biconomy/tree/main/scw-contracts/contracts\smart-contract-wallet\aa-4337\core\SenderCreator.sol#L17

<a href="#Summary">[GAS‑22]</a><a name="GAS&#x2011;22"> Struct packing

Entrypoint.sol

  145:     struct MemoryUserOp {
  146:         address sender;
  147:         uint256 nonce;
  148:         uint256 callGasLimit;
  149:         uint256 verificationGasLimit;
  150:         uint256 preVerificationGas;
  151:         address paymaster;
  152:         uint256 maxFeePerGas;
  153:         uint256 maxPriorityFeePerGas;
  154:     }

Nonce unlikely to reach max uint256. Can set to uint96 and thus saving 1 storage slot.

  145:     struct MemoryUserOp {
  146:         address sender;
  147:         uint96 nonce;
  148:         uint256 callGasLimit;
  149:         uint256 verificationGasLimit;
  150:         uint256 preVerificationGas;
  151:         address paymaster;
  152:         uint256 maxFeePerGas;
  153:         uint256 maxPriorityFeePerGas;
  154:     }

IStakeManager.sol

   62:     struct StakeInfo {
   63:         uint256 stake;
   64:         uint256 unstakeDelaySec;
   65:     }

stake is seen to be used in struct DepositInfo with uint112 and unstakeDelaySec with uint32. Hence, we can save 1 storage slot by changing to:

   62:     struct StakeInfo {
   63:         uint112 stake;
   64:         uint32 unstakeDelaySec;
   65:     }

UserOperation.sol

   19:     struct UserOperation {
   20: 
   21:         address sender;
   22:         uint256 nonce;
   23:         bytes initCode;
   24:         bytes callData;
   25:         uint256 callGasLimit;
   26:         uint256 verificationGasLimit;
   27:         uint256 preVerificationGas;
   28:         uint256 maxFeePerGas;
   29:         uint256 maxPriorityFeePerGas;
   30:         bytes paymasterAndData;
   31:         bytes signature;
   32:     }

Nonce unlikely to reach max uint256. Can set to uint96 and thus saving 1 storage slot.

   19:     struct UserOperation {
   20: 
   21:         address sender;
   22:         uint96 nonce;
   23:         bytes initCode;
   24:         bytes callData;
   25:         uint256 callGasLimit;
   26:         uint256 verificationGasLimit;
   27:         uint256 preVerificationGas;
   28:         uint256 maxFeePerGas;
   29:         uint256 maxPriorityFeePerGas;
   30:         bytes paymasterAndData;
   31:         bytes signature;
   32:     }

#0 - c4-judge

2023-01-22T16:08:12Z

gzeon-c4 marked the issue as grade-a

#1 - c4-sponsor

2023-02-09T12:35:53Z

livingrockrises marked the issue as sponsor confirmed

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