Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

QA Report #169

Closed
code423n4 opened this issue Jun 3, 2022 · 5 comments
Closed

QA Report #169

code423n4 opened this issue Jun 3, 2022 · 5 comments
Labels
bug Something isn't working invalid This doesn't seem right QA (Quality Assurance) Assets are not at risk. State handling, function incorrect as to spec, issues with clarity, syntax

Comments

@code423n4
Copy link
Contributor

Table of Contents:

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

Same finding from other contests:

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). If there is only one argument to abi.encodePacked() it can often be cast to bytes() or bytes32() instead.

contracts/conduit/ConduitController.sol:
   72                      keccak256(
   73:                         abi.encodePacked(
   74                              bytes1(0xff),
   75                              address(this),
   76                              conduitKey,
   77                              _CONDUIT_CREATION_CODE_HASH
   78                          )
   79                      )

  324                      keccak256(
  325:                         abi.encodePacked(
  326                              bytes1(0xff),
  327                              address(this),
  328                              conduitKey,
  329                              _CONDUIT_CREATION_CODE_HASH
  330                          )
  331                      )

contracts/lib/ConsiderationBase.sol:
  193          eip712DomainTypehash = keccak256(
  194:             abi.encodePacked(
  195                  "EIP712Domain(",
  196                      "string name,",
  197                      "string version,",
  198                      "uint256 chainId,",
  199                      "address verifyingContract",
  200                  ")"
  201              )
  202          );

  211          orderTypehash = keccak256(
  212:             abi.encodePacked(
  213                  orderComponentsPartialTypeString,
  214                  considerationItemTypeString,
  215                  offerItemTypeString
  216              )
  217          );

[N-01] Use at least Solidity 0.8.12 to get string.concat()

Use a solidity version of at least 0.8.12 to be able to use string.concat() instead of abi.encodePacked():

contracts/conduit/ConduitController.sol:
    2: pragma solidity >=0.8.7;
   73:                         abi.encodePacked(
  325:                         abi.encodePacked(

Additionally, consider using string.concat() instead of abi.encodePacked() here:

contracts/lib/ConsiderationBase.sol:
    2: pragma solidity 0.8.13;
  150:         bytes memory offerItemTypeString = abi.encodePacked(
  162:         bytes memory considerationItemTypeString = abi.encodePacked(
  175:         bytes memory orderComponentsPartialTypeString = abi.encodePacked(
  194:             abi.encodePacked(
  212:             abi.encodePacked(

[N-02] Unused named returns

While not consuming more gas with the Optimizer enabled: using both named returns and a return statement isn't necessary. Removing one of those can improve code clarity:

  • File: Consideration.sol
215:     function fulfillAvailableOrders(
...
225:         returns (bool[] memory availableOrders, Execution[] memory executions)
226:     {
...
228:         return
229:             _fulfillAvailableAdvancedOrders(
230:                 _convertOrdersToAdvanced(orders), // Convert to advanced orders.
231:                 new CriteriaResolver[](0), // No criteria resolvers supplied.
232:                 offerFulfillments,
233:                 considerationFulfillments,
234:                 fulfillerConduitKey,
235:                 maximumFulfilled
236:             );
237:     }
300:     function fulfillAvailableAdvancedOrders(
...
311:         returns (bool[] memory availableOrders, Execution[] memory executions)
312:     {
...
314:         return
315:             _fulfillAvailableAdvancedOrders(
316:                 advancedOrders,
317:                 criteriaResolvers,
318:                 offerFulfillments,
319:                 considerationFulfillments,
320:                 fulfillerConduitKey,
321:                 maximumFulfilled
322:             );
323:     }
349:     function matchOrders(
...
352:     ) external payable override returns (Execution[] memory executions) {
...
354:         return
355:             _matchAdvancedOrders(
356:                 _convertOrdersToAdvanced(orders),
357:                 new CriteriaResolver[](0), // No criteria resolvers supplied.
358:                 fulfillments
359:             );
360:     }
398:     function matchAdvancedOrders(
...
402:     ) external payable override returns (Execution[] memory executions) {
...
404:         return
405:             _matchAdvancedOrders(
406:                 advancedOrders,
407:                 criteriaResolvers,
408:                 fulfillments
409:             );
410:     }
  • File: OrderCombiner.sol
107:     function _fulfillAvailableAdvancedOrders(
...
116:         returns (bool[] memory availableOrders, Execution[] memory executions)
117:     {
...
135:         return (availableOrders, executions);
445:     function _executeAvailableFulfillments(
...
452:         returns (bool[] memory availableOrders, Execution[] memory executions)
453:     {
...
547:         return (availableOrders, executions);
548:     }
704:     function _matchAdvancedOrders(
...
708:     ) internal returns (Execution[] memory executions) {
...
718:         return _fulfillAdvancedOrders(advancedOrders, fulfillments);
719:     }
738:     function _fulfillAdvancedOrders(
...
741:     ) internal returns (Execution[] memory executions) {
...
791:         return (executions);
792:     }
  • File: OrderFulfiller.sol
457:     function _convertOrdersToAdvanced(Order[] calldata orders)
...
460:         returns (AdvancedOrder[] memory advancedOrders)
...
478:         return advancedOrders;
479:     }

[N-03] It's better to emit after all processing is done

contracts/conduit/ConduitController.sol (1):

  201:         // Emit an event indicating that the potential owner has been updated.
  202:         emit PotentialOwnerUpdated(conduit, newPotentialOwner);
  203  
  204          // Set the new potential owner as the potential owner of the conduit.
  205          _conduits[conduit].potentialOwner = newPotentialOwner;

contracts/conduit/ConduitController.sol (2):

  218:         // Emit an event indicating that the potential owner has been cleared.
  219:         emit PotentialOwnerUpdated(conduit, address(0));
  220  
  221          // Clear the current new potential owner from the conduit.
  222          delete _conduits[conduit].potentialOwner;

contracts/conduit/ConduitController.sol (3 & 4):

  242:         // Emit an event indicating that the potential owner has been cleared.
  243:         emit PotentialOwnerUpdated(conduit, address(0));
  244  
  245          // Clear the current new potential owner from the conduit.
  246          delete _conduits[conduit].potentialOwner;
  247  
  248:         // Emit an event indicating conduit ownership has been transferred.
  249:         emit OwnershipTransferred(
  250              conduit,
  251              _conduits[conduit].owner,
  252              msg.sender
  253          );
  254  
  255          // Set the caller as the owner of the conduit.
  256          _conduits[conduit].owner = msg.sender;

[N-04] Avoid floating pragmas: the version should be locked

contracts/conduit/Conduit.sol:
  2: pragma solidity >=0.8.7;

contracts/conduit/ConduitController.sol:
  2: pragma solidity >=0.8.7;

contracts/conduit/lib/ConduitEnums.sol:
  2: pragma solidity >=0.8.7;

contracts/conduit/lib/ConduitStructs.sol:
  2: pragma solidity >=0.8.7;

contracts/interfaces/AbridgedTokenInterfaces.sol:
  2: pragma solidity >=0.8.7;

contracts/interfaces/AmountDerivationErrors.sol:
  2: pragma solidity >=0.8.7;

contracts/interfaces/ConduitControllerInterface.sol:
  2: pragma solidity >=0.8.7;

contracts/interfaces/ConduitInterface.sol:
  2: pragma solidity >=0.8.7;

contracts/interfaces/ConsiderationEventsAndErrors.sol:
  2: pragma solidity >=0.8.7;

contracts/interfaces/ConsiderationInterface.sol:
  2: pragma solidity >=0.8.7;

contracts/interfaces/CriteriaResolutionErrors.sol:
  2: pragma solidity >=0.8.7;

contracts/interfaces/EIP1271Interface.sol:
  2: pragma solidity >=0.8.7;

contracts/interfaces/FulfillmentApplicationErrors.sol:
  2: pragma solidity >=0.8.7;

contracts/interfaces/ImmutableCreate2FactoryInterface.sol:
  2: pragma solidity >=0.8.7;

contracts/interfaces/ReentrancyErrors.sol:
  2: pragma solidity >=0.8.7;

contracts/interfaces/SeaportInterface.sol:
  2: pragma solidity >=0.8.7;

contracts/interfaces/SignatureVerificationErrors.sol:
  2: pragma solidity >=0.8.7;

contracts/interfaces/TokenTransferrerErrors.sol:
  2: pragma solidity >=0.8.7;

contracts/interfaces/ZoneInteractionErrors.sol:
  2: pragma solidity >=0.8.7;

contracts/interfaces/ZoneInterface.sol:
  2: pragma solidity >=0.8.7;

contracts/lib/ConsiderationConstants.sol:
  2: pragma solidity >=0.8.7;

contracts/lib/ConsiderationEnums.sol:
  2: pragma solidity >=0.8.7;

contracts/lib/ConsiderationStructs.sol:
  2: pragma solidity >=0.8.7;

contracts/lib/TokenTransferrer.sol:
  2: pragma solidity >=0.8.7;

contracts/lib/TokenTransferrerConstants.sol:
  2: pragma solidity >=0.8.7;

[N-05] Missing NatSpec comments

The following comments are missing (see @audit tags):

  • contracts/interfaces/ConduitControllerInterface.sol:
  139      /**
  140       * @notice Initiate conduit ownership transfer by assigning a new potential
  141       *         owner for the given conduit. Once set, the new potential owner
  142       *         may call `acceptOwnership` to claim ownership of the conduit.
  143       *         Only the owner of the conduit in question may call this function.
  144       *
  145:      * @param conduit The conduit for which to initiate ownership transfer. //@audit missing @param newPotentialOwner
  146       */
  147      function transferOwnership(address conduit, address newPotentialOwner)
  148          external;
  • contracts/lib/AmountDeriver.sol:
  123      /**
  124       * @dev Internal pure function to apply a fraction to a consideration
  125       * or offer item.
  126       *
  127       * @param startAmount     The starting amount of the item.
  128       * @param endAmount       The ending amount of the item.
  129       * @param numerator       A value indicating the portion of the order that
  130       *                        should be filled.
  131       * @param denominator     A value indicating the total size of the order.
  132       * @param elapsed         The time elapsed since the order's start time.
  133       * @param remaining       The time left until the order's end time.
  134:      * @param duration        The total duration of the order. //@audit missing @param roundUp
  135       *
  136       * @return amount The received item to transfer with the final amount.
  137       */
  138      function _applyFraction(
  139          uint256 startAmount,
  140          uint256 endAmount,
  141          uint256 numerator,
  142          uint256 denominator,
  143          uint256 elapsed,
  144          uint256 remaining,
  145          uint256 duration,
  146          bool roundUp
  147      ) internal pure returns (uint256 amount) {
  • contracts/lib/BasicOrderFulfiller.sol:
  1001      /**
  1002       * @dev Internal function to transfer ERC20 tokens to a given recipient as
  1003       *      part of basic order fulfillment.
  1004       *
  1005       * @param from                 The originator of the ERC20 token transfer.
  1006       * @param to                   The recipient of the ERC20 token transfer.
  1007       * @param erc20Token           The ERC20 token to transfer.
  1008       * @param amount               The amount of ERC20 tokens to transfer.
  1009       * @param additionalRecipients The additional recipients of the order.
  1010       * @param fromOfferer          A boolean indicating whether to decrement
  1011:      *                             amount from the offered amount.  //@audit missing @param accumulator
  1012       */
  1013      function _transferERC20AndFinalize(
  1014          address from,
  1015          address to,
  1016          address erc20Token,
  1017          uint256 amount,
  1018          AdditionalRecipient[] calldata additionalRecipients,
  1019          bool fromOfferer,
  1020          bytes memory accumulator
  1021      ) internal {
@code423n4 code423n4 added bug Something isn't working QA (Quality Assurance) Assets are not at risk. State handling, function incorrect as to spec, issues with clarity, syntax labels Jun 3, 2022
code423n4 added a commit that referenced this issue Jun 3, 2022
@0xleastwood 0xleastwood added the invalid This doesn't seem right label Jun 24, 2022
@HardlyDifficult HardlyDifficult removed the invalid This doesn't seem right label Jul 6, 2022
@code-423n4 code-423n4 deleted a comment from 0xleastwood Jul 6, 2022
@GalloDaSballo
Copy link

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

Don't believe any meaningful risk was shown, you'd want to avoid using encodePacked to avoid hash-clashes, however none were demonstrated

[N-01] Use at least Solidity 0.8.12 to get string.concat()

This would be just syntactic sugar, don't think it's valid

[N-02] Unused named returns

The codebase always returns the named variable explicitly, which is done consistently. Hence the named return is used.

[N-03] It's better to emit after all processing is done

Given the specific context, I assume the code was re-written to avoid false positives from Slither (state change before "external calls". I believe the events are emitted properly, and consistently

[N-04] Avoid floating pragmas: the version should be locked

Disagree per discussion on #67

[N-05] Missing NatSpec comments

I don't believe this to be valid, natspec is always optional

I think the judges were correct in marking this report invalid

@IllIllI000
Copy link

NatSpec is optional, but if it is added, it's inconsistent to only list some of the params

@GalloDaSballo
Copy link

Still disagree but giving a valid NC

@HardlyDifficult
Copy link
Collaborator

The bar for QA reports in this contest is at least 2 valid non-critical findings or at least one valid low risk finding. Per the comments above, this submission is below that bar -- closing as invalid.

@HardlyDifficult HardlyDifficult added the invalid This doesn't seem right label Jul 20, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working invalid This doesn't seem right QA (Quality Assurance) Assets are not at risk. State handling, function incorrect as to spec, issues with clarity, syntax
Projects
None yet
Development

No branches or pull requests

5 participants