PoolTogether contest - cryptonue's results

A no-loss prize-savings protocol.

General Information

Platform: Code4rena

Start Date: 01/12/2022

Pot Size: $26,900 USDC

Total HM: 3

Participants: 19

Period: 4 days

Judge: GalloDaSballo

Id: 188

League: ETH

PoolTogether

Findings Distribution

Researcher Performance

Rank: 12/19

Findings: 2

Award: $665.02

QA:
grade-a
Gas:
grade-b

🌟 Selected for report: 0

🚀 Solo Findings: 0

Findings Information

🌟 Selected for report: 0xSmartContract

Also found by: cryptonue, gzeon, ladboy233

Labels

bug
grade-a
QA (Quality Assurance)
edited-by-warden
Q-04

Awards

611.5965 USDC - $611.60

External Links

There is no point implementing the setExecutor function

Since the setExecutor function can only be called once, which is to set the executor, meanwhile if the executor is not set, the executeCalls will always revert because not passing the _isAuthorized() function, so it's best just remove the setExecutor and directly set the executor on the constructor (and make immutable).

As simple as, if executor is yet to be known, just wait until it's known, then deployment the contract.

139:   function setExecutor(ICrossChainExecutor _executor) external {
140:     require(address(executor) == address(0), "Relayer/executor-already-set");
141:     executor = _executor;
142:   }

using abi.encodedPacked which is not preferably be used because of ambiguity

File: CallLib.sol
64:       (bool _success, bytes memory _returnData) = _call.target.call(
65:         abi.encodePacked(_call.data, _nonce, _sender)
66:       );

abi.encodePacked is a non standard encoding that should preferably not be used due to its ambiguity with dynamic types, if you are not dealing with dynamic types and inputs which size is already multiple of 32 then they can both give the same output.

In general, the encoding is ambiguous as soon as there are two dynamically-sized elements, because of the missing length field.

No check if _calls is empty (length is 0)

All of Executors (EthereumToArbitrumExecutor, EthereumToOptimismExecutor, EthereumToPolygonExecutor), when trying to call CallLib.executeCalls the _calls parameter input are not checked if it cointains call (length > 0) or not.

We can just revert if the _calls length is 0

No event of storage variable changes (setRelayer)

On setRelayer function, it doesnt have any emit event, it's good practice to emit an event when storage variable is changed.

#0 - GalloDaSballo

2022-12-14T19:59:58Z

There is no point implementing the setExecutor function

I think I'll ignore the finding as technically that's correct, but you need relayer and executor to be set, it's a chicken and egg problem

using abi.encodedPacked which is not preferably be used because of ambiguity

Disputed by Sponsor

Will flag to sponsor as packed vs unpacked will have a different result, but am not sure this is valid Specifically I believe call.data and nonce can actually clash and create undefined scenarios.

This I believe is the first time in C4 in which I believe encode should be preferred to avoid very low probably edge cases

No check if _calls is empty (length is 0)

R

No event of storage variable changes (setRelayer)

NC

#1 - GalloDaSballo

2022-12-26T22:08:03Z

@PierrickGT I made a note to flag this one, specifically I have some reason to believe that abi.encode should be preferred vs abi.encodePacked

1L 1R 1NC

#2 - c4-judge

2022-12-26T23:14:08Z

GalloDaSballo marked the issue as grade-a

#3 - PierrickGT

2022-12-27T18:30:28Z

@GalloDaSballo we follow EIP-2771 to happen the nonce and sender to the call data which in most cases will be a function call using abi.encodeWithSignature(): https://github.com/pooltogether/ERC5164/blob/62d562b19e0d08e54e498e2322af456f6abd6550/script/bridge/BridgeToOptimismGoerli.s.sol#L23

If we use abi.encode instead of abi.encodePacked, the call will revert and fail to execute.

#4 - GalloDaSballo

2022-12-27T19:14:54Z

I believe, per the Sponsors comment, that while I had some concerns regarding calldata clashing, we must use abi.encodePacked per the EIP.

Will be updating the score to reflect that.

My thoughts around the clash risk are that I can imagine a situation using fallback, in which the last param is ignored and instead a injected param via abi.encodePacked is used.

#5 - GalloDaSballo

2023-01-05T08:17:24Z

After removing 1L

1R 1NC

The report is still grade A

Findings Information

🌟 Selected for report: Tricko

Also found by: 0x4non, AkshaySrivastav, Madalad, Rolezn, adriro, cryptonue, neko_nyaa

Labels

bug
G (Gas Optimization)
grade-b
edited-by-warden
G-07

Awards

53.4153 USDC - $53.42

External Links

Make relayer immutable

Since relayer can only be set once via setRelayer, this concept is similar to immutable, and this immutable will get directly embedded in bytecode, saving SLOAD.

In order for relayer to be immutable, the relayer need to be set inside constructor, and remove the setRelayer.

If we think about it again, the setRelayer can only be called once when the relayer is not set (address 0), and the executeCalls will revert if the relayer is not set (because of _isAuthorized). There are no other function inside Executor contracts beside executeCalls therefore we can safely remove the setRelayer and set the relayer inside constructor with no changes made to logic.

#0 - GalloDaSballo

2022-12-26T21:21:01Z

1050

#1 - c4-judge

2022-12-26T23:13:35Z

GalloDaSballo marked the issue as grade-b

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