runNextAction
is a function called by the keeper, and relies on the keeper to provide the input parameters that are used throughout the function.
The issue is how the metadata
that is passed in, is handled. It is used inconsistently, especially for its data regarding dexSwap
and gmx
.
The inconsistent use can lead to wrong information being used for actions that involve swapping on dex
and gmx
and creating positions with gmx
.
metadata
is swap data that is passed in to runNextAction
by the keeper and it can include Only Paraswap dex
data, gmx
data, or both. But if both are included, the first element should always be Paraswap dex
. This is stated and verified in _runSwap
: which is called throughout runNextAction
Lets look at runNextAction
to see how metadata
is used initially. For the scenario of 1x long, it calls runSwap
and passes in the metadata
that it receieved from the keeper:
_runSwap
then proceeds, depending on if the metadata
includes both elements dex
and gmx
or just 1.
In the case it includes BOTH it enforces that the first element is dex
. And if only 1 element is sent in, it checks if the first element is dex
. Thorughout, it expects and operates off of metadata[0]
being the dex
element.
It also reverts, if the first element is not dex
With the handling and expectation of the first element of metadata
being dex
.
runNextAction
inconsistently uses metadata
in a different way in other operations, within the same function, which was just expected to have metadata
that has the first element being dex
and reverting if not.
lets examine the next line of code after the runSwap just seen.
This now uses metadata[1]
for dexSwap
-> which we just saw should have the data for dex
at position metadata[0]
- the first element of the metadata
array.
Also, it uses acceptablePrice
from metadata[0]
- but it should be using gmx
data at metadata[1]
-> because that is used for createIncreasePosition
-> which interacts with gmx
and never with a dexSwap
.
The same misuse of metadata
is used in runNextAction
when NextActionSelector.WITHDRAW
:
_withdraw
uses metadata
to retreive acceptablePrice
same as above.
BUT, In the very same function, the very next scenario NextActionSelector.SWAP_ACTION
It calls _runSwap
with the same metadata
passed in, but this uses the metadata
in the correct way / expected way. It will use metadata[0]
for dexSwap
and metadata[1]
for gmx
_runSwap
example was used above, this is just showing that within the same function runNextAction
-> the exact same metadata
is used inconsistently and asymmetrically.
The actions for swapping directly in runNExtAction
will use the wrong data for swaps on dexSwap
-> it will use the data that is representing gmx
.
When calling createIncreasePosition
and withdraw
, it will pass in data that is for dexSwap
, when those functions interact only with gmx
and are expecting the data representing gmx
-> metadata[1]
This can lead to wrong data being used for swaps and order creations on gmx, which can have unintended consequences.
Manual Review
Follow a consistent use of metadata
-> the metadata
elements array does not change throughout the function runNExtSwap
-> it is always the same as is when passed in.
I suggest following the expectation of the order of elements as described in _runSwap
as the protocol is expecting and checks for the correct order of elements, and reverts if not.
Please read the CodeHawks documentation to know which submissions are valid. If you disagree, provide a coded PoC and explain the real likelihood and the detailed impact on the mainnet without any supposition (if, it could, etc) to prove your point. Keepers are added by the admin, there is no "malicious keeper" and if there is a problem in those keepers, that's out of scope. ReadMe and known issues states: " * System relies heavily on keeper for executing trades * Single keeper point of failure if not properly distributed * Malicious keeper could potentially front-run or delay transactions * Assume that Keeper will always have enough gas to execute transactions. There is a pay execution fee function, but the assumption should be that there's more than enough gas to cover transaction failures, retries, etc * There are two spot swap functionalies: (1) using GMX swap and (2) using Paraswap. We can assume that any swap failure will be retried until success. " " * Heavy dependency on GMX protocol functioning correctly * Owner can update GMX-related addresses * Changes in GMX protocol could impact system operations * We can assume that the GMX keeper won't misbehave, delay, or go offline. " "Issues related to GMX Keepers being DOS'd or losing functionality would be considered invalid."
There is no real proof, concrete root cause, specific impact, or enough details in those submissions. Examples include: "It could happen" without specifying when, "If this impossible case happens," "Unexpected behavior," etc. Make a Proof of Concept (PoC) using external functions and realistic parameters. Do not test only the internal function where you think you found something.
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.