This is a sample Aspect that can prevent hacks similar to the reentrant attack happened to Curve.fi on 2023.07.
Aspect is a new way to build on-chain native extensions. If you are interested about what is Aspect and how Aspect works, please check out our website and dev guide to learn more details.
This is how the attack happens in short:
- The attacked Curve.fi pool is written in vyper (another smart contract lang can be compiled into EVM bytecode).
- The version of vyper they are using is 0.2.15, which is affected by a known reentrant lock issue in the compiler.
- The attacker bypassed the reentrancy lock by calling
add_liquidity
fromremove_liquidity
through fallback function, which should be blocked by the reentrancy guard but in fact not.
To delve into the attack details, you can check out the attack transaction here.
Learn How does Aspect Programming prevent reentrancy attacks through on-chain runtime protection.
The example contract and Aspect provided in this repo are mainly the following 3 files:
contracts/curve.vy
This is a simplified version of Curve smart contract implemented with vyper. Similar to the Curve pool, it has two methods: add_liquidity
and remove_liquidity
, which are both guarded by the same reentrant lock. AddLiquidity
and RemoveLiquidity
events will be emitted when corresponding method gets called.
contracts/attack.sol
This is a simplified version of attack contract implemented in solidity. It will try to start a reentrant attack through the remove_liquidity
with its fallback function.
assembly/aspect/aspect.ts
This is a runtime reentrant protection Aspect that can detect reentrant attack by checking the callstacks during the transaction execution, and it will revert the transaction immediately if it observed any duplicate calls on the call path.
To reproduce the attack and show how Aspect can protect runtime reentrancy, we have provided 2 files in the scripts
folder: curve-reentrancy.cjs
and curve-reentrancy-with-aspect.cjs
, where,
curve-reentrancy.cjs
will deploy the attack contract and Curve contract, and make the attack. You can observe a successful reentrancy by running this script.curve-reentrancy-with-aspect.cjs
will not only do the above steps but also deploy and bind the reentrancy protection Aspect to the Curve contract before the attack. You will observe a reentrancy failure by running this script, since the reentrancy will be stopped by the Aspect.
To reproduce the attack, you need to install solc and vyper (specific version with reentrant lock bug) first
pip install vyper==0.2.16
npm install -g [email protected]
Put your private key hex with some ART in private.key
file in the root folder.
-
Install Nodejs dependencies
npm install
-
Build smart contracts and aspects
npm run build
-
Execute the re-entrant attack on the simplified Curve contract, and observe the log events in the receipt. If the reentrant attack succeeded, you will see both
AddLiquidity
andRemoveLiquidity
events logged.npm run reentrant:attack
-
Execute the re-entrant attack on the simplified Curve contract with Aspect protection, and observe the receipt. If the protection succeeded, you will see the transaction gets reverted.
npm run reentrant:guard