Omniscia SaucerSwap Audit

TransferHelper Manual Review Findings

TransferHelper Manual Review Findings

THR-01M: Inexistent Cast of Variable

Description:

The value argument of the IHederaTokenService::transferToken call needs to be of type int64, however, no such casting occurs within the TransferHelper::safeTransferFrom function.

Impact:

As the value is not cast to its signed counterpart, the IHederaTokenService::transferToken call may improperly consume the input value thus causing incorrect transfers to occur.

Example:

contracts/libraries/TransferHelper.sol
25(bool success, bytes memory result) = precompileAddress.call(
26 abi.encodeWithSelector(IHederaTokenService.transferToken.selector,
27 token, from, to, (value)));

Recommendation:

We advise the value variable to be safely cast to the int64 data type, ensuring that the invocation style is compliant with the IHederaTokenService interface.

Alleviation (d8d187efd1fa23b943c82694aaaccb5b9e427096):

The code has been refactored throughout its functions to consistently utilize the IHederaTokenService when performing token actions and in this refactor, all uint256 to int64 conversions are now performed using safe casting.

As such, we consider this exhibit alleviated.

THR-02M: Inexistent Integration of the Hedera Token Service

Description:

The referenced functions indicate points where the IHederaTokenService should be integrated for the contract to function properly, however, they remain as they were in their original implementation.

Impact:

The TransferHelper contract does not appear to be fully compatible with the Hedera Protocol ecosystem, rendering the AMM system impossible to operate there.

Example:

contracts/libraries/TransferHelper.sol
36/// @notice Transfers tokens from msg.sender to a recipient
37/// @dev Errors with ST if transfer fails
38/// @param token The contract address of the token which will be transferred
39/// @param to The recipient of the transfer
40/// @param value The value of the transfer
41function safeTransfer(
42 address token,
43 address to,
44 uint256 value
45) internal {
46 (bool success, bytes memory data) = token.call(abi.encodeWithSelector(IERC20.transfer.selector, to, value));
47 require(success && (data.length == 0 || abi.decode(data, (bool))), 'ST');
48 emit Transfer(address(this), to, value, token);
49}
50
51/// @notice Approves the stipulated contract to spend the given allowance in the given token
52/// @dev Errors with 'SA' if transfer fails
53/// @param token The contract address of the token to be approved
54/// @param to The target of the approval
55/// @param value The amount of the given token the target will be allowed to spend
56function safeApprove(
57 address token,
58 address to,
59 uint256 value
60) internal {
61 (bool success, bytes memory data) = token.call(abi.encodeWithSelector(IERC20.approve.selector, to, value));
62 require(success && (data.length == 0 || abi.decode(data, (bool))), 'SA');
63 emit Approval(address(this), to, value, token);
64}

Recommendation:

We advise them to be updated, properly utilizing the relevant functions of the IHederaTokenService to ensure that the TransferHelper contract is properly compatible with the Hedera Protocol ecosystem.

Alleviation (d8d187efd1fa23b943c82694aaaccb5b9e427096):

After discussions with the SaucerSwap team and inspection of the Hedera Improvement Proposals and specifically HIP-218 and HIP-376, we have concluded that a direct invocation of ERC20::transfer / ERC20::approve is equivalent to an invocation of a transfer / approval via the HederaTokenService.

As such, we consider this exhibit nullified. We would like to note that despite this, the SaucerSwap team has opted to refactor their codebase to consistently utilize the IHederaTokenService when interacting with tokens thus increasing the standardization of their codebase.