Biconomy - Smart Contract Wallet contest - IllIllI'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: 27/105

Findings: 3

Award: $444.60

QA:
grade-a
Gas:
grade-b

🌟 Selected for report: 0

šŸš€ Solo Findings: 0

Findings Information

Labels

2 (Med Risk)
satisfactory
duplicate-261

Awards

44.8261 USDC - $44.83

External Links

Judge has assessed an item in Issue #362 as 2 risk. The relevant finding follows:

[N‑01] Upgradeable contract is missing a __gap[50] storage variable to allow for new storage variables in later versions

#0 - c4-judge

2023-02-12T16:25:50Z

gzeon-c4 marked the issue as duplicate of #261

#1 - c4-judge

2023-02-12T16:25:56Z

gzeon-c4 marked the issue as satisfactory

Summary

Low Risk Issues

IssueInstances
[L‑01]Upgradeable contract not initialized1
[L‑02]Not all parent contracts are upgradeable4
[L‑03]Loss of precision1
[L‑04]Signatures vulnerable1
[L‑05]Empty receive()/payable fallback() function does not authorize requests1
[L‑06]require() should be used instead of assert()2
[L‑07]Open TODOs1

Total: 11 instances over 7 issues

Non-critical Issues

IssueInstances
[N‑01]Upgradeable contract is missing a __gap[50] storage variable to allow for new storage variables in later versions1
[N‑02]Import declarations should import specific identifiers, rather than the whole file54
[N‑03]Missing initializer modifier on constructor1
[N‑04]Unused file2
[N‑05]Adding a return statement when the function defines a named return variable, is redundant1
[N‑06]override function arguments that are unused should have the variable name removed or commented out to avoid compiler warnings3
[N‑07]Non-assembly method available2
[N‑08]constants should be defined rather than using magic numbers192
[N‑09]Missing event and or timelock for critical parameter change2
[N‑10]Events that mark critical parameter changes should contain both the old and the new value3
[N‑11]Use a more recent version of solidity5
[N‑12]Use scientific notation (e.g. 1e18) rather than exponentiation (e.g. 10**18)13
[N‑13]Constant redefined elsewhere2
[N‑14]Inconsistent spacing in comments30
[N‑15]Lines are too long12
[N‑16]Non-library/interface files should use fixed compiler versions, not floating ones2
[N‑17]Typos3
[N‑18]Misplaced SPDX identifier2
[N‑19]File is missing NatSpec7
[N‑20]NatSpec is incomplete17
[N‑21]Not using the named return variables anywhere in the function is confusing5
[N‑22]Duplicated require()/revert() checks should be refactored to a modifier or function5
[N‑23]Consider using delete rather than assigning zero to clear values3
[N‑24]Avoid the use of sensitive terms87
[N‑25]Large assembly blocks should have extensive comments5
[N‑26]Contracts should have full test coverage1
[N‑27]Large or complicated code bases should implement fuzzing tests1
[N‑28]Function ordering does not follow the Solidity style guide31
[N‑29]Contract does not follow the Solidity style guide's suggested layout ordering3
[N‑30]Interfaces should be indicated with an I prefix in the contract name3

Total: 498 instances over 30 issues

Low Risk Issues

[L‑01] Upgradeable contract not initialized

Upgradeable contracts are initialized via an initializer function rather than by a constructor. Leaving such a contract uninitialized may lead to it being taken over by a malicious user

There is 1 instance of this issue:

File: scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol

/// @audit missing __ReentrancyGuard_init()
18:   contract SmartAccount is 

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol#L18

[L‑02] Not all parent contracts are upgradeable

Upgrades will break if the contracts in question have variables whose values are initialized in the constructor, or have variables where initial values are set in field declarations

There are 4 instances of this issue:

File: scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol

/// @audit ModuleManager
/// @audit SignatureDecoder
/// @audit SecuredTokenTransfer
/// @audit FallbackManager
18:   contract SmartAccount is 

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol#L18

[L‑03] Loss of precision

Division by large numbers may result in the result being zero, due to solidity not supporting fractions. Consider requiring a minimum amount for the numerator to ensure that it is always larger than the denominator

There is 1 instance of this issue:

File: scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol

288:              payment = (gasUsed + baseGas) * (gasPrice) / (tokenGasPriceFactor);

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol#L288

[L‑04] Signatures vulnerable

ecrecover() accepts as valid, two versions of signatures, meaning an attacker can use the same signature twice. Consider adding checks for signature malleability, or using OpenZeppelin's ECDSA library to perform the extra checks necessary in order to prevent this attack. In addition, non-zero must be checked for as the resulting signer, or else invalid signatures may be useable

There is 1 instance of this issue:

File: scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol

350:              _signer = ecrecover(dataHash, v, r, s);

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

[L‑05] Empty receive()/payable fallback() function does not authorize requests

If the intention is for the Ether to be used, the function should call another function, otherwise it should revert (e.g. require(msg.sender == address(weth))). Having no access control on the function means that someone may send Ether to the contract, and have no way to get anything back out, which is a loss of funds. If the concern is having to spend a small amount of gas to check the sender against an immutable address, the code should at least have a function to rescue unused Ether.

There is 1 instance of this issue:

File: scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol

550:      receive() external payable {}

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

[L‑06] 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 documentation 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".

There are 2 instances of this issue:

File: scw-contracts/contracts/smart-contract-wallet/common/Singleton.sol

13:           assert(_IMPLEMENTATION_SLOT == bytes32(uint256(keccak256("biconomy.scw.proxy.implementation")) - 1));

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

File: scw-contracts/contracts/smart-contract-wallet/Proxy.sol

16:            assert(_IMPLEMENTATION_SLOT == bytes32(uint256(keccak256("biconomy.scw.proxy.implementation")) - 1));

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/Proxy.sol#L16

[L‑07] Open TODOs

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

There is 1 instance of this issue:

File: scw-contracts/contracts/smart-contract-wallet/aa-4337/core/EntryPoint.sol

255:          // TODO: copy logic of gasPrice?

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

Non-critical Issues

[N‑01] Upgradeable contract is missing a __gap[50] storage variable to allow for new storage variables in later versions

See this link 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.

There is 1 instance of this issue:

File: scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol

18    contract SmartAccount is 
19         Singleton,
20         BaseSmartAccount,
21         IERC165,
22         ModuleManager,
23         SignatureDecoder,
24         SecuredTokenTransfer,
25         ISignatureValidatorConstants,
26         FallbackManager,
27         Initializable,
28:        ReentrancyGuardUpgradeable

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol#L18-L28

[N‑02] Import declarations should import specific identifiers, rather than the whole file

Using import declarations of the form import {<identifier_name>} from "some/file.sol" avoids polluting the symbol namespace making flattened files smaller, and speeds up compilation

There are 54 instances of this issue:

File: scw-contracts/contracts/smart-contract-wallet/aa-4337/core/EntryPoint.sol

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/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/aa-4337/core/EntryPoint.sol#L12

File: scw-contracts/contracts/smart-contract-wallet/aa-4337/core/StakeManager.sol

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

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

File: scw-contracts/contracts/smart-contract-wallet/aa-4337/interfaces/IAccount.sol

4:    import "./UserOperation.sol";

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

File: scw-contracts/contracts/smart-contract-wallet/aa-4337/interfaces/IAggregatedAccount.sol

4:    import "./UserOperation.sol";

5:    import "./IAccount.sol";

6:    import "./IAggregator.sol";

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

File: scw-contracts/contracts/smart-contract-wallet/aa-4337/interfaces/IAggregator.sol

4:    import "./UserOperation.sol";

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

File: scw-contracts/contracts/smart-contract-wallet/aa-4337/interfaces/IEntryPoint.sol

12:   import "./UserOperation.sol";

13:   import "./IStakeManager.sol";

14:   import "./IAggregator.sol";

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

File: scw-contracts/contracts/smart-contract-wallet/aa-4337/interfaces/IPaymaster.sol

4:    import "./UserOperation.sol";

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

File: scw-contracts/contracts/smart-contract-wallet/base/Executor.sol

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

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

File: scw-contracts/contracts/smart-contract-wallet/base/FallbackManager.sol

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

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

File: scw-contracts/contracts/smart-contract-wallet/base/ModuleManager.sol

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

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

6:    import "./Executor.sol";

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/base/ModuleManager.sol#L4

File: scw-contracts/contracts/smart-contract-wallet/BaseSmartAccount.sol

8:    import "@account-abstraction/contracts/interfaces/IAccount.sol";

9:    import "@account-abstraction/contracts/interfaces/IEntryPoint.sol";

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

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/BaseSmartAccount.sol#L8

File: scw-contracts/contracts/smart-contract-wallet/handler/DefaultCallbackHandler.sol

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/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/handler/DefaultCallbackHandler.sol#L4

File: scw-contracts/contracts/smart-contract-wallet/paymasters/BasePaymaster.sol

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

8:    import "@account-abstraction/contracts/interfaces/IPaymaster.sol";

9:    import "@account-abstraction/contracts/interfaces/IEntryPoint.sol";

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

File: scw-contracts/contracts/smart-contract-wallet/paymasters/PaymasterHelpers.sol

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

5:    import "@account-abstraction/contracts/interfaces/UserOperation.sol";

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

File: scw-contracts/contracts/smart-contract-wallet/paymasters/verifying/singleton/VerifyingSingletonPaymaster.sol

5:    import "../../BasePaymaster.sol";

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

7:    import "@openzeppelin/contracts/proxy/utils/Initializable.sol";

8:    import "@openzeppelin/contracts/utils/Address.sol";

9:    import "../../PaymasterHelpers.sol";

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

File: scw-contracts/contracts/smart-contract-wallet/SmartAccountFactory.sol

4:    import "./Proxy.sol";

5:    import "./BaseSmartAccount.sol"; 

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/SmartAccountFactory.sol#L4

File: scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol

4:    import "./libs/LibAddress.sol";

5:    import "@openzeppelin/contracts/token/ERC20/IERC20.sol";

6:    import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";

7:    import "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.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";

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

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol#L4

[N‑03] Missing initializer modifier on constructor

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

There is 1 instance of this issue:

File: scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol

27:        Initializable,

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol#L27

[N‑04] Unused file

The file is never imported by any other file

There are 2 instances of this issue:

File: scw-contracts/contracts/smart-contract-wallet/interfaces/IERC1271Wallet.sol

1:    // SPDX-License-Identifier: Apache-2.0

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/interfaces/IERC1271Wallet.sol#L1

File: scw-contracts/contracts/smart-contract-wallet/libs/Math.sol

1:    // SPDX-License-Identifier: MIT

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/libs/Math.sol#L1

[N‑05] Adding a return statement when the function defines a named return variable, is redundant

There is 1 instance of this issue:

File: scw-contracts/contracts/smart-contract-wallet/libs/Math.sol

133:              return result;

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/libs/Math.sol#L133

[N‑06] override function arguments that are unused should have the variable name removed or commented out to avoid compiler warnings

There are 3 instances of this issue:

File: scw-contracts/contracts/smart-contract-wallet/paymasters/BasePaymaster.sol

28:       function validatePaymasterUserOp(UserOperation calldata userOp, bytes32 userOpHash, uint256 maxCost)

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/paymasters/BasePaymaster.sol#L28

[N‑07] Non-assembly method available

assembly{ id := chainid() } => uint256 id = block.chainid, assembly { size := extcodesize() } => uint256 size = address().code.length, assembly { hash := extcodehash() } => bytes32 hash = address().codehash There are some automated tools that will flag a project as having higher complexity if there is inline-assembly, so it's best to avoid using it where it's not necessary

There are 2 instances of this issue:

File: scw-contracts/contracts/smart-contract-wallet/libs/LibAddress.sol

14:       assembly { csize := extcodesize(account) }

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/libs/LibAddress.sol#L14

File: scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol

144:              id := chainid()

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol#L144

[N‑08] constants should be defined rather than using magic numbers

Even assembly can benefit from using readable constants instead of hex/numeric literals

There are 192 instances of this issue:

File: scw-contracts/contracts/smart-contract-wallet/aa-4337/core/EntryPoint.sol

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

/// @audit 20
214:              mUserOp.paymaster = address(bytes20(paymasterAndData[: 20]));

/// @audit 20
/// @audit 20
237:          address factory = initCode.length >= 20 ? address(bytes20(initCode[0 : 20])) : address(0);

/// @audit 3
252:          uint256 mul = mUserOp.paymaster != address(0) ? 3 : 1;

/// @audit 20
269:              address factory = address(bytes20(initCode[0 : 20]));

/// @audit 0
512:          assembly {mstore(0, number())}

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

File: scw-contracts/contracts/smart-contract-wallet/aa-4337/core/SenderCreator.sol

/// @audit 20
16:           address initAddress = address(bytes20(initCode[0 : 20]));

/// @audit 20
17:           bytes memory initCallData = initCode[20 :];

/// @audit 0
/// @audit 0x20
/// @audit 0
/// @audit 32
21:               success := call(gas(), initAddress, 0, add(initCallData, 0x20), mload(initCallData), 0, 32)

/// @audit 0
22:               sender := mload(0)

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/aa-4337/core/SenderCreator.sol#L16

File: scw-contracts/contracts/smart-contract-wallet/aa-4337/interfaces/UserOperation.sol

/// @audit 32
65:               let len := sub(sub(sig.offset, ofs), 32)

/// @audit 0x40
66:               ret := mload(0x40)

/// @audit 0x40
/// @audit 32
67:               mstore(0x40, add(ret, add(len, 32)))

/// @audit 32
69:               calldatacopy(add(ret, 32), ofs, len)

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/aa-4337/interfaces/UserOperation.sol#L65

File: scw-contracts/contracts/smart-contract-wallet/aa-4337/utils/Exec.sol

/// @audit 0x20
/// @audit 0
/// @audit 0
15:               success := call(txGas, to, value, add(data, 0x20), mload(data), 0, 0)

/// @audit 0x20
/// @audit 0
/// @audit 0
25:               success := staticcall(txGas, to, add(data, 0x20), mload(data), 0, 0)

/// @audit 0x20
/// @audit 0
/// @audit 0
35:               success := delegatecall(txGas, to, add(data, 0x20), mload(data), 0, 0)

/// @audit 0x40
42:               let ptr := mload(0x40)

/// @audit 0x40
/// @audit 0x20
43:               mstore(0x40, add(ptr, add(returndatasize(), 0x20)))

/// @audit 0x20
/// @audit 0
45:               returndatacopy(add(ptr, 0x20), 0, returndatasize())

/// @audit 32
53:               revert(add(returnData, 32), mload(returnData))

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

File: scw-contracts/contracts/smart-contract-wallet/base/Executor.sol

/// @audit 0x20
/// @audit 0
/// @audit 0
23:                   success := delegatecall(txGas, to, add(data, 0x20), mload(data), 0, 0)

/// @audit 0x20
/// @audit 0
/// @audit 0
28:                   success := call(txGas, to, value, add(data, 0x20), mload(data), 0, 0)

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/base/Executor.sol#L23

File: scw-contracts/contracts/smart-contract-wallet/base/FallbackManager.sol

/// @audit 0
/// @audit 0
38:                   return(0, 0)

/// @audit 0
/// @audit 0
40:               calldatacopy(0, 0, calldatasize())

/// @audit 0
/// @audit 0
/// @audit 20
/// @audit 0
/// @audit 0
45:               let success := call(gas(), handler, 0, 0, add(calldatasize(), 20), 0, 0)

/// @audit 0
/// @audit 0
46:               returndatacopy(0, 0, returndatasize())

/// @audit 0
48:                   revert(0, returndatasize())

/// @audit 0
50:               return(0, returndatasize())

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/base/FallbackManager.sol#L38

File: scw-contracts/contracts/smart-contract-wallet/base/ModuleManager.sol

/// @audit 0x40
90:               let ptr := mload(0x40)

/// @audit 0x40
/// @audit 0x20
93:               mstore(0x40, add(ptr, add(returndatasize(), 0x20)))

/// @audit 0x20
/// @audit 0
97:               returndatacopy(add(ptr, 0x20), 0, returndatasize())

/// @audit 0x0
121:          while (currentModule != address(0x0) && currentModule != SENTINEL_MODULES && moduleCount < pageSize) {

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/base/ModuleManager.sol#L90

File: scw-contracts/contracts/smart-contract-wallet/common/SecuredTokenTransfer.sol

/// @audit 0xa9059cbb
17:           bytes memory data = abi.encodeWithSelector(0xa9059cbb, receiver, amount);

/// @audit 10000
/// @audit 0
/// @audit 0x20
/// @audit 0
/// @audit 0x20
22:               let success := call(sub(gas(), 10000), token, 0, add(data, 0x20), mload(data), 0, 0x20)

/// @audit 0
31:                       transferred := 0

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/common/SecuredTokenTransfer.sol#L17

File: scw-contracts/contracts/smart-contract-wallet/common/SignatureDecoder.sol

/// @audit 0x41
24:               let signaturePos := mul(0x41, pos)

/// @audit 0x20
25:               r := mload(add(signatures, add(signaturePos, 0x20)))

/// @audit 0x40
26:               s := mload(add(signatures, add(signaturePos, 0x40)))

/// @audit 0x41
/// @audit 0xff
32:               v := and(mload(add(signatures, add(signaturePos, 0x41))), 0xff)

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/common/SignatureDecoder.sol#L24

File: scw-contracts/contracts/smart-contract-wallet/handler/DefaultCallbackHandler.sol

/// @audit 0xf23a6e61
22:           return 0xf23a6e61;

/// @audit 0xbc197c81
32:           return 0xbc197c81;

/// @audit 0x150b7a02
41:           return 0x150b7a02;

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/handler/DefaultCallbackHandler.sol#L22

File: scw-contracts/contracts/smart-contract-wallet/libs/Math.sol

/// @audit 3
117:              uint256 inverse = (3 * denominator) ^ 2;

/// @audit 128
208:              if (value >> 128 > 0) {

/// @audit 128
209:                  value >>= 128;

/// @audit 128
210:                  result += 128;

/// @audit 64
212:              if (value >> 64 > 0) {

/// @audit 64
213:                  value >>= 64;

/// @audit 64
214:                  result += 64;

/// @audit 32
216:              if (value >> 32 > 0) {

/// @audit 32
217:                  value >>= 32;

/// @audit 32
218:                  result += 32;

/// @audit 16
220:              if (value >> 16 > 0) {

/// @audit 16
221:                  value >>= 16;

/// @audit 16
222:                  result += 16;

/// @audit 8
224:              if (value >> 8 > 0) {

/// @audit 8
225:                  value >>= 8;

/// @audit 8
226:                  result += 8;

/// @audit 4
228:              if (value >> 4 > 0) {

/// @audit 4
229:                  value >>= 4;

/// @audit 4
230:                  result += 4;

/// @audit 64
261:              if (value >= 10**64) {

/// @audit 64
262:                  value /= 10**64;

/// @audit 64
263:                  result += 64;

/// @audit 32
265:              if (value >= 10**32) {

/// @audit 32
266:                  value /= 10**32;

/// @audit 32
267:                  result += 32;

/// @audit 16
269:              if (value >= 10**16) {

/// @audit 16
270:                  value /= 10**16;

/// @audit 16
271:                  result += 16;

/// @audit 8
273:              if (value >= 10**8) {

/// @audit 8
274:                  value /= 10**8;

/// @audit 8
275:                  result += 8;

/// @audit 4
277:              if (value >= 10**4) {

/// @audit 4
278:                  value /= 10**4;

/// @audit 4
279:                  result += 4;

/// @audit 128
312:              if (value >> 128 > 0) {

/// @audit 128
313:                  value >>= 128;

/// @audit 16
314:                  result += 16;

/// @audit 64
316:              if (value >> 64 > 0) {

/// @audit 64
317:                  value >>= 64;

/// @audit 8
318:                  result += 8;

/// @audit 32
320:              if (value >> 32 > 0) {

/// @audit 32
321:                  value >>= 32;

/// @audit 4
322:                  result += 4;

/// @audit 16
324:              if (value >> 16 > 0) {

/// @audit 16
325:                  value >>= 16;

/// @audit 8
328:              if (value >> 8 > 0) {

/// @audit 3
342:              return result + (rounding == Rounding.Up && 1 << (result << 3) < value ? 1 : 0);

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/libs/Math.sol#L117

File: scw-contracts/contracts/smart-contract-wallet/libs/MultiSendCallOnly.sol

/// @audit 0x20
25:               let i := 0x20

/// @audit 0xf8
34:                   let operation := shr(0xf8, mload(add(transactions, i)))

/// @audit 0x60
/// @audit 0x01
37:                   let to := shr(0x60, mload(add(transactions, add(i, 0x01))))

/// @audit 0x15
39:                   let value := mload(add(transactions, add(i, 0x15)))

/// @audit 0x35
41:                   let dataLength := mload(add(transactions, add(i, 0x35)))

/// @audit 0x55
43:                   let data := add(transactions, add(i, 0x55))

/// @audit 0
44:                   let success := 0

/// @audit 0
/// @audit 0
47:                           success := call(gas(), to, value, data, dataLength, 0, 0)

/// @audit 0
46:                       case 0 {

/// @audit 0
/// @audit 0
51:                           revert(0, 0)

/// @audit 1
50:                       case 1 {

/// @audit 0
53:                   if eq(success, 0) {

/// @audit 0
/// @audit 0
54:                       revert(0, 0)

/// @audit 0x55
57:                   i := add(i, add(0x55, dataLength))

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/libs/MultiSendCallOnly.sol#L25

File: scw-contracts/contracts/smart-contract-wallet/libs/MultiSend.sol

/// @audit 0x20
31:               let i := 0x20

/// @audit 0xf8
40:                   let operation := shr(0xf8, mload(add(transactions, i)))

/// @audit 0x60
/// @audit 0x01
43:                   let to := shr(0x60, mload(add(transactions, add(i, 0x01))))

/// @audit 0x15
45:                   let value := mload(add(transactions, add(i, 0x15)))

/// @audit 0x35
47:                   let dataLength := mload(add(transactions, add(i, 0x35)))

/// @audit 0x55
49:                   let data := add(transactions, add(i, 0x55))

/// @audit 0
50:                   let success := 0

/// @audit 0
/// @audit 0
53:                           success := call(gas(), to, value, data, dataLength, 0, 0)

/// @audit 0
52:                       case 0 {

/// @audit 0
/// @audit 0
56:                           success := delegatecall(gas(), to, data, dataLength, 0, 0)

/// @audit 1
55:                       case 1 {

/// @audit 0
58:                   if eq(success, 0) {

/// @audit 0
/// @audit 0
59:                       revert(0, 0)

/// @audit 0x55
62:                   i := add(i, add(0x55, dataLength))

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/libs/MultiSend.sol#L31

File: scw-contracts/contracts/smart-contract-wallet/paymasters/PaymasterHelpers.sol

/// @audit 20
36:           (address paymasterId, bytes memory signature) = abi.decode(paymasterAndData[20:], (address, bytes));

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/paymasters/PaymasterHelpers.sol#L36

File: scw-contracts/contracts/smart-contract-wallet/paymasters/verifying/singleton/VerifyingSingletonPaymaster.sol

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

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

File: scw-contracts/contracts/smart-contract-wallet/Proxy.sol

/// @audit 0
/// @audit 0
27:               calldatacopy(0, 0, calldatasize())

/// @audit 0
/// @audit 0
/// @audit 0
28:               let result := delegatecall(gas(), target, 0, calldatasize(), 0, 0)

/// @audit 0
/// @audit 0
29:               returndatacopy(0, 0, returndatasize())

/// @audit 0
/// @audit 0
31:               case 0 {revert(0, returndatasize())}

/// @audit 0
32:               default {return (0, returndatasize())}

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/Proxy.sol#L27

File: scw-contracts/contracts/smart-contract-wallet/SmartAccountFactory.sol

/// @audit 0x0
/// @audit 0x20
38:               proxy := create2(0x0, add(0x20, deploymentData), mload(deploymentData), salt)

/// @audit 0x0
/// @audit 0x20
57:               proxy := create(0x0, add(0x20, deploymentData), mload(deploymentData))

/// @audit 0xff
71:          bytes32 hash = keccak256(abi.encodePacked(bytes1(0xff), address(this), salt, keccak256(code)));

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/SmartAccountFactory.sol#L38

File: scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol

/// @audit 21000
/// @audit 8
200:          uint256 startGas = gasleft() + 21000 + msg.data.length * 8;

/// @audit 64
/// @audit 63
/// @audit 2500
/// @audit 500
224:          require(gasleft() >= max((_tx.targetTxGas * 64) / 63,_tx.targetTxGas + 2500) + 500, "BSA010");

/// @audit 2500
229:              success = execute(_tx.to, _tx.value, _tx.data, _tx.operation, refundInfo.gasPrice == 0 ? (gasleft() - 2500) : _tx.targetTxGas);

/// @audit 65
322:                  require(uint256(s) >= uint256(1) * 65, "BSA021");

/// @audit 32
325:                  require(uint256(s) + 32 <= signatures.length, "BSA022");

/// @audit 0x20
331:                      contractSignatureLen := mload(add(add(signatures, s), 0x20))

/// @audit 32
333:                  require(uint256(s) + 32 + contractSignatureLen <= signatures.length, "BSA023");

/// @audit 0x20
340:                      contractSignature := add(add(signatures, s), 0x20)

/// @audit 30
344:          else if(v > 30) {

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

/// @audit 0x19
/// @audit 0x01
445:          return abi.encodePacked(bytes1(0x19), bytes1(0x01), domainSeparator(), safeTxHash);

/// @audit 32
481:                  revert(add(result, 32), mload(result))

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol#L200

[N‑09] Missing event and or timelock for critical parameter change

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

There are 2 instances of this issue:

File: scw-contracts/contracts/smart-contract-wallet/paymasters/BasePaymaster.sol

24        function setEntryPoint(IEntryPoint _entryPoint) public onlyOwner {
25            entryPoint = _entryPoint;
26:       }

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/paymasters/BasePaymaster.sol#L24-L26

File: scw-contracts/contracts/smart-contract-wallet/paymasters/verifying/singleton/VerifyingSingletonPaymaster.sol

65        function setSigner( address _newVerifyingSigner) external onlyOwner{
66            require(_newVerifyingSigner != address(0), "VerifyingPaymaster: new signer can not be zero address");
67            verifyingSigner = _newVerifyingSigner;
68:       }

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

[N‑10] Events that mark critical parameter changes should contain both the old and the new value

This should especially be done if the new value is not required to be different from the old value

There are 3 instances of this issue:

File: scw-contracts/contracts/smart-contract-wallet/base/FallbackManager.sol

/// @audit setFallbackHandler()
28:           emit ChangedFallbackHandler(handler);

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/base/FallbackManager.sol#L28

File: scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol

/// @audit updateImplementation()
124:          emit ImplementationUpdated(address(this), VERSION, _implementation);

/// @audit updateEntryPoint()
129:          emit EntryPointChanged(address(_entryPoint), _newEntryPoint);

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol#L124

[N‑11] Use a more recent version of solidity

Use a solidity version of at least 0.8.13 to get the ability to use using for with a list of free functions

There are 5 instances of this issue:

File: scw-contracts/contracts/smart-contract-wallet/aa-4337/core/EntryPoint.sol

6:    pragma solidity ^0.8.12;

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/aa-4337/core/EntryPoint.sol#L6

File: scw-contracts/contracts/smart-contract-wallet/BaseSmartAccount.sol

2:    pragma solidity 0.8.12;

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/BaseSmartAccount.sol#L2

File: scw-contracts/contracts/smart-contract-wallet/paymasters/PaymasterHelpers.sol

2:    pragma solidity 0.8.12;

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/paymasters/PaymasterHelpers.sol#L2

File: scw-contracts/contracts/smart-contract-wallet/paymasters/verifying/singleton/VerifyingSingletonPaymaster.sol

2:    pragma solidity 0.8.12;

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/paymasters/verifying/singleton/VerifyingSingletonPaymaster.sol#L2

File: scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol

2:    pragma solidity 0.8.12;

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol#L2

[N‑12] Use scientific notation (e.g. 1e18) rather than exponentiation (e.g. 10**18)

While the compiler knows to optimize away the exponentiation, it's still better coding practice to use idioms that do not require compiler optimization, if they exist

There are 13 instances of this issue:

File: scw-contracts/contracts/smart-contract-wallet/libs/Math.sol

261:              if (value >= 10**64) {

262:                  value /= 10**64;

265:              if (value >= 10**32) {

266:                  value /= 10**32;

269:              if (value >= 10**16) {

270:                  value /= 10**16;

273:              if (value >= 10**8) {

274:                  value /= 10**8;

277:              if (value >= 10**4) {

278:                  value /= 10**4;

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

282:                  value /= 10**2;

285:              if (value >= 10**1) {

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/libs/Math.sol#L261

[N‑13] Constant redefined elsewhere

Consider defining in only one contract so that values cannot become out of sync when only one location is updated. A cheap way to store constants in a single location is to create an internal constant in a library. If the variable is a local cache of another contract's value, consider making the cache variable internal or private, which will require external users to query the contract with the source of truth, so that callers don't get out of sync.

There are 2 instances of this issue:

File: scw-contracts/contracts/smart-contract-wallet/SmartAccountFactory.sol

/// @audit seen in scw-contracts/contracts/smart-contract-wallet/handler/DefaultCallbackHandler.sol 
11:       string public constant VERSION = "1.0.2";

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/SmartAccountFactory.sol#L11

File: scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol

/// @audit seen in scw-contracts/contracts/smart-contract-wallet/SmartAccountFactory.sol 
36:       string public constant VERSION = "1.0.2"; // using AA 0.3.0

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol#L36

[N‑14] Inconsistent spacing in comments

Some lines use // x and some use //x. The instances below point out the usages that don't follow the majority, within each file

There are 30 instances of this issue:

File: scw-contracts/contracts/smart-contract-wallet/aa-4337/core/EntryPoint.sol

85:       } //unchecked

144:      //a memory copy of UserOp fields (except that dynamic byte arrays: callData, initCode and signature

187:          //note: opIndex is ignored (relevant only if mode==postOpReverted, which is only possible outside of innerHandleOp)

250:          //when using a Paymaster, the verificationGasLimit is used also to as a limit for the postOp call.

402:          //a "marker" where account opcode validation is done and paymaster opcode validation is about to start

489:              //legacy mode (for networks that don't support basefee opcode)

508:      //place the NUMBER opcode in the code.

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/aa-4337/core/EntryPoint.sol#L85

File: scw-contracts/contracts/smart-contract-wallet/aa-4337/interfaces/IEntryPoint.sol

70:       //UserOps handled, per aggregator

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/aa-4337/interfaces/IEntryPoint.sol#L70

File: scw-contracts/contracts/smart-contract-wallet/aa-4337/interfaces/IPaymaster.sol

45:           postOpReverted //user op succeeded, but caused postOp to revert. Now its a 2nd call, after user's op was deliberately reverted.

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/aa-4337/interfaces/IPaymaster.sol#L45

File: scw-contracts/contracts/smart-contract-wallet/aa-4337/interfaces/IStakeManager.sol

61:       //API struct used by getStakeInfo and simulateValidation

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/aa-4337/interfaces/IStakeManager.sol#L61

File: scw-contracts/contracts/smart-contract-wallet/aa-4337/interfaces/UserOperation.sol

38:           //read sender from userOp, which is first userOp member (saves 800 gas...)

43:       //relayer/block builder might submit the TX with higher priorityFee, but the user should not

50:               //legacy mode (for networks that don't support basefee opcode)

58:           //lighter signature scheme. must match UserOp.ts#packUserOp

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/aa-4337/interfaces/UserOperation.sol#L38

File: scw-contracts/contracts/smart-contract-wallet/BaseSmartAccount.sol

22:           uint256 gasPrice; //gasPrice or tokenGasPrice

110:              //ignore failure (its EntryPoint's job to verify, not account.)

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/BaseSmartAccount.sol#L22

File: scw-contracts/contracts/smart-contract-wallet/paymasters/verifying/singleton/VerifyingSingletonPaymaster.sol

79:           //can't use userOp.hash(), since it contains also the paymasterAndData itself.

105:          //ECDSA library supports both 64 and 65-byte long signatures.

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/paymasters/verifying/singleton/VerifyingSingletonPaymaster.sol#L79

File: scw-contracts/contracts/smart-contract-wallet/SmartAccountFactory.sol

13:       //states : registry

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/SmartAccountFactory.sol#L13

File: scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol

149:      //@review getNonce specific to EntryPoint requirements

201:          //console.log("init %s", 21000 + msg.data.length * 8);

237:                  //console.log("sent %s", startGas - gasleft());

243:              //console.log("extra gas %s ", extraGas);

268:          //console.log("hp %s", requiredGas);

292:          //console.log("hpr %s", requiredGas);

313:          //review

486:      //called by entryPoint, only after validateUserOp succeeded.

487:      //@review

488:      //Method is updated to instruct delegate call and emit regular events

509:          //ignore signature mismatch of from==ZERO_ADDRESS (for eth_callUserOp validation purposes)

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol#L149

[N‑15] 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

There are 12 instances of this issue:

File: scw-contracts/contracts/smart-contract-wallet/aa-4337/core/EntryPoint.sol

319:          try IAccount(sender).validateUserOp{gas : mUserOp.verificationGasLimit}(op, opInfo.userOpHash, aggregator, missingAccountFunds) returns (uint256 _deadline) {

349:      function _validatePaymasterPrepayment(uint256 opIndex, UserOperation calldata op, UserOpInfo memory opInfo, uint256 requiredPreFund, uint256 gasUsedByValidateAccountPrepayment) internal returns (bytes memory context, uint256 deadline) {

440:      function _handlePostOp(uint256 opIndex, IPaymaster.PostOpMode mode, UserOpInfo memory opInfo, bytes memory context, uint256 actualGas) private returns (uint256 actualGasCost) {

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/aa-4337/core/EntryPoint.sol#L319

File: scw-contracts/contracts/smart-contract-wallet/aa-4337/interfaces/IEntryPoint.sol

28:       event UserOperationEvent(bytes32 indexed userOpHash, address indexed sender, address indexed paymaster, uint256 nonce, bool success, uint256 actualGasCost, uint256 actualGasUsed);

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/aa-4337/interfaces/IEntryPoint.sol#L28

File: scw-contracts/contracts/smart-contract-wallet/aa-4337/interfaces/UserOperation.sol

16:        * @param paymasterAndData if set, this field hold the paymaster address and "paymaster-specific-data". the paymaster will pay for the transaction instead of the sender

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/aa-4337/interfaces/UserOperation.sol#L16

File: scw-contracts/contracts/smart-contract-wallet/interfaces/ERC1155TokenReceiver.sol

10:           @dev An ERC1155-compliant smart contract MUST call this function on the token recipient contract, at the end of a `safeTransferFrom` after the balance has been updated.        

31:           @dev An ERC1155-compliant smart contract MUST call this function on the token recipient contract, at the end of a `safeBatchTransferFrom` after the balances have been updated.        

32:           This function MUST return `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))` (i.e. 0xbc197c81) if it accepts the transfer(s).

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/interfaces/ERC1155TokenReceiver.sol#L10

File: scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol

46:       //     "AccountTx(address to,uint256 value,bytes data,uint8 operation,uint256 targetTxGas,uint256 baseGas,uint256 gasPrice,address gasToken,address refundReceiver,uint256 nonce)"

239:                  payment = handlePayment(startGas - gasleft(), refundInfo.baseGas, refundInfo.gasPrice, refundInfo.tokenGasPriceFactor, refundInfo.gasToken, refundInfo.refundReceiver);

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`

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/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol#L46

[N‑16] Non-library/interface files should use fixed compiler versions, not floating ones

There are 2 instances of this issue:

File: scw-contracts/contracts/smart-contract-wallet/aa-4337/core/EntryPoint.sol

6:    pragma solidity ^0.8.12;

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/aa-4337/core/EntryPoint.sol#L6

File: scw-contracts/contracts/smart-contract-wallet/aa-4337/core/SenderCreator.sol

2:    pragma solidity ^0.8.12;

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/aa-4337/core/SenderCreator.sol#L2

[N‑17] Typos

There are 3 instances of this issue:

File: scw-contracts/contracts/smart-contract-wallet/aa-4337/interfaces/IAggregatedAccount.sol

/// @audit valiate
10:    * - the validateUserOp MUST valiate the aggregator parameter, and MAY ignore the userOp.signature field.

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/aa-4337/interfaces/IAggregatedAccount.sol#L10

File: scw-contracts/contracts/smart-contract-wallet/common/SecuredTokenTransfer.sol

/// @audit keccack
15:           // 0xa9059cbb - keccack("transfer(address,uint256)")

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/common/SecuredTokenTransfer.sol#L15

File: scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol

/// @audit Seperators
38:       // Domain Seperators

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol#L38

[N‑18] Misplaced SPDX identifier

The SPDX identifier should be on the very first line of the file

There are 2 instances of this issue:

File: scw-contracts/contracts/smart-contract-wallet/aa-4337/core/EntryPoint.sol

1:    /**

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/aa-4337/core/EntryPoint.sol#L1

File: scw-contracts/contracts/smart-contract-wallet/aa-4337/interfaces/IEntryPoint.sol

1:    /**

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/aa-4337/interfaces/IEntryPoint.sol#L1

[N‑19] File is missing NatSpec

There are 7 instances of this issue:

File: scw-contracts/contracts/smart-contract-wallet/aa-4337/interfaces/IAggregatedAccount.sol

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/aa-4337/interfaces/IAggregatedAccount.sol

File: scw-contracts/contracts/smart-contract-wallet/aa-4337/utils/Exec.sol

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/aa-4337/utils/Exec.sol

File: scw-contracts/contracts/smart-contract-wallet/base/Executor.sol

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/base/Executor.sol

File: scw-contracts/contracts/smart-contract-wallet/common/Enum.sol

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/common/Enum.sol

File: scw-contracts/contracts/smart-contract-wallet/common/Singleton.sol

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/common/Singleton.sol

File: scw-contracts/contracts/smart-contract-wallet/handler/DefaultCallbackHandler.sol

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/handler/DefaultCallbackHandler.sol

File: scw-contracts/contracts/smart-contract-wallet/interfaces/ERC777TokensRecipient.sol

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/interfaces/ERC777TokensRecipient.sol

[N‑20] NatSpec is incomplete

There are 17 instances of this issue:

File: scw-contracts/contracts/smart-contract-wallet/aa-4337/core/EntryPoint.sol

/// @audit Missing: '@param outOpInfo'
/// @audit Missing: '@param aggregator'
/// @audit Missing: '@return'
378       /**
379        * validate account and paymaster (if defined).
380        * also make sure total validation doesn't exceed verificationGasLimit
381        * this method is called off-chain (simulateValidation()) and on-chain (from handleOps)
382        * @param opIndex the index of this userOp into the "opInfos" array
383        * @param userOp the userOp to validate
384        */
385       function _validatePrepayment(uint256 opIndex, UserOperation calldata userOp, UserOpInfo memory outOpInfo, address aggregator)
386:      private returns (address actualAggregator, uint256 deadline) {

/// @audit Missing: '@return'
438        * @param actualGas the gas used so far by this user operation
439        */
440:      function _handlePostOp(uint256 opIndex, IPaymaster.PostOpMode mode, UserOpInfo memory opInfo, bytes memory context, uint256 actualGas) private returns (uint256 actualGasCost) {

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/aa-4337/core/EntryPoint.sol#L378-L386

File: scw-contracts/contracts/smart-contract-wallet/base/ModuleManager.sol

/// @audit Missing: '@return'
59        /// @param data Data payload of module transaction.
60        /// @param operation Operation type of module transaction.
61        function execTransactionFromModule(
62            address to,
63            uint256 value,
64            bytes memory data,
65            Enum.Operation operation
66:       ) public virtual returns (bool success) {

/// @audit Missing: '@return'
78        /// @param data Data payload of module transaction.
79        /// @param operation Operation type of module transaction.
80        function execTransactionFromModuleReturnData(
81            address to,
82            uint256 value,
83            bytes memory data,
84            Enum.Operation operation
85:       ) public returns (bool success, bytes memory returnData) {

/// @audit Missing: '@param module'
103       /// @dev Returns if an module is enabled
104       /// @return True if the module is enabled
105:      function isModuleEnabled(address module) public view returns (bool) {

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/base/ModuleManager.sol#L59-L66

File: scw-contracts/contracts/smart-contract-wallet/common/SecuredTokenTransfer.sol

/// @audit Missing: '@return'
8         /// @param receiver Receiver to whom the token should be transferred
9         /// @param amount The amount of tokens that should be transferred
10        function transferToken(
11            address token,
12            address receiver,
13            uint256 amount
14:       ) internal returns (bool transferred) {

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/common/SecuredTokenTransfer.sol#L8-L14

File: scw-contracts/contracts/smart-contract-wallet/common/SignatureDecoder.sol

/// @audit Missing: '@return'
8         /// @param pos which signature to read. A prior bounds check of this parameter should be performed, to avoid out of bounds access
9         /// @param signatures concatenated rsv signatures
10        function signatureSplit(bytes memory signatures, uint256 pos)
11            internal
12            pure
13            returns (
14                uint8 v,
15                bytes32 r,
16:               bytes32 s

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/common/SignatureDecoder.sol#L8-L16

File: scw-contracts/contracts/smart-contract-wallet/interfaces/ISignatureValidator.sol

/// @audit Missing: '@return'
17         * MUST allow external calls
18         */
19:       function isValidSignature(bytes memory _data, bytes memory _signature) public view virtual returns (bytes4);

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/interfaces/ISignatureValidator.sol#L17-L19

File: scw-contracts/contracts/smart-contract-wallet/libs/LibAddress.sol

/// @audit Missing: '@return'
9        *      a contract's deployment, as the code is not yet stored on-chain.
10       */
11:     function isContract(address account) internal view returns (bool) {

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/libs/LibAddress.sol#L9-L11

File: scw-contracts/contracts/smart-contract-wallet/SmartAccountFactory.sol

/// @audit Missing: '@return'
31         * @param _index extra salt that allows to deploy more wallets if needed for same EOA (default 0)
32         */
33:       function deployCounterFactualWallet(address _owner, address _entryPoint, address _handler, uint _index) public returns(address proxy){

/// @audit Missing: '@return'
51         * @param _handler fallback handler address
52        */ 
53:       function deployWallet(address _owner, address _entryPoint, address _handler) public returns(address proxy){ 

/// @audit Missing: '@return'
66         * @param _index extra salt that allows to deploy more wallets if needed for same EOA (default 0)
67        */
68:       function getAddressForCounterfactualWallet(address _owner, uint _index) external view returns (address _wallet) {

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/SmartAccountFactory.sol#L31-L33

File: scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol

/// @audit Missing: '@return'
190       /// @param refundInfo Required information for gas refunds
191       /// @param signatures Packed signature data ({bytes32 r}{bytes32 s}{uint8 v})
192       function execTransaction(
193           Transaction memory _tx,
194           uint256 batchId,
195           FeeRefund memory refundInfo,
196           bytes memory signatures
197:      ) public payable virtual override returns (bool success) {

/// @audit Missing: '@param data'
297       /**
298        * @dev Checks whether the signature provided is valid for the provided data, hash. Will revert otherwise.
299        * @param dataHash Hash of the data (could be either a message hash or transaction hash)
300        * @param signatures Signature data that should be verified. Can be ECDSA signature, contract signature (EIP-1271) or approved hash.
301        */
302       function checkSignatures(
303           bytes32 dataHash,
304           bytes memory data,
305:          bytes memory signatures

/// @audit Missing: '@param tokenGasPriceFactor'
377       /// @dev Returns hash to be signed by owner.
378       /// @param to Destination address.
379       /// @param value Ether value.
380       /// @param data Data payload.
381       /// @param operation Operation type.
382       /// @param targetTxGas Fas that should be used for the safe transaction.
383       /// @param baseGas Gas costs for data used to trigger the safe transaction.
384       /// @param gasPrice Maximum gas price that should be used for this transaction.
385       /// @param gasToken Token address (or 0 if ETH) that is used for the payment.
386       /// @param refundReceiver Address of receiver of gas payment (or 0 if tx.origin).
387       /// @param _nonce Transaction nonce.
388       /// @return Transaction hash.
389       function getTransactionHash(
390           address to,
391           uint256 value,
392           bytes calldata data,
393           Enum.Operation operation,
394           uint256 targetTxGas,
395           uint256 baseGas,
396           uint256 gasPrice,
397           uint256 tokenGasPriceFactor,
398           address gasToken,
399           address payable refundReceiver,
400           uint256 _nonce
401:      ) public view returns (bytes32) {

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol#L190-L197

[N‑21] Not using the named return variables anywhere in the function is confusing

Consider changing the variable to be an unnamed one

There are 5 instances of this issue:

File: scw-contracts/contracts/smart-contract-wallet/aa-4337/core/StakeManager.sol

/// @audit info
18:       function getDepositInfo(address account) public view returns (DepositInfo memory info) {

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

File: scw-contracts/contracts/smart-contract-wallet/paymasters/PaymasterHelpers.sol

/// @audit context
24        function paymasterContext(
25            UserOperation calldata op,
26            PaymasterData memory data
27:       ) internal pure returns (bytes memory context) {

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/paymasters/PaymasterHelpers.sol#L24-L27

File: scw-contracts/contracts/smart-contract-wallet/paymasters/verifying/singleton/VerifyingSingletonPaymaster.sol

/// @audit context
/// @audit deadline
97        function validatePaymasterUserOp(UserOperation calldata userOp, bytes32 /*userOpHash*/, uint256 requiredPreFund)
98:       external view override returns (bytes memory context, uint256 deadline) {

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/paymasters/verifying/singleton/VerifyingSingletonPaymaster.sol#L97-L98

File: scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol

/// @audit deadline
506       function _validateSignature(UserOperation calldata userOp, bytes32 userOpHash, address)
507:      internal override virtual returns (uint256 deadline) {

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol#L506-L507

[N‑22] Duplicated require()/revert() checks should be refactored to a modifier or function

The compiler will inline the function, which will avoid JUMP instructions usually associated with functions

There are 5 instances of this issue:

File: scw-contracts/contracts/smart-contract-wallet/base/ModuleManager.sol

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

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/base/ModuleManager.sol#L49

File: scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol

286:              require(success, "BSA011");

289:              require(transferToken(gasToken, receiver, payment), "BSA012");

374:          revert(string(abi.encodePacked(requiredGas)));

351:              require(_signer == owner, "INVALID_SIGNATURE");

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol#L286

[N‑23] Consider using delete rather than assigning zero to clear values

The delete keyword more closely matches the semantics of what is being done, and draws more attention to the changing of state, which may lead to a more thorough audit of its associated logic

There are 3 instances of this issue:

File: scw-contracts/contracts/smart-contract-wallet/aa-4337/core/StakeManager.sol

102:          info.unstakeDelaySec = 0;

103:          info.withdrawTime = 0;

104:          info.stake = 0;

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

[N‑24] Avoid the use of sensitive terms

Use alternative variants, e.g. allowlist/denylist instead of whitelist/blacklist

There are 87 instances of this issue:

File: scw-contracts/contracts/smart-contract-wallet/aa-4337/core/EntryPoint.sol

447:          address paymaster = mUserOp.paymaster;

211:          bytes calldata paymasterAndData = userOp.paymasterAndData;

357:          DepositInfo storage paymasterInfo = deposits[paymaster];

408:              uint paymasterDeadline;

349:      function _validatePaymasterPrepayment(uint256 opIndex, UserOperation calldata op, UserOpInfo memory opInfo, uint256 requiredPreFund, uint256 gasUsedByValidateAccountPrepayment) internal returns (bytes memory context, uint256 deadline) {

221:       * Simulate a call to account.validateUserOp and paymaster.validatePaymasterUserOp.

250:          //when using a Paymaster, the verificationGasLimit is used also to as a limit for the postOp call.

343:       * in case the request has a paymaster:

344:       * validate paymaster is staked and has enough deposit.

345:       * call paymaster.validatePaymasterUserOp.

346:       * revert with proper FailedOp in case paymaster reverts.

347:       * decrement paymaster's deposit

379:       * validate account and paymaster (if defined).

402:          //a "marker" where account opcode validation is done and paymaster opcode validation is about to start

432:       * if a paymaster is defined and its validation returned a non-empty context, its postOp is called.

433:       * the excess amount is refunded to the account (or paymaster - if it is was used in the request)

437:       * @param context the context returned in validatePaymasterUserOp

510:      // account and paymaster.

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/aa-4337/core/EntryPoint.sol#L447

File: scw-contracts/contracts/smart-contract-wallet/aa-4337/core/StakeManager.sol

10:    * deposit is just a balance used to pay for UserOperations (either by a paymaster or an account)

11:    * stake is value locked for at least "unstakeDelay" by a paymaster.

15:       /// maps paymaster to their deposits and stakes

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/aa-4337/core/StakeManager.sol#L10

File: scw-contracts/contracts/smart-contract-wallet/aa-4337/interfaces/IAccount.sol

20:        *      In case there is a paymaster in the request (or the current deposit is high enough), this value will be zero.

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/aa-4337/interfaces/IAccount.sol#L20

File: scw-contracts/contracts/smart-contract-wallet/aa-4337/interfaces/IEntryPoint.sol

63:       error FailedOp(uint256 opIndex, address paymaster, string reason);

139:          StakeInfo senderInfo, StakeInfo factoryInfo, StakeInfo paymasterInfo, AggregatorStakeInfo aggregatorInfo);

22:        * @param paymaster - if non-null, the paymaster that pays for this request.

24:        * @param actualGasCost - actual amount paid (by account or paymaster) for this UserOperation

35:        * @param paymaster the paymaster used by this UserOp

57:        *  @param paymaster - if paymaster.validatePaymasterUserOp fails, this will be the paymaster's address. if validateUserOp failed,

58:        *       this value will be zero (since it failed before accessing the paymaster)

61:        *   Useful for mitigating DoS attempts against batchers or for troubleshooting of account/paymaster reverts.

107:       * Simulate a call to account.validateUserOp and paymaster.validatePaymasterUserOp.

118:       * @param deadline until what time this userOp is valid (the minimum value of account and paymaster's deadline)

121:       * @param paymasterInfo stake information about the paymaster (if any)

131:       * @param deadline until what time this userOp is valid (the minimum value of account and paymaster's deadline)

134:       * @param paymasterInfo stake information about the paymaster (if any)

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/aa-4337/interfaces/IEntryPoint.sol#L63

File: scw-contracts/contracts/smart-contract-wallet/aa-4337/interfaces/IPaymaster.sol

26        function validatePaymasterUserOp(UserOperation calldata userOp, bytes32 userOpHash, uint256 maxCost)
27:       external returns (bytes memory context, uint256 deadline);

7:     * the interface exposed by a paymaster contract, who agrees to pay the gas for user's operations.

13:        * payment validation: check if paymaster agree to pay.

16:        * Note that bundlers will reject this method if it changes the state, unless the paymaster is trusted (whitelisted)

17:        * The paymaster pre-pays using its deposit, and receive back a refund after the postOp method returns.

37:        * @param context - the context value returned by validatePaymasterUserOp

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/aa-4337/interfaces/IPaymaster.sol#L26-L27

File: scw-contracts/contracts/smart-contract-wallet/aa-4337/interfaces/IStakeManager.sol

6:     * deposit is just a balance used to pay for UserOperations (either by a paymaster or an account)

43:        * @param staked true if this account is staked as a paymaster

44:        * @param stake actual amount of ether staked for this paymaster.

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/aa-4337/interfaces/IStakeManager.sol#L6

File: scw-contracts/contracts/smart-contract-wallet/aa-4337/interfaces/UserOperation.sol

30:           bytes paymasterAndData;

12:        * @param verificationGasLimit gas used for validateUserOp and validatePaymasterUserOp

16:        * @param paymasterAndData if set, this field hold the paymaster address and "paymaster-specific-data". the paymaster will pay for the transaction instead of the sender

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/aa-4337/interfaces/UserOperation.sol#L30

File: scw-contracts/contracts/smart-contract-wallet/base/ModuleManager.sol

28:       /// @dev Allows to add a module to the whitelist.

31:       /// @param module Module to be whitelisted.

42:       /// @dev Allows to remove a module from the whitelist.

67:           // Only whitelisted modules are allowed.

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/base/ModuleManager.sol#L28

File: scw-contracts/contracts/smart-contract-wallet/BaseSmartAccount.sol

104:       *  this value MAY be zero, in case there is enough deposit, or the userOp has a paymaster.

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/BaseSmartAccount.sol#L104

File: scw-contracts/contracts/smart-contract-wallet/paymasters/BasePaymaster.sol

28        function validatePaymasterUserOp(UserOperation calldata userOp, bytes32 userOpHash, uint256 maxCost)
29:       external virtual override returns (bytes memory context, uint256 deadline);

12:    * Helper class for creating a paymaster.

39:        * @dev if subclass returns a non-empty context from validatePaymasterUserOp, it must also implement this method.

45:        * @param context - the context value returned by validatePaymasterUserOp

51:           // subclass must override this method if validatePaymasterUserOp returns a context

56:        * add a deposit for this paymaster, used for paying for transaction fees

71:        * add stake for this paymaster.

73:        * @param unstakeDelaySec - the unstake delay for this paymaster. Can only be increased.

80:        * return current paymaster's deposit on the entryPoint.

88:        * The paymaster can't serve requests once unlocked, until it calls addStake again

95:        * withdraw the entire paymaster's stake.

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/paymasters/BasePaymaster.sol#L28-L29

File: scw-contracts/contracts/smart-contract-wallet/paymasters/PaymasterHelpers.sol

44:           (address paymasterId) = abi.decode(context, (address));

35:           bytes calldata paymasterAndData = op.paymasterAndData;

24        function paymasterContext(
25            UserOperation calldata op,
26            PaymasterData memory data
27:       ) internal pure returns (bytes memory context) {

34:       function decodePaymasterData(UserOperation calldata op) internal pure returns (PaymasterData memory) {

43:       function decodePaymasterContext(bytes memory context) internal pure returns (PaymasterContext memory) {

22:        * @dev Encodes the paymaster context: sender, token, rate, and fee

32:        * @dev Decodes paymaster data assuming it follows PaymasterData

41:        * @dev Decodes paymaster context assuming it follows PaymasterContext

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/paymasters/PaymasterHelpers.sol#L44

File: scw-contracts/contracts/smart-contract-wallet/paymasters/verifying/singleton/VerifyingSingletonPaymaster.sol

31:       mapping(address => uint256) public paymasterIdBalances;

48:       function depositFor(address paymasterId) public payable {

102:          PaymasterData memory paymasterData = userOp.decodePaymasterData();

127:      address extractedPaymasterId = data.paymasterId;

97        function validatePaymasterUserOp(UserOperation calldata userOp, bytes32 /*userOpHash*/, uint256 requiredPreFund)
98:       external view override returns (bytes memory context, uint256 deadline) {

14:    * A sample paymaster that uses external service to decide whether to pay for the UserOp.

15:    * The paymaster trusts an external signer to sign the transaction.

19:    * - the paymaster signs to agree to PAY for GAS.

46:        * add a deposit for this paymaster and given paymasterId (Dapp Depositor address), used for paying for transaction fees

73:        * it is called on-chain from the validatePaymasterUserOp, to validate the signature.

74:        * note that this signature covers all fields of the UserOperation, except the "paymasterAndData",

79:           //can't use userOp.hash(), since it contains also the paymasterAndData itself.

95:        * the "paymasterAndData" is expected to be the paymaster and a signature over the entire request params

106:          // we only "require" it here so that the revert reason on invalid signature will be of "VerifyingPaymaster", and not "ECDSA"

114:     * @dev Executes the paymaster's payment conditions

116:     * @param context payment conditions signed by the paymaster in `validatePaymasterUserOp`

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/paymasters/verifying/singleton/VerifyingSingletonPaymaster.sol#L31

[N‑25] Large assembly blocks should have extensive comments

Assembly blocks are take a lot more time to audit than normal Solidity code, and often have gotchas and side-effects that the Solidity versions of the same code do not. Consider adding more comments explaining what is being done in every step of the assembly code

There are 5 instances of this issue:

File: scw-contracts/contracts/smart-contract-wallet/aa-4337/interfaces/UserOperation.sol

63            assembly {
64                let ofs := userOp
65                let len := sub(sub(sig.offset, ofs), 32)
66                ret := mload(0x40)
67                mstore(0x40, add(ret, add(len, 32)))
68                mstore(ret, len)
69                calldatacopy(add(ret, 32), ofs, len)
70:           }

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

File: scw-contracts/contracts/smart-contract-wallet/aa-4337/utils/Exec.sol

41            assembly {
42                let ptr := mload(0x40)
43                mstore(0x40, add(ptr, add(returndatasize(), 0x20)))
44                mstore(ptr, returndatasize())
45                returndatacopy(add(ptr, 0x20), 0, returndatasize())
46                returnData := ptr
47:           }

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

File: scw-contracts/contracts/smart-contract-wallet/base/FallbackManager.sol

35            assembly {
36                let handler := sload(slot)
37                if iszero(handler) {
38                    return(0, 0)
39                }
40                calldatacopy(0, 0, calldatasize())
41                // The msg.sender address is shifted to the left by 12 bytes to remove the padding
42                // Then the address without padding is stored right after the calldata
43                mstore(calldatasize(), shl(96, caller()))
44                // Add 20 bytes for the address appended add the end
45                let success := call(gas(), handler, 0, 0, add(calldatasize(), 20), 0, 0)
46                returndatacopy(0, 0, returndatasize())
47                if iszero(success) {
48                    revert(0, returndatasize())
49                }
50                return(0, returndatasize())
51:           }

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

File: scw-contracts/contracts/smart-contract-wallet/common/SecuredTokenTransfer.sol

19            assembly {
20                // We write the return value to scratch space.
21                // See https://docs.soliditylang.org/en/v0.7.6/internals/layout_in_memory.html#layout-in-memory
22                let success := call(sub(gas(), 10000), token, 0, add(data, 0x20), mload(data), 0, 0x20)
23                switch returndatasize()
24                    case 0 {
25                        transferred := success
26                    }
27                    case 0x20 {
28                        transferred := iszero(or(iszero(success), iszero(mload(0))))
29                    }
30                    default {
31                        transferred := 0
32                    }
33:           }

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/common/SecuredTokenTransfer.sol#L19-L33

File: scw-contracts/contracts/smart-contract-wallet/Proxy.sol

25            assembly {
26                target := sload(_IMPLEMENTATION_SLOT)
27                calldatacopy(0, 0, calldatasize())
28                let result := delegatecall(gas(), target, 0, calldatasize(), 0, 0)
29                returndatacopy(0, 0, returndatasize())
30                switch result
31                case 0 {revert(0, returndatasize())}
32                default {return (0, returndatasize())}
33:           }

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/Proxy.sol#L25-L33

[N‑26] Contracts should have full test coverage

While 100% code coverage does not guarantee that there are no bugs, it often will catch easy-to-find bugs, and will ensure that there are fewer regressions when the code invariably has to be modified. Furthermore, in order to get full coverage, code authors will often have to re-organize their code so that it is more modular, so that each component can be tested separately, which reduces interdependencies between modules and layers, and makes for code that is easier to reason about and audit.

There is 1 instance of this issue:

File: Various Files

[N‑27] Large or complicated code bases should implement fuzzing tests

Large code bases, or code with lots of inline-assembly, complicated math, or complicated interactions between multiple contracts, should implement fuzzing tests. Fuzzers such as Echidna require the test writer to come up with invariants which should not be violated under any circumstances, and the fuzzer tests various inputs and function calls to ensure that the invariants always hold. Even code with 100% code coverage can still have bugs due to the order of the operations a user performs, and fuzzers, with properly and extensively-written invariants, can close this testing gap significantly.

There is 1 instance of this issue:

File: Various Files

[N‑28] Function ordering does not follow the Solidity style guide

According to the Solidity style guide, functions should be laid out in the following order :constructor(), receive(), fallback(), external, public, internal, private, but the cases below do not follow this pattern

There are 31 instances of this issue:

File: scw-contracts/contracts/smart-contract-wallet/aa-4337/core/EntryPoint.sol

/// @audit _executeUserOp() came earlier
68:       function handleOps(UserOperation[] calldata ops, address payable beneficiary) public {

/// @audit handleAggregatedOps() came earlier
168:      function innerHandleOp(bytes calldata callData, UserOpInfo memory opInfo, bytes calldata context) external returns (uint256 actualGasCost) {

/// @audit _copyUserOpToMemory() came earlier
226:      function simulateValidation(UserOperation calldata userOp) external {

/// @audit _createSenderIfNeeded() came earlier
280:      function getSenderAddress(bytes calldata initCode) public {

/// @audit _handlePostOp() came earlier
484:      function getUserOpGasPrice(MemoryUserOp memory mUserOp) internal view returns (uint256) {

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

File: scw-contracts/contracts/smart-contract-wallet/aa-4337/core/StakeManager.sol

/// @audit getStakeInfo() came earlier
30:       function balanceOf(address account) public view returns (uint256) {

/// @audit balanceOf() came earlier
34        receive() external payable {
35:           depositTo(msg.sender);

/// @audit internalIncrementDeposit() came earlier
48:       function depositTo(address account) public payable {

/// @audit addStake() came earlier
80        function unlockStake() external {
81:           DepositInfo storage info = deposits[msg.sender];

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

File: scw-contracts/contracts/smart-contract-wallet/base/FallbackManager.sol

/// @audit internalSetFallbackHandler() came earlier
26:       function setFallbackHandler(address handler) public authorized {

/// @audit setFallbackHandler() came earlier
32        fallback() external {
33:           bytes32 slot = FALLBACK_HANDLER_STORAGE_SLOT;

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

File: scw-contracts/contracts/smart-contract-wallet/base/ModuleManager.sol

/// @audit setupModules() came earlier
32:       function enableModule(address module) public authorized {

/// @audit isModuleEnabled() came earlier
114:      function getModulesPaginated(address start, uint256 pageSize) external view returns (address[] memory array, address next) {

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/base/ModuleManager.sol#L32

File: scw-contracts/contracts/smart-contract-wallet/BaseSmartAccount.sol

/// @audit entryPoint() came earlier
60        function validateUserOp(UserOperation calldata userOp, bytes32 userOpHash, address aggregator, uint256 missingAccountFunds)
61:       external override virtual returns (uint256 deadline) {

/// @audit _payPrefund() came earlier
114:      function init(address _owner, address _entryPointAddress, address _handler) external virtual;

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/BaseSmartAccount.sol#L60-L61

File: scw-contracts/contracts/smart-contract-wallet/paymasters/BasePaymaster.sol

/// @audit setEntryPoint() came earlier
28        function validatePaymasterUserOp(UserOperation calldata userOp, bytes32 userOpHash, uint256 maxCost)
29:       external virtual override returns (bytes memory context, uint256 deadline);

/// @audit _postOp() came earlier
58        function deposit() public virtual payable {
59:           entryPoint.depositTo{value : msg.value}(address(this));

/// @audit withdrawTo() came earlier
75:       function addStake(uint32 unstakeDelaySec) external payable onlyOwner {

/// @audit getDeposit() came earlier
90:       function unlockStake() external onlyOwner {

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/paymasters/BasePaymaster.sol#L28-L29

File: scw-contracts/contracts/smart-contract-wallet/paymasters/verifying/singleton/VerifyingSingletonPaymaster.sol

/// @audit withdrawTo() came earlier
65:       function setSigner( address _newVerifyingSigner) external onlyOwner{

/// @audit getHash() came earlier
97        function validatePaymasterUserOp(UserOperation calldata userOp, bytes32 /*userOpHash*/, uint256 requiredPreFund)
98:       external view override returns (bytes memory context, uint256 deadline) {

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

File: scw-contracts/contracts/smart-contract-wallet/SmartAccountFactory.sol

/// @audit deployWallet() came earlier
68:       function getAddressForCounterfactualWallet(address _owner, uint _index) external view returns (address _wallet) {

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/SmartAccountFactory.sol#L68

File: scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol

/// @audit entryPoint() came earlier
109:      function setOwner(address _newOwner) external mixedAuth {

/// @audit max() came earlier
192       function execTransaction(
193           Transaction memory _tx,
194           uint256 batchId,
195           FeeRefund memory refundInfo,
196           bytes memory signatures
197:      ) public payable virtual override returns (bool success) {

/// @audit handlePayment() came earlier
271       function handlePaymentRevert(
272           uint256 gasUsed,
273           uint256 baseGas,
274           uint256 gasPrice,
275           uint256 tokenGasPriceFactor,
276           address gasToken,
277           address payable refundReceiver
278:      ) external returns (uint256 payment) {

/// @audit checkSignatures() came earlier
363       function requiredTxGas(
364           address to,
365           uint256 value,
366           bytes calldata data,
367           Enum.Operation operation
368:      ) external returns (uint256) {

/// @audit encodeTransactionData() came earlier
449:      function transfer(address payable dest, uint amount) external nonReentrant onlyOwner {

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

/// @audit _validateSignature() came earlier
518:      function getDeposit() public view returns (uint256) {

/// @audit withdrawDepositTo() came earlier
545:      function supportsInterface(bytes4 interfaceId) external view virtual override returns (bool) {

/// @audit supportsInterface() came earlier
550:      receive() external payable {}

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

[N‑29] Contract does not follow the Solidity style guide's suggested layout ordering

The style guide says that, within a contract, the ordering should be 1) Type declarations, 2) State variables, 3) Events, 4) Modifiers, and 5) Functions, but the contract(s) below do not follow this ordering

There are 3 instances of this issue:

File: scw-contracts/contracts/smart-contract-wallet/aa-4337/interfaces/IPaymaster.sol

/// @audit function postOp came earlier
42        enum PostOpMode {
43            opSucceeded, // user op succeeded
44            opReverted, // user op reverted. still has to pay for gas.
45            postOpReverted //user op succeeded, but caused postOp to revert. Now its a 2nd call, after user's op was deliberately reverted.
46:       }

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/aa-4337/interfaces/IPaymaster.sol#L42-L46

File: scw-contracts/contracts/smart-contract-wallet/base/FallbackManager.sol

/// @audit event ChangedFallbackHandler came earlier
12:       bytes32 internal constant FALLBACK_HANDLER_STORAGE_SLOT = 0x6c9a6c4a39284e37ed1cf53d337577d14212a4870fb976a4366c693b939918d5;

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/base/FallbackManager.sol#L12

File: scw-contracts/contracts/smart-contract-wallet/base/ModuleManager.sol

/// @audit event ExecutionFromModuleFailure came earlier
16:       address internal constant SENTINEL_MODULES = address(0x1);

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/base/ModuleManager.sol#L16

[N‑30] Interfaces should be indicated with an I prefix in the contract name

There are 3 instances of this issue:

File: scw-contracts/contracts/smart-contract-wallet/interfaces/ERC1155TokenReceiver.sol

7:    interface ERC1155TokenReceiver {

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/interfaces/ERC1155TokenReceiver.sol#L7

File: scw-contracts/contracts/smart-contract-wallet/interfaces/ERC721TokenReceiver.sol

5:    interface ERC721TokenReceiver {

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/interfaces/ERC721TokenReceiver.sol#L5

File: scw-contracts/contracts/smart-contract-wallet/interfaces/ERC777TokensRecipient.sol

4:    interface ERC777TokensRecipient {

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/interfaces/ERC777TokensRecipient.sol#L4


Excluded findings

These findings are excluded from awards calculations because there are publicly-available automated tools that find them. The valid ones appear here for completeness

Summary

Low Risk Issues

IssueInstances
[L‑01]abi.encodePacked() should not be used with dynamic types when passing the result to a hash function such as keccak256()1

Total: 1 instances over 1 issues

Non-critical Issues

IssueInstances
[N‑01]require()/revert() statements should have descriptive reason strings4
[N‑02]public functions not called by the contract should be declared external instead18
[N‑03]constants should be defined rather than using magic numbers1
[N‑04]Event is missing indexed fields17

Total: 40 instances over 4 issues

Low Risk Issues

[L‑01] abi.encodePacked() should not be used with dynamic types when passing the result to a hash function such as keccak256()

Use abi.encode() instead which will pad items to 32 bytes, which will prevent hash collisions (e.g. abi.encodePacked(0x123,0x456) => 0x123456 => abi.encodePacked(0x1,0x23456), but abi.encode(0x123,0x456) => 0x0...1230...456). "Unless there is a compelling reason, abi.encode should be preferred". If there is only one argument to abi.encodePacked() it can often be cast to bytes() or bytes32() instead.

If all arguments are strings and or bytes, bytes.concat() should be used instead

There is 1 instance of this issue:

File: scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol

/// @audit (valid but excluded finding)
347:              _signer = ecrecover(keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", dataHash)), v - 4, r, s);

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

Non-critical Issues

[N‑01] require()/revert() statements should have descriptive reason strings

There are 4 instances of this issue:

File: scw-contracts/contracts/smart-contract-wallet/libs/Math.sol

/// @audit (valid but excluded finding)
78:               require(denominator > prod1);

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/libs/Math.sol#L78

File: scw-contracts/contracts/smart-contract-wallet/paymasters/BasePaymaster.sol

/// @audit (valid but excluded finding)
105:          require(msg.sender == address(entryPoint));

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/paymasters/BasePaymaster.sol#L105

File: scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol

/// @audit (valid but excluded finding)
371:          require(execute(to, value, data, operation, gasleft()));

/// @audit (valid but excluded finding)
528:          require(req);

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol#L371

[N‑02] public functions not called by the contract should be declared external instead

Contracts are allowed to override their parents' functions and change the visibility from external to public.

There are 18 instances of this issue:

File: scw-contracts/contracts/smart-contract-wallet/aa-4337/core/EntryPoint.sol

/// @audit (valid but excluded finding)
68:       function handleOps(UserOperation[] calldata ops, address payable beneficiary) public {

/// @audit (valid but excluded finding)
93        function handleAggregatedOps(
94            UserOpsPerAggregator[] calldata opsPerAggregator,
95:           address payable beneficiary

/// @audit (valid but excluded finding)
280:      function getSenderAddress(bytes calldata initCode) public {

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

File: scw-contracts/contracts/smart-contract-wallet/base/FallbackManager.sol

/// @audit (valid but excluded finding)
26:       function setFallbackHandler(address handler) public authorized {

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

File: scw-contracts/contracts/smart-contract-wallet/base/ModuleManager.sol

/// @audit (valid but excluded finding)
32:       function enableModule(address module) public authorized {

/// @audit (valid but excluded finding)
47:       function disableModule(address prevModule, address module) public authorized {

/// @audit (valid but excluded finding)
80        function execTransactionFromModuleReturnData(
81            address to,
82            uint256 value,
83            bytes memory data,
84            Enum.Operation operation
85:       ) public returns (bool success, bytes memory returnData) {

/// @audit (valid but excluded finding)
105:      function isModuleEnabled(address module) public view returns (bool) {

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/base/ModuleManager.sol#L32

File: scw-contracts/contracts/smart-contract-wallet/libs/MultiSendCallOnly.sol

/// @audit (valid but excluded finding)
21:       function multiSend(bytes memory transactions) public payable {

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/libs/MultiSendCallOnly.sol#L21

File: scw-contracts/contracts/smart-contract-wallet/libs/MultiSend.sol

/// @audit (valid but excluded finding)
26:       function multiSend(bytes memory transactions) public payable {

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/libs/MultiSend.sol#L26

File: scw-contracts/contracts/smart-contract-wallet/paymasters/verifying/singleton/VerifyingSingletonPaymaster.sol

/// @audit (valid but excluded finding)
48:       function depositFor(address paymasterId) public payable {

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

File: scw-contracts/contracts/smart-contract-wallet/SmartAccountFactory.sol

/// @audit (valid but excluded finding)
33:       function deployCounterFactualWallet(address _owner, address _entryPoint, address _handler, uint _index) public returns(address proxy){

/// @audit (valid but excluded finding)
53:       function deployWallet(address _owner, address _entryPoint, address _handler) public returns(address proxy){ 

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/SmartAccountFactory.sol#L33

File: scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol

/// @audit (valid but excluded finding)
155       function getNonce(uint256 batchId)
156       public view
157:      returns (uint256) {

/// @audit (valid but excluded finding)
389       function getTransactionHash(
390           address to,
391           uint256 value,
392           bytes calldata data,
393           Enum.Operation operation,
394           uint256 targetTxGas,
395           uint256 baseGas,
396           uint256 gasPrice,
397           uint256 tokenGasPriceFactor,
398           address gasToken,
399           address payable refundReceiver,
400           uint256 _nonce
401:      ) public view returns (bytes32) {

/// @audit (valid but excluded finding)
518:      function getDeposit() public view returns (uint256) {

/// @audit (valid but excluded finding)
525       function addDeposit() public payable {
526   
527:          (bool req,) = address(entryPoint()).call{value : msg.value}("");

/// @audit (valid but excluded finding)
536:      function withdrawDepositTo(address payable withdrawAddress, uint256 amount) public onlyOwner {

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol#L155-L157

[N‑03] constants should be defined rather than using magic numbers

Even assembly can benefit from using readable constants instead of hex/numeric literals

There is 1 instance of this issue:

File: scw-contracts/contracts/smart-contract-wallet/base/FallbackManager.sol

/// @audit 96 - (valid but excluded finding)
43:               mstore(calldatasize(), shl(96, caller()))

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/base/FallbackManager.sol#L43

[N‑04] Event is missing indexed fields

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

There are 17 instances of this issue:

File: scw-contracts/contracts/smart-contract-wallet/aa-4337/interfaces/IEntryPoint.sol

/// @audit (valid but excluded finding)
37:       event AccountDeployed(bytes32 indexed userOpHash, address indexed sender, address factory, address paymaster);

/// @audit (valid but excluded finding)
46:       event UserOperationRevertReason(bytes32 indexed userOpHash, address indexed sender, uint256 nonce, bytes revertReason);

/// @audit (valid but excluded finding)
51:       event SignatureAggregatorChanged(address aggregator);

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/aa-4337/interfaces/IEntryPoint.sol#L37

File: scw-contracts/contracts/smart-contract-wallet/aa-4337/interfaces/IStakeManager.sol

/// @audit (valid but excluded finding)
11        event Deposited(
12            address indexed account,
13            uint256 totalDeposit
14:       );

/// @audit (valid but excluded finding)
16        event Withdrawn(
17            address indexed account,
18            address withdrawAddress,
19            uint256 amount
20:       );

/// @audit (valid but excluded finding)
23        event StakeLocked(
24            address indexed account,
25            uint256 totalStaked,
26            uint256 withdrawTime
27:       );

/// @audit (valid but excluded finding)
30        event StakeUnlocked(
31            address indexed account,
32            uint256 withdrawTime
33:       );

/// @audit (valid but excluded finding)
35        event StakeWithdrawn(
36            address indexed account,
37            address withdrawAddress,
38            uint256 amount
39:       );

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/aa-4337/interfaces/IStakeManager.sol#L11-L14

File: scw-contracts/contracts/smart-contract-wallet/base/Executor.sol

/// @audit (valid but excluded finding)
9:        event ExecutionFailure(address to, uint256 value, bytes data, Enum.Operation operation, uint256 txGas);

/// @audit (valid but excluded finding)
10:       event ExecutionSuccess(address to, uint256 value, bytes data, Enum.Operation operation, uint256 txGas);

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/base/Executor.sol#L9

File: scw-contracts/contracts/smart-contract-wallet/base/FallbackManager.sol

/// @audit (valid but excluded finding)
9:        event ChangedFallbackHandler(address handler);

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/base/FallbackManager.sol#L9

File: scw-contracts/contracts/smart-contract-wallet/base/ModuleManager.sol

/// @audit (valid but excluded finding)
11:       event EnabledModule(address module);

/// @audit (valid but excluded finding)
12:       event DisabledModule(address module);

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/base/ModuleManager.sol#L11

File: scw-contracts/contracts/smart-contract-wallet/Proxy.sol

/// @audit (valid but excluded finding)
13:       event Received(uint indexed value, address indexed sender, bytes data);

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/Proxy.sol#L13

File: scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol

/// @audit (valid but excluded finding)
64:       event ImplementationUpdated(address _scw, string version, address newImplementation);

/// @audit (valid but excluded finding)
65:       event EntryPointChanged(address oldEntryPoint, address newEntryPoint);

/// @audit (valid but excluded finding)
67:       event WalletHandlePayment(bytes32 txHash, uint256 payment);

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol#L64

#0 - c4-judge

2023-01-22T15:40:36Z

gzeon-c4 marked the issue as grade-a

#1 - c4-sponsor

2023-02-09T12:30:34Z

livingrockrises marked the issue as sponsor confirmed

Awards

38.7634 USDC - $38.76

Labels

bug
G (Gas Optimization)
grade-b
sponsor confirmed
G-10

External Links

Summary

Gas Optimizations

IssueInstancesTotal Gas Saved
[G‑01]internal functions only called once can be inlined to save gas9180
[G‑02]Add unchecked {} for subtractions where the operands cannot underflow because of a previous require() or if-statement2170
[G‑03]++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-loops5300
[G‑04]require()/revert() strings longer than 32 bytes cost extra gas1-
[G‑05]keccak256() should only need to be called on a specific string literal once142
[G‑06]Optimize names to save gas21462
[G‑07]>= costs less gas than >13
[G‑08]Splitting require() statements that use && saves gas39
[G‑09]require() or revert() statements that check input arguments should be at the top of the function6-
[G‑10]Functions guaranteed to revert when called by normal users can be marked payable484

Total: 53 instances over 10 issues with 1250 gas saved

Gas totals use lower bounds of ranges and count two iterations of each for-loop. All values above are runtime, not deployment, values; deployment values are listed in the individual issue descriptions. The table above as well as its gas numbers do not include any of the excluded findings.

Gas Optimizations

[G‑01] internal functions only called once can be inlined to save gas

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

There are 9 instances of this issue:

File: scw-contracts/contracts/smart-contract-wallet/aa-4337/core/EntryPoint.sol

203:      function _copyUserOpToMemory(UserOperation calldata userOp, MemoryUserOp memory mUserOp) internal pure {

248:      function _getRequiredPrefund(MemoryUserOp memory mUserOp) internal view returns (uint256 requiredPrefund) {

261:      function _createSenderIfNeeded(uint256 opIndex, UserOpInfo memory opInfo, bytes calldata initCode) internal {

289       function _validateAccountPrepayment(uint256 opIndex, UserOperation calldata op, UserOpInfo memory opInfo, address aggregator, uint256 requiredPrefund)
290:      internal returns (uint256 gasUsedByValidateAccountPrepayment, address actualAggregator, uint256 deadline) {

349:      function _validatePaymasterPrepayment(uint256 opIndex, UserOperation calldata op, UserOpInfo memory opInfo, uint256 requiredPreFund, uint256 gasUsedByValidateAccountPrepayment) internal returns (bytes memory context, uint256 deadline) {

496:      function min(uint256 a, uint256 b) internal pure returns (uint256) {

500:      function getOffsetOfMemoryBytes(bytes memory data) internal pure returns (uint256 offset) {

504:      function getMemoryBytesFromOffset(uint256 offset) internal pure returns (bytes memory data) {

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

File: scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol

181:      function max(uint256 a, uint256 b) internal pure returns (uint256) {

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol#L181

[G‑02] Add unchecked {} for subtractions where the operands cannot underflow because of a previous require() or if-statement

require(a <= b); x = b - a => require(a <= b); unchecked { x = b - a }

There are 2 instances of this issue:

File: scw-contracts/contracts/smart-contract-wallet/aa-4337/core/EntryPoint.sol

/// @audit require() on line 353
354:          uint256 gas = verificationGasLimit - gasUsedByValidateAccountPrepayment;

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

File: scw-contracts/contracts/smart-contract-wallet/aa-4337/core/StakeManager.sol

/// @audit require() on line 117
118:          info.deposit = uint112(info.deposit - withdrawAmount);

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

[G‑03] ++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

There are 5 instances of this issue:

File: scw-contracts/contracts/smart-contract-wallet/aa-4337/core/EntryPoint.sol

100:          for (uint256 i = 0; i < opasLen; i++) {

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

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

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

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

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

[G‑04] require()/revert() strings longer than 32 bytes cost extra gas

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

There is 1 instance of this issue:

File: scw-contracts/contracts/smart-contract-wallet/paymasters/verifying/singleton/VerifyingSingletonPaymaster.sol

42:           revert("Deposit must be for a paymasterId. Use depositFor");

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/paymasters/verifying/singleton/VerifyingSingletonPaymaster.sol#L42

[G‑05] keccak256() should only need to be called on a specific string literal once

It should be saved to an immutable variable, and the variable used instead. If the hash is being used as a part of a function selector, the cast to bytes4 should also only be done once

There is 1 instance of this issue:

File: scw-contracts/contracts/smart-contract-wallet/common/Singleton.sol

13:           assert(_IMPLEMENTATION_SLOT == bytes32(uint256(keccak256("biconomy.scw.proxy.implementation")) - 1));

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

[G‑06] Optimize names to save gas

public/external function names and public member variable names can be optimized to save gas. See this link for an example of how it works. Below are the interfaces/abstract contracts that can be optimized so that the most frequently-called functions use the least amount of gas possible during method lookup. Method IDs that have two leading zero bytes can save 128 gas each during deployment, and renaming functions to have lower method IDs will save 22 gas per call, per sorted position shifted

There are 21 instances of this issue:

File: scw-contracts/contracts/smart-contract-wallet/aa-4337/core/EntryPoint.sol

/// @audit handleOps(), handleAggregatedOps(), innerHandleOp(), getUserOpHash(), simulateValidation(), getSenderAddress()
21:   contract EntryPoint is IEntryPoint, StakeManager {

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/aa-4337/core/EntryPoint.sol#L21

File: scw-contracts/contracts/smart-contract-wallet/aa-4337/core/SenderCreator.sol

/// @audit createSender()
8:    contract SenderCreator {

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/aa-4337/core/SenderCreator.sol#L8

File: scw-contracts/contracts/smart-contract-wallet/aa-4337/core/StakeManager.sol

/// @audit getDepositInfo(), depositTo(), addStake(), unlockStake(), withdrawStake(), withdrawTo()
13:   abstract contract StakeManager is IStakeManager {

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/aa-4337/core/StakeManager.sol#L13

File: scw-contracts/contracts/smart-contract-wallet/aa-4337/interfaces/IAccount.sol

/// @audit validateUserOp()
5:    interface IAccount {

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/aa-4337/interfaces/IAccount.sol#L5

File: scw-contracts/contracts/smart-contract-wallet/aa-4337/interfaces/IAggregatedAccount.sol

/// @audit getAggregator()
12:   interface IAggregatedAccount is IAccount {

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/aa-4337/interfaces/IAggregatedAccount.sol#L12

File: scw-contracts/contracts/smart-contract-wallet/aa-4337/interfaces/IAggregator.sol

/// @audit validateSignatures(), validateUserOpSignature(), aggregateSignatures()
9:    interface IAggregator {

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/aa-4337/interfaces/IAggregator.sol#L9

File: scw-contracts/contracts/smart-contract-wallet/aa-4337/interfaces/IEntryPoint.sol

/// @audit handleOps(), handleAggregatedOps(), getUserOpHash(), simulateValidation(), getSenderAddress()
16:   interface IEntryPoint is IStakeManager {

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/aa-4337/interfaces/IEntryPoint.sol#L16

File: scw-contracts/contracts/smart-contract-wallet/aa-4337/interfaces/IPaymaster.sol

/// @audit validatePaymasterUserOp(), postOp()
10:   interface IPaymaster {

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/aa-4337/interfaces/IPaymaster.sol#L10

File: scw-contracts/contracts/smart-contract-wallet/aa-4337/interfaces/IStakeManager.sol

/// @audit getDepositInfo(), depositTo(), addStake(), unlockStake(), withdrawStake(), withdrawTo()
9:    interface IStakeManager {

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/aa-4337/interfaces/IStakeManager.sol#L9

File: scw-contracts/contracts/smart-contract-wallet/base/FallbackManager.sol

/// @audit setFallbackHandler()
8:    contract FallbackManager is SelfAuthorized {

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/base/FallbackManager.sol#L8

File: scw-contracts/contracts/smart-contract-wallet/base/ModuleManager.sol

/// @audit enableModule(), disableModule(), execTransactionFromModule(), execTransactionFromModuleReturnData(), isModuleEnabled(), getModulesPaginated()
9:    contract ModuleManager is SelfAuthorized, Executor {    

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/base/ModuleManager.sol#L9

File: scw-contracts/contracts/smart-contract-wallet/BaseSmartAccount.sol

/// @audit nonce(), nonce(), entryPoint(), init(), execTransaction()
33:   abstract contract BaseSmartAccount is IAccount {

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/BaseSmartAccount.sol#L33

File: scw-contracts/contracts/smart-contract-wallet/interfaces/ERC777TokensRecipient.sol

/// @audit tokensReceived()
4:    interface ERC777TokensRecipient {

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/interfaces/ERC777TokensRecipient.sol#L4

File: scw-contracts/contracts/smart-contract-wallet/interfaces/IERC1271Wallet.sol

/// @audit isValidSignature()
5:    interface IERC1271Wallet {

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/interfaces/IERC1271Wallet.sol#L5

File: scw-contracts/contracts/smart-contract-wallet/interfaces/ISignatureValidator.sol

/// @audit isValidSignature()
9:    abstract contract ISignatureValidator is ISignatureValidatorConstants {

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/interfaces/ISignatureValidator.sol#L9

File: scw-contracts/contracts/smart-contract-wallet/libs/MultiSendCallOnly.sol

/// @audit multiSend()
8:    contract MultiSendCallOnly {

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/libs/MultiSendCallOnly.sol#L8

File: scw-contracts/contracts/smart-contract-wallet/libs/MultiSend.sol

/// @audit multiSend()
9:    contract MultiSend {

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/libs/MultiSend.sol#L9

File: scw-contracts/contracts/smart-contract-wallet/paymasters/BasePaymaster.sol

/// @audit setEntryPoint(), withdrawTo(), addStake(), getDeposit(), unlockStake(), withdrawStake()
16:   abstract contract BasePaymaster is IPaymaster, Ownable {

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/paymasters/BasePaymaster.sol#L16

File: scw-contracts/contracts/smart-contract-wallet/paymasters/verifying/singleton/VerifyingSingletonPaymaster.sol

/// @audit depositFor(), setSigner(), getHash()
22:   contract VerifyingSingletonPaymaster is BasePaymaster {

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/paymasters/verifying/singleton/VerifyingSingletonPaymaster.sol#L22

File: scw-contracts/contracts/smart-contract-wallet/SmartAccountFactory.sol

/// @audit deployCounterFactualWallet(), deployWallet(), getAddressForCounterfactualWallet()
7:    contract SmartAccountFactory {

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/SmartAccountFactory.sol#L7

File: scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol

/// @audit setOwner(), updateImplementation(), updateEntryPoint(), domainSeparator(), getChainId(), getNonce(), handlePaymentRevert(), checkSignatures(), requiredTxGas(), getTransactionHash(), encodeTransactionData(), transfer(), pullTokens(), execute(), executeBatch(), execFromEntryPoint(), getDeposit(), addDeposit(), withdrawDepositTo()
18:   contract SmartAccount is 

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol#L18

[G‑07] >= costs less gas than >

The compiler uses opcodes GT and ISZERO for solidity code that uses >, but only requires LT for >=, which saves 3 gas

There is 1 instance of this issue:

File: scw-contracts/contracts/smart-contract-wallet/libs/Math.sol

20:           return a > b ? a : b;

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/libs/Math.sol#L20

[G‑08] Splitting require() statements that use && saves gas

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

There are 3 instances of this issue:

File: scw-contracts/contracts/smart-contract-wallet/base/ModuleManager.sol

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

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

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

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/base/ModuleManager.sol#L34

[G‑09] require() or revert() statements that check input arguments should be at the top of the function

Checks that involve constants should come before checks that involve state variables, function calls, and calculations. By doing these checks first, the function is able to revert before wasting a Gcoldsload (2100 gas*) in a function that may ultimately revert in the unhappy case.

There are 6 instances of this issue:

File: scw-contracts/contracts/smart-contract-wallet/aa-4337/core/StakeManager.sol

/// @audit expensive op on line 60
61:           require(_unstakeDelaySec > 0, "must specify unstake delay");

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/aa-4337/core/StakeManager.sol#L61

File: scw-contracts/contracts/smart-contract-wallet/paymasters/verifying/singleton/VerifyingSingletonPaymaster.sol

/// @audit expensive op on line 36
37:           require(_verifyingSigner != address(0), "VerifyingPaymaster: signer of paymaster can not be zero address");

/// @audit expensive op on line 49
50:           require(paymasterId != address(0), "Paymaster Id can not be zero address");

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/paymasters/verifying/singleton/VerifyingSingletonPaymaster.sol#L37

File: scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol

/// @audit expensive op on line 168
169:          require(_owner != address(0),"Invalid owner");

/// @audit expensive op on line 168
170:          require(_entryPointAddress != address(0), "Invalid Entrypoint");

/// @audit expensive op on line 168
171:          require(_handler != address(0), "Invalid Entrypoint");

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol#L169

[G‑10] Functions guaranteed to revert when called by normal users can be marked payable

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

There are 4 instances of this issue:

File: scw-contracts/contracts/smart-contract-wallet/paymasters/BasePaymaster.sol

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

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

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/paymasters/BasePaymaster.sol#L67

File: scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol

449:      function transfer(address payable dest, uint amount) external nonReentrant onlyOwner {

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

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol#L449


Excluded findings

These findings are excluded from awards calculations because there are publicly-available automated tools that find them. The valid ones appear here for completeness

Summary

Gas Optimizations

IssueInstancesTotal Gas Saved
[G‑01]Using calldata instead of memory for read-only arguments in external functions saves gas4480
[G‑02]<array>.length should not be looked up in every loop of a for-loop13
[G‑03]require()/revert() strings longer than 32 bytes cost extra gas13-
[G‑04]Using bools for storage incurs overhead117100
[G‑05]Using > 0 costs more gas than != 0 when used on a uint in a require() statement318
[G‑06]internal functions not called by the contract should be removed to save deployment gas1-
[G‑07]++i costs less gas than i++, especially when it's used in for-loops (--i/i-- too)1155
[G‑08]Using private rather than public for constants, saves gas4-
[G‑09]Division by two should use bit shifting120
[G‑10]Use custom errors rather than revert()/require() strings to save gas69-
[G‑11]Functions guaranteed to revert when called by normal users can be marked payable7147

Total: 115 instances over 11 issues with 17823 gas saved

Gas totals use lower bounds of ranges and count two iterations of each for-loop. All values above are runtime, not deployment, values; deployment values are listed in the individual issue descriptions. The table above as well as its gas numbers do not include any of the excluded findings.

Gas Optimizations

[G‑01] Using calldata instead of memory for read-only arguments in external functions saves gas

When a function with a memory array is called externally, the abi.decode() step has to use a for-loop to copy each index of the calldata to the memory index. Each iteration of this for-loop costs at least 60 gas (i.e. 60 * <mem_array>.length). Using calldata directly, obliviates the need for such a loop in the contract code and runtime execution. Note that even if an interface defines a function as having memory arguments, it's still valid for implementation contracs to use calldata arguments instead.

If the array is passed to an internal function which passes the array to another internal function where the array is modified and therefore memory is used in the external call, it's still more gass-efficient to use calldata when the external function uses modifiers, since the modifiers may prevent the internal functions from being called. Structs have the same overhead as an array of length one

Note that I've also flagged instances where the function is public but can be marked as external since it's not called by the contract, and cases where a constructor is involved

There are 4 instances of this issue:

File: scw-contracts/contracts/smart-contract-wallet/aa-4337/core/EntryPoint.sol

/// @audit opInfo - (valid but excluded finding)
168:      function innerHandleOp(bytes calldata callData, UserOpInfo memory opInfo, bytes calldata context) external returns (uint256 actualGasCost) {

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/aa-4337/core/EntryPoint.sol#L168

File: scw-contracts/contracts/smart-contract-wallet/base/ModuleManager.sol

/// @audit data - (valid but excluded finding)
80        function execTransactionFromModuleReturnData(
81            address to,
82            uint256 value,
83            bytes memory data,
84            Enum.Operation operation
85:       ) public returns (bool success, bytes memory returnData) {

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/base/ModuleManager.sol#L80-L85

File: scw-contracts/contracts/smart-contract-wallet/libs/MultiSendCallOnly.sol

/// @audit transactions - (valid but excluded finding)
21:       function multiSend(bytes memory transactions) public payable {

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/libs/MultiSendCallOnly.sol#L21

File: scw-contracts/contracts/smart-contract-wallet/libs/MultiSend.sol

/// @audit transactions - (valid but excluded finding)
26:       function multiSend(bytes memory transactions) public payable {

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/libs/MultiSend.sol#L26

[G‑02] <array>.length should not be looked up in every loop of a for-loop

The overheads outlined below are PER LOOP, excluding the first loop

  • storage arrays incur a Gwarmaccess (100 gas)
  • memory arrays use MLOAD (3 gas)
  • calldata arrays use CALLDATALOAD (3 gas)

Caching the length changes each of these to a DUP<N> (3 gas), and gets rid of the extra DUP<N> needed to store the stack offset

There is 1 instance of this issue:

File: scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol

/// @audit (valid but excluded finding)
468:          for (uint i = 0; i < dest.length;) {

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol#L468

[G‑03] require()/revert() strings longer than 32 bytes cost extra gas

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

There are 13 instances of this issue:

File: scw-contracts/contracts/smart-contract-wallet/libs/MultiSend.sol

/// @audit (valid but excluded finding)
27:           require(address(this) != multisendSingleton, "MultiSend should only be called via delegatecall");

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/libs/MultiSend.sol#L27

File: scw-contracts/contracts/smart-contract-wallet/paymasters/verifying/singleton/VerifyingSingletonPaymaster.sol

/// @audit (valid but excluded finding)
36:           require(address(_entryPoint) != address(0), "VerifyingPaymaster: Entrypoint can not be zero address");

/// @audit (valid but excluded finding)
37:           require(_verifyingSigner != address(0), "VerifyingPaymaster: signer of paymaster can not be zero address");

/// @audit (valid but excluded finding)
49:           require(!Address.isContract(paymasterId), "Paymaster Id can not be smart contract address");

/// @audit (valid but excluded finding)
50:           require(paymasterId != address(0), "Paymaster Id can not be zero address");

/// @audit (valid but excluded finding)
66:           require(_newVerifyingSigner != address(0), "VerifyingPaymaster: new signer can not be zero address");

/// @audit (valid but excluded finding)
107:          require(sigLength == 64 || sigLength == 65, "VerifyingPaymaster: invalid signature length in paymasterAndData");

/// @audit (valid but excluded finding)
108:          require(verifyingSigner == hash.toEthSignedMessageHash().recover(paymasterData.signature), "VerifyingPaymaster: wrong signature");

/// @audit (valid but excluded finding)
109:          require(requiredPreFund <= paymasterIdBalances[paymasterData.paymasterId], "Insufficient balance for paymaster id");

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/paymasters/verifying/singleton/VerifyingSingletonPaymaster.sol#L36

File: scw-contracts/contracts/smart-contract-wallet/SmartAccountFactory.sol

/// @audit (valid but excluded finding)
18:           require(_baseImpl != address(0), "base wallet address can not be zero");

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/SmartAccountFactory.sol#L18

File: scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol

/// @audit (valid but excluded finding)
77:           require(msg.sender == owner, "Smart Account:: Sender is not authorized");

/// @audit (valid but excluded finding)
110:          require(_newOwner != address(0), "Smart Account:: new Signatory address cannot be zero");

/// @audit (valid but excluded finding)
128:          require(_newEntryPoint != address(0), "Smart Account:: new entry point address cannot be zero");

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol#L77

[G‑04] Using bools for storage incurs overhead

    // Booleans are more expensive than uint256 or any type that takes up a full
    // word because each write operation emits an extra SLOAD to first read the
    // slot's contents, replace the bits taken up by the boolean, and then write
    // back. This is the compiler's defense against contract upgrades and
    // pointer aliasing, and it cannot be disabled.

https://github.com/OpenZeppelin/openzeppelin-contracts/blob/58f635312aa21f947cae5f8578638a85aa2519f5/contracts/security/ReentrancyGuard.sol#L23-L27 Use uint256(1) and uint256(2) for true/false to avoid a Gwarmaccess (100 gas) for the extra SLOAD, and to avoid Gsset (20000 gas) when changing from false to true, after having been true in the past

There is 1 instance of this issue:

File: scw-contracts/contracts/smart-contract-wallet/SmartAccountFactory.sol

/// @audit (valid but excluded finding)
15:       mapping (address => bool) public isAccountExist;

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/SmartAccountFactory.sol#L15

[G‑05] Using > 0 costs more gas than != 0 when used on a uint in a require() statement

This change saves 6 gas per instance. The optimization works until solidity version 0.8.13 where there is a regression in gas costs.

There are 3 instances of this issue:

File: scw-contracts/contracts/smart-contract-wallet/aa-4337/core/StakeManager.sol

/// @audit (valid but excluded finding)
61:           require(_unstakeDelaySec > 0, "must specify unstake delay");

/// @audit (valid but excluded finding)
64:           require(stake > 0, "no stake specified");

/// @audit (valid but excluded finding)
99:           require(stake > 0, "No stake to withdraw");

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/aa-4337/core/StakeManager.sol#L61

[G‑06] internal functions not called by the contract should be removed to save deployment gas

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

There is 1 instance of this issue:

File: scw-contracts/contracts/smart-contract-wallet/common/Singleton.sol

/// @audit (valid but excluded finding)
20:       function _getImplementation() internal view returns (address _imp) {

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

[G‑07] ++i costs less gas than i++, especially when it's used in for-loops (--i/i-- too)

Saves 5 gas per loop

There are 11 instances of this issue:

File: scw-contracts/contracts/smart-contract-wallet/aa-4337/core/EntryPoint.sol

/// @audit (valid but excluded finding)
74:           for (uint256 i = 0; i < opslen; i++) {

/// @audit (valid but excluded finding)
80:           for (uint256 i = 0; i < opslen; i++) {

/// @audit (valid but excluded finding)
100:          for (uint256 i = 0; i < opasLen; i++) {

/// @audit (valid but excluded finding)
107:          for (uint256 a = 0; a < opasLen; a++) {

/// @audit (valid but excluded finding)
112:              for (uint256 i = 0; i < opslen; i++) {

/// @audit (valid but excluded finding)
114:                  opIndex++;

/// @audit (valid but excluded finding)
128:          for (uint256 a = 0; a < opasLen; a++) {

/// @audit (valid but excluded finding)
134:              for (uint256 i = 0; i < opslen; i++) {

/// @audit (valid but excluded finding)
136:                  opIndex++;

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/aa-4337/core/EntryPoint.sol#L74

File: scw-contracts/contracts/smart-contract-wallet/base/ModuleManager.sol

/// @audit (valid but excluded finding)
124:              moduleCount++;

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/base/ModuleManager.sol#L124

File: scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol

/// @audit (valid but excluded finding)
216:              nonces[batchId]++;

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol#L216

[G‑08] Using private rather than public for constants, saves gas

If needed, the values can be read from the verified contract source code, or if there are multiple values there can be a single getter function that returns a tuple of the values of all currently-public constants. Saves 3406-3606 gas in deployment gas due to the compiler not having to create non-payable getter functions for deployment calldata, not having to store the bytes of the value outside of where it's used, and not adding another entry to the method ID table

There are 4 instances of this issue:

File: scw-contracts/contracts/smart-contract-wallet/handler/DefaultCallbackHandler.sol

/// @audit (valid but excluded finding)
12:       string public constant NAME = "Default Callback Handler";

/// @audit (valid but excluded finding)
13:       string public constant VERSION = "1.0.0";

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/handler/DefaultCallbackHandler.sol#L12

File: scw-contracts/contracts/smart-contract-wallet/SmartAccountFactory.sol

/// @audit (valid but excluded finding)
11:       string public constant VERSION = "1.0.2";

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/SmartAccountFactory.sol#L11

File: scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol

/// @audit (valid but excluded finding)
36:       string public constant VERSION = "1.0.2"; // using AA 0.3.0

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol#L36

[G‑09] Division by two should use bit shifting

<x> / 2 is the same as <x> >> 1. While the compiler uses the SHR opcode to accomplish both, the version that uses division incurs an overhead of 20 gas due to JUMPs to and from a compiler utility function that introduces checks which can be avoided by using unchecked {} around the division by two

There is 1 instance of this issue:

File: scw-contracts/contracts/smart-contract-wallet/libs/Math.sol

/// @audit (valid but excluded finding)
36:           return (a & b) + (a ^ b) / 2;

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/libs/Math.sol#L36

[G‑10] Use custom errors rather than revert()/require() strings to save gas

Custom errors are available from solidity version 0.8.4. Custom errors save ~50 gas each time they're hit by avoiding having to allocate and store the revert string. Not defining the strings also save deployment gas

There are 69 instances of this issue:

File: scw-contracts/contracts/smart-contract-wallet/aa-4337/core/EntryPoint.sol

/// @audit (valid but excluded finding)
36:           require(beneficiary != address(0), "AA90 invalid beneficiary");

/// @audit (valid but excluded finding)
38:           require(success, "AA91 failed send to beneficiary");

/// @audit (valid but excluded finding)
170:          require(msg.sender == address(this), "AA92 internal call only");

/// @audit (valid but excluded finding)
213:              require(paymasterAndData.length >= 20, "AA93 invalid paymasterAndData");

/// @audit (valid but excluded finding)
353:          require(verificationGasLimit > gasUsedByValidateAccountPrepayment, "AA41 too little verificationGas");

/// @audit (valid but excluded finding)
397:          require(maxGasValues <= type(uint120).max, "AA94 gas values overflow");

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/aa-4337/core/EntryPoint.sol#L36

File: scw-contracts/contracts/smart-contract-wallet/aa-4337/core/StakeManager.sol

/// @audit (valid but excluded finding)
41:           require(newAmount <= type(uint112).max, "deposit overflow");

/// @audit (valid but excluded finding)
61:           require(_unstakeDelaySec > 0, "must specify unstake delay");

/// @audit (valid but excluded finding)
62:           require(_unstakeDelaySec >= info.unstakeDelaySec, "cannot decrease unstake time");

/// @audit (valid but excluded finding)
64:           require(stake > 0, "no stake specified");

/// @audit (valid but excluded finding)
65:           require(stake < type(uint112).max, "stake overflow");

/// @audit (valid but excluded finding)
82:           require(info.unstakeDelaySec != 0, "not staked");

/// @audit (valid but excluded finding)
83:           require(info.staked, "already unstaking");

/// @audit (valid but excluded finding)
99:           require(stake > 0, "No stake to withdraw");

/// @audit (valid but excluded finding)
100:          require(info.withdrawTime > 0, "must call unlockStake() first");

/// @audit (valid but excluded finding)
101:          require(info.withdrawTime <= block.timestamp, "Stake withdrawal is not due");

/// @audit (valid but excluded finding)
107:          require(success, "failed to withdraw stake");

/// @audit (valid but excluded finding)
117:          require(withdrawAmount <= info.deposit, "Withdraw amount too large");

/// @audit (valid but excluded finding)
121:          require(success, "failed to withdraw");

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/aa-4337/core/StakeManager.sol#L41

File: scw-contracts/contracts/smart-contract-wallet/base/ModuleManager.sol

/// @audit (valid but excluded finding)
21:           require(modules[SENTINEL_MODULES] == address(0), "BSA100");

/// @audit (valid but excluded finding)
25:               require(execute(to, 0, data, Enum.Operation.DelegateCall, gasleft()), "BSA000");

/// @audit (valid but excluded finding)
34:           require(module != address(0) && module != SENTINEL_MODULES, "BSA101");

/// @audit (valid but excluded finding)
36:           require(modules[module] == address(0), "BSA102");

/// @audit (valid but excluded finding)
49:           require(module != address(0) && module != SENTINEL_MODULES, "BSA101");

/// @audit (valid but excluded finding)
50:           require(modules[prevModule] == module, "BSA103");

/// @audit (valid but excluded finding)
68:           require(msg.sender != SENTINEL_MODULES && modules[msg.sender] != address(0), "BSA104");

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/base/ModuleManager.sol#L21

File: scw-contracts/contracts/smart-contract-wallet/BaseSmartAccount.sol

/// @audit (valid but excluded finding)
74:           require(msg.sender == address(entryPoint()), "account: not from EntryPoint");

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/BaseSmartAccount.sol#L74

File: scw-contracts/contracts/smart-contract-wallet/libs/MultiSend.sol

/// @audit (valid but excluded finding)
27:           require(address(this) != multisendSingleton, "MultiSend should only be called via delegatecall");

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/libs/MultiSend.sol#L27

File: scw-contracts/contracts/smart-contract-wallet/paymasters/verifying/singleton/VerifyingSingletonPaymaster.sol

/// @audit (valid but excluded finding)
36:           require(address(_entryPoint) != address(0), "VerifyingPaymaster: Entrypoint can not be zero address");

/// @audit (valid but excluded finding)
37:           require(_verifyingSigner != address(0), "VerifyingPaymaster: signer of paymaster can not be zero address");

/// @audit (valid but excluded finding)
49:           require(!Address.isContract(paymasterId), "Paymaster Id can not be smart contract address");

/// @audit (valid but excluded finding)
50:           require(paymasterId != address(0), "Paymaster Id can not be zero address");

/// @audit (valid but excluded finding)
57:           require(amount <= currentBalance, "Insufficient amount to withdraw");

/// @audit (valid but excluded finding)
66:           require(_newVerifyingSigner != address(0), "VerifyingPaymaster: new signer can not be zero address");

/// @audit (valid but excluded finding)
107:          require(sigLength == 64 || sigLength == 65, "VerifyingPaymaster: invalid signature length in paymasterAndData");

/// @audit (valid but excluded finding)
108:          require(verifyingSigner == hash.toEthSignedMessageHash().recover(paymasterData.signature), "VerifyingPaymaster: wrong signature");

/// @audit (valid but excluded finding)
109:          require(requiredPreFund <= paymasterIdBalances[paymasterData.paymasterId], "Insufficient balance for paymaster id");

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/paymasters/verifying/singleton/VerifyingSingletonPaymaster.sol#L36

File: scw-contracts/contracts/smart-contract-wallet/SmartAccountFactory.sol

/// @audit (valid but excluded finding)
18:           require(_baseImpl != address(0), "base wallet address can not be zero");

/// @audit (valid but excluded finding)
40:           require(address(proxy) != address(0), "Create2 call failed");

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/SmartAccountFactory.sol#L18

File: scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol

/// @audit (valid but excluded finding)
77:           require(msg.sender == owner, "Smart Account:: Sender is not authorized");

/// @audit (valid but excluded finding)
83:           require(msg.sender == owner || msg.sender == address(this),"Only owner or self");

/// @audit (valid but excluded finding)
89:           require(msg.sender == address(entryPoint()), "wallet: not from EntryPoint");

/// @audit (valid but excluded finding)
110:          require(_newOwner != address(0), "Smart Account:: new Signatory address cannot be zero");

/// @audit (valid but excluded finding)
121:          require(_implementation.isContract(), "INVALID_IMPLEMENTATION");

/// @audit (valid but excluded finding)
128:          require(_newEntryPoint != address(0), "Smart Account:: new entry point address cannot be zero");

/// @audit (valid but excluded finding)
167:          require(owner == address(0), "Already initialized");

/// @audit (valid but excluded finding)
168:          require(address(_entryPoint) == address(0), "Already initialized");

/// @audit (valid but excluded finding)
169:          require(_owner != address(0),"Invalid owner");

/// @audit (valid but excluded finding)
170:          require(_entryPointAddress != address(0), "Invalid Entrypoint");

/// @audit (valid but excluded finding)
171:          require(_handler != address(0), "Invalid Entrypoint");

/// @audit (valid but excluded finding)
224:          require(gasleft() >= max((_tx.targetTxGas * 64) / 63,_tx.targetTxGas + 2500) + 500, "BSA010");

/// @audit (valid but excluded finding)
232:              require(success || _tx.targetTxGas != 0 || refundInfo.gasPrice != 0, "BSA013");

/// @audit (valid but excluded finding)
262:              require(success, "BSA011");

/// @audit (valid but excluded finding)
265:              require(transferToken(gasToken, receiver, payment), "BSA012");

/// @audit (valid but excluded finding)
286:              require(success, "BSA011");

/// @audit (valid but excluded finding)
289:              require(transferToken(gasToken, receiver, payment), "BSA012");

/// @audit (valid but excluded finding)
322:                  require(uint256(s) >= uint256(1) * 65, "BSA021");

/// @audit (valid but excluded finding)
325:                  require(uint256(s) + 32 <= signatures.length, "BSA022");

/// @audit (valid but excluded finding)
333:                  require(uint256(s) + 32 + contractSignatureLen <= signatures.length, "BSA023");

/// @audit (valid but excluded finding)
342:                  require(ISignatureValidator(_signer).isValidSignature(data, contractSignature) == EIP1271_MAGIC_VALUE, "BSA024");

/// @audit (valid but excluded finding)
348:              require(_signer == owner, "INVALID_SIGNATURE");

/// @audit (valid but excluded finding)
351:              require(_signer == owner, "INVALID_SIGNATURE");

/// @audit (valid but excluded finding)
450:          require(dest != address(0), "this action will burn your funds");

/// @audit (valid but excluded finding)
452:          require(success,"transfer failed");

/// @audit (valid but excluded finding)
467:          require(dest.length == func.length, "wrong array lengths");

/// @audit (valid but excluded finding)
491:          require(success, "Userop Failed");

/// @audit (valid but excluded finding)
495:          require(msg.sender == address(entryPoint()) || msg.sender == owner, "account: not Owner or EntryPoint");

/// @audit (valid but excluded finding)
502:          require(nonces[0]++ == userOp.nonce, "account: invalid nonce");

/// @audit (valid but excluded finding)
511:          require(owner == hash.recover(userOp.signature) || tx.origin == address(0), "account: wrong signature");

https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol#L77

[G‑11] Functions guaranteed to revert when called by normal users can be marked payable

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

There are 7 instances of this issue:

File: scw-contracts/contracts/smart-contract-wallet/paymasters/BasePaymaster.sol

/// @audit (valid but excluded finding)
24:       function setEntryPoint(IEntryPoint _entryPoint) public onlyOwner {

/// @audit (valid but excluded finding)
90:       function unlockStake() external onlyOwner {

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

File: scw-contracts/contracts/smart-contract-wallet/paymasters/verifying/singleton/VerifyingSingletonPaymaster.sol

/// @audit (valid but excluded finding)
65:       function setSigner( address _newVerifyingSigner) external onlyOwner{

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

File: scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol

/// @audit (valid but excluded finding)
455:      function pullTokens(address token, address dest, uint256 amount) external onlyOwner {

/// @audit (valid but excluded finding)
460:      function execute(address dest, uint value, bytes calldata func) external onlyOwner{

/// @audit (valid but excluded finding)
465:      function executeBatch(address[] calldata dest, bytes[] calldata func) external onlyOwner{

/// @audit (valid but excluded finding)
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/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol#L455

#0 - c4-judge

2023-01-22T16:16:55Z

gzeon-c4 marked the issue as grade-b

#1 - c4-sponsor

2023-02-09T12:30:10Z

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