Platform: Code4rena
Start Date: 14/03/2024
Pot Size: $49,000 USDC
Total HM: 3
Participants: 51
Period: 7 days
Judge: 3docSec
Id: 350
League: ETH
Rank: 46/51
Findings: 1
Award: $13.69
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: K42
Also found by: 0x11singh99, 0xAnah, Hajime, SAQ, SM3_SS, albahaca, clara, dharma09, hunter_w3b, naman1778, shamsulhaq123, slvDev
13.6948 USDC - $13.69
Note: The issues addressed here were not reported by the bot, for packing variables, notes explaining the how and why are included
Note: The bot report did not cover these instances.
We can replaced string clientDataJSON with bytes32 clientDataJSONHash. Storing the hash instead of the full string can significantly reduce gas costs if the string is relatively large.
For challengeTndex and typeIndex we can reduce data types size which is easily acoomodate large number and we can also reduce size of r,s to store in slot.By doing this we can save 2 slot.
File: src/WebAuthnSol/WebAuthn.sol 21: struct WebAuthnAuth { /// @dev The WebAuthn authenticator data. /// See https://www.w3.org/TR/webauthn-2/#dom-authenticatorassertionresponse-authenticatordata. bytes authenticatorData; /// @dev The WebAuthn client data JSON. /// See https://www.w3.org/TR/webauthn-2/#dom-authenticatorresponse-clientdatajson. string clientDataJSON; /// @dev The index at which "challenge":"..." occurs in `clientDataJSON`. uint256 challengeIndex; /// @dev The index at which "type":"..." occurs in `clientDataJSON`. uint256 typeIndex; /// @dev The r value of secp256r1 signature uint256 r; /// @dev The s value of secp256r1 signature uint256 s; }
diff --git a/src/WebAuthnSol/WebAuthn.sol b/src/WebAuthnSol/WebAuthn.sol index a0b29e5..3887265 100644 --- a/src/WebAuthnSol/WebAuthn.sol +++ b/src/WebAuthnSol/WebAuthn.sol @@ -24,15 +24,15 @@ library WebAuthn { bytes authenticatorData; /// @dev The WebAuthn client data JSON. /// See https://www.w3.org/TR/webauthn-2/#dom-authenticatorresponse-clientdatajson. - string clientDataJSON; + bytes32 clientDataJSON; /// @dev The index at which "challenge":"..." occurs in `clientDataJSON`. - uint256 challengeIndex; + uint128 challengeIndex; /// @dev The index at which "type":"..." occurs in `clientDataJSON`. - uint256 typeIndex; + uint128 typeIndex; /// @dev The r value of secp256r1 signature - uint256 r; + uint128 r; /// @dev The s value of secp256r1 signature - uint256 s; + uint128 s; }
Note: The bot report did not cover these instances.
File: src/WebAuthnSol/WebAuthn.sol 40: bytes1 private constant AUTH_DATA_FLAGS_UP = 0x01; /// @dev Bit 2 of the authenticator data struct, corresponding to the "User Verified" bit. /// See https://www.w3.org/TR/webauthn-2/#flags. bytes1 private constant AUTH_DATA_FLAGS_UV = 0x04;
diff --git a/src/WebAuthnSol/WebAuthn.sol b/src/WebAuthnSol/WebAuthn.sol index a0b29e5..22860c6 100644 --- a/src/WebAuthnSol/WebAuthn.sol +++ b/src/WebAuthnSol/WebAuthn.sol @@ -43,13 +43,13 @@ library WebAuthn { /// See https://www.w3.org/TR/webauthn-2/#flags. bytes1 private constant AUTH_DATA_FLAGS_UV = 0x04; - /// @dev Secp256r1 curve order / 2 used as guard to prevent signature malleabi lity issue. - uint256 private constant P256_N_DIV_2 = FCL.n / 2; - /// @dev The precompiled contract address to use for signature verification in the “secp256r1” elliptic curve. /// See https://github.com/ethereum/RIPs/blob/master/RIPS/rip-7212.md. address private constant VERIFIER = address(0x100); + /// @dev Secp256r1 curve order / 2 used as guard to prevent signature malleabi lity issue. + uint256 private constant P256_N_DIV_2 = FCL.n / 2; + /// @dev The expected type (hash) in the client data JSON when verifying asser tion signatures. /// See https://www.w3.org/TR/webauthn-2/#dom-collectedclientdata-type bytes32 private constant EXPECTED_TYPE_HASH = keccak256('"type":"webauthn.get" ');
In CoinbaseSmartWallet.sol:executeBatch()
: calls[i]
we can cache once and use multiple times. Instead of repeatedly calling calls[i] in the loop, we can calculate it once and use the result.
File: src/SmartWallet/CoinbaseSmartWallet.sol 205: function executeBatch(Call[] calldata calls) public payable virtual onlyEntryPointOrOwner { for (uint256 i; i < calls.length;) { _call(calls[i].target, calls[i].value, calls[i].data); //@audit cache calls[i] unchecked { ++i; } } }
diff --git a/src/SmartWallet/CoinbaseSmartWallet.sol b/src/SmartWallet/CoinbaseSmartWallet.sol index 2278a5a..1d09e54 100644 --- a/src/SmartWallet/CoinbaseSmartWallet.sol +++ b/src/SmartWallet/CoinbaseSmartWallet.sol @@ -204,7 +204,8 @@ contract CoinbaseSmartWallet is MultiOwnable, UUPSUpgradeable, Receiver, ERC1271 /// @param calls The list of `Call`s to execute. function executeBatch(Call[] calldata calls) public payable virtual onlyEntryPointOrOwner { for (uint256 i; i < calls.length;) { - _call(calls[i].target, calls[i].value, calls[i].data); + uint256 _calls = calls[i]; + _call(_calls.target, _calls.value, _calls.data); unchecked { ++i; }
In MultiOwnable.sol:_initializeOwners()
: owners.length[i]
we can cache once and use multiple times. Instead of repeatedly calling owners.length[i] in the loop, we can calculate it once and use the result.
File: src/SmartWallet/MultiOwnable.sol 162: function _initializeOwners(bytes[] memory owners) internal virtual { for (uint256 i; i < owners.length; i++) { if (owners[i].length != 32 && owners[i].length != 64) { revert InvalidOwnerBytesLength(owners[i]); //@audit } if (owners[i].length == 32 && uint256(bytes32(owners[i])) > type(uint160).max) { revert InvalidEthereumAddressOwner(owners[i]); } _addOwnerAtIndex(owners[i], _getMultiOwnableStorage().nextOwnerIndex++); } }
diff --git a/src/SmartWallet/MultiOwnable.sol b/src/SmartWallet/MultiOwnable.sol index 64fd19a..9a46f34 100644 --- a/src/SmartWallet/MultiOwnable.sol +++ b/src/SmartWallet/MultiOwnable.sol @@ -161,11 +161,12 @@ contract MultiOwnable { /// @param owners The intiial list of owners to register. function _initializeOwners(bytes[] memory owners) internal virtual { for (uint256 i; i < owners.length; i++) { - if (owners[i].length != 32 && owners[i].length != 64) { + uint256 owners_ = owners.length; + if (owners_ != 32 && owners_ != 64) { revert InvalidOwnerBytesLength(owners[i]); } - if (owners[i].length == 32 && uint256(bytes32(own ers[i])) > type(uint160).max) { + if (owners_ == 32 && uint256(bytes32(owners[i])) > type(uint160).max) { revert InvalidEthereumAddressOwner(owners[i]) ; }
#0 - raymondfam
2024-03-22T21:45:59Z
3 well elaborate G.
#1 - c4-pre-sort
2024-03-22T21:46:03Z
raymondfam marked the issue as sufficient quality report
#2 - c4-judge
2024-03-27T13:17:46Z
3docSec marked the issue as grade-b