This repo contains hack.py
file that demonstrates how ECO tokens can be stolen from Peanut.
The script demonstrates how to steal ECO from Peanut V3 on Optimism since this contract holds the most ECO tokens, but in general all Peanut contracts (V3, V4, V5) across all chains where ECO is deployed are at risk.
All ECO tokens across all Peanut contracts can be stolen.
In Peanut contracts, there is dedicated support for ECO token (which is inflationary). In case of Peanut V3, there are special contractType
4 and 5 that are used for ECO deposits. However, Peanut doesn't restrict the users from depositing ECO with contractType = 1
(i.e. as a casual ERC20 token).
ECO token is inflationary. Let's call ECO's inflation multiplier as mult
. So, ECO transfers work as follows:
- User calls
ECO.transfer(recipient, amount)
- ECO contract under the hood transfers
amount * mult
.
mult
is something that slowely increases over time. At the time of writing it is ~1.025
on Optimism (i.e. the inflation is 2.5%).
So let's say a user calls Peanut's makeDeposit function with X
being the amount of ECO tokens that they wish to deposit. The amount transferred under the hood is X * mult1
(let's call current multiplier value mult1
).
Now, let's say that an inflation event has occured and mult is now mult2 = 1.035
(i.e. the inflation is 3.5%). The user withdraws their deposit. The Peanut contract calls ECO.transfer(X)
. Everything seems smooth, but! Under the hood ECO contract transfers X * mult2
ECO tokens from Peanut to the user.
Since mult2
is greater than mult1
, the user recevies X * (mult2 - mult1)
more ECO tokens that hey initially deposited. And this extra is taken from the general Peanut ECO balance, i.e. from other Peanut deposits.
So, if during ECO's inflation increase event, somebody:
- Deposits ECO tokens to Peanut via
contractType = 1
(i.e. a pure ERC20 token deposit) - Waits until the inflation multiplier is increased
- Withdraws the deposited tokens
They receive more tokens than they deposited. And thus are able to steal all ECO tokens in a certain Peanut contract.
What's also interesting here is that even if there is no malicious actor, but just some people use contractType = 1
for ECO deposits, they are already draining the peanut contracts.
The python script that I made works with a Tenderly DevNet fork of Optimism Mainnet. So, in order to reproduce the attack, do the following:
- Create a Tenderl DevNet
- Go to https://tenderly.co/devnets
- Create a tenderly account if you don't have one yet
- Click "Create Template" button in top right
- In the YAML template field paste the content from
tenderly-config.yaml
in this repo. - Click "Create". This will create a pure fork of current state of Optimism Mainnet with no modifications.
- Click on the created template.
- Click "Spawn DevNet" button.
- Copy the rpc url and insert it in
OPTIMISM_RPC
constant at the top ofhack.py
file.
- Setup python environment
- Create a python virtual environment. I personally used python3.11 and created the venv via
python3.11 -m venv venv
, but the script might work with other python versions too (I have not tested though). - Activate the venv via
source venv/bin/activate
- Install packages via
pip install -r requirements.txt
- Create a python virtual environment. I personally used python3.11 and created the venv via
- Hack it! Type
python hack.py
.
The script will airdrop some ETH and some ECO tokens to the drainer account.
Then the drainer account will deposit its ECO tokens in the Peanut contract.
Then the script will increase the inflation multiplier by 0.01 (1%). ECO inflation multiplier is being increased regularly in production by the ECO team.
Then the drainer will withdraw its deposit.
And voila! We have just drained / stolen ECO tokens!