Omniscia Native Audit
PeripheryPayments Manual Review Findings
PeripheryPayments Manual Review Findings
PPS-01M: Improper Payable Attribute of Functions
| Type | Severity | Location |
|---|---|---|
| Language Specific | ![]() | PeripheryPayments.sol:L21, L36, L51, L72, L97, L105, L109 |
Description:
The referenced functions are set as payable yet do not handle native funds sent to them.
Impact:
Funds transmitted as part of the referenced payable calls can be compromised by any party via methods like refundETH.
Example:
19// public methods20/// @inheritdoc IPeripheryPayments21function unwrapWETH9(uint256 amountMinimum, address recipient) public payable override {22 uint256 balanceWETH9 = IWETH9(WETH9).balanceOf(address(this));23 require(balanceWETH9 >= amountMinimum, "Insufficient WETH9");24
25 if (balanceWETH9 > 0) {26 IWETH9(WETH9).withdraw(balanceWETH9);27 TransferHelper.safeTransferETH(recipient, balanceWETH9);28 }29}Recommendation:
We advise them to omit the payable keyword, preventing funds from being locked or mis-sent to the contract.
Alleviation:
After extensive discussions with the Native team, we concluded that this is the same paradigm applied by Uniswap V3 and is done for the sake of optimality. As such, the code behaves as expected and is meant to tap into its own funds prior to consuming the user's funds for fulfilling WETH9 payments.
PPS-02M: Inexistent Evaluation of Payer
| Type | Severity | Location |
|---|---|---|
| Logical Fault | ![]() | PeripheryPayments.sol:L120 |
Description:
The pay function will incorrectly assume that the payer is the contract itself when the token is set as WETH9 and the contract has sufficient balance.
Impact:
Unexpected behaviour can currently arise in the Router::swapCallback implementation when dealing with multiple orders in sequence that contain the WETH9 token.
Example:
114// internal methods115/// @param token The token to pay116/// @param payer The entity that must pay117/// @param recipient The entity that will receive payment118/// @param value The amount to pay119function pay(address token, address payer, address recipient, uint256 value) internal {120 if (token == WETH9 && address(this).balance >= value) {121 //require(address(this).balance >= value, "Insufficient native token value");122 // pay with WETH9123 IWETH9(WETH9).deposit{value: value}(); // wrap only what is needed to pay124 IWETH9(WETH9).transfer(recipient, value);125 } else if (payer == address(this)) {126 // pay with tokens already in the contract (for the exact input multihop case)127 TransferHelper.safeTransfer(token, recipient, value);128 } else {129 // pull payment130 TransferHelper.safeTransferFrom(token, payer, recipient, value);131 }132}Recommendation:
Given that the contract can be used in multiple orders, a user may want to use their own WETH to satisfy a particular payment and use the contract's address(this).balance for another purpose. We advise the code to properly validate that the payer is the contract itself in the first conditional. As an added note, the conditionals can be optimized by including an outer if-else branch that evaluates payer == address(this) and an inner if-else clause that evaluates token == WETH9 && address(this).balance >= value, optimizing the code's gas cost.
Alleviation:
After extensive discussions with the Native team, we concluded that this is the same paradigm applied by Uniswap V3 and is done for the sake of optimality. As such, the code behaves as expected and is meant to tap into its own funds prior to consuming the user's funds for fulfilling WETH9 payments.
