Skip to content

Commit

Permalink
fix: solidity 0.8.25; transient storage; nonreentrant using transient…
Browse files Browse the repository at this point in the history
… storage; prevent flash loan attack on liquidity using transient storage;
  • Loading branch information
kyriediculous committed Jul 3, 2024
1 parent f0a6cc0 commit 16d20af
Show file tree
Hide file tree
Showing 36 changed files with 126 additions and 49 deletions.
17 changes: 13 additions & 4 deletions .solhint.json
Original file line number Diff line number Diff line change
@@ -1,19 +1,28 @@
{
"extends": "solhint:recommended",
"rules": {
"code-complexity": ["error", 8],
"compiler-version": ["error", ">=0.8.20"],
"code-complexity": [
"error",
8
],
"compiler-version": [
"error",
">=0.8.25"
],
"func-name-mixedcase": "off",
"func-visibility": [
"error",
{
"ignoreConstructors": true
}
],
"max-line-length": ["error", 120],
"max-line-length": [
"error",
120
],
"named-parameters-mapping": "warn",
"no-console": "off",
"not-rely-on-time": "off",
"one-contract-per-file": "off"
}
}
}
2 changes: 1 addition & 1 deletion foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
auto_detect_solc = false
block_timestamp = 1_680_220_800 # March 31, 2023 at 00:00 GMT
bytecode_hash = "none"
evm_version = "paris" # See https://www.evmdiff.com/features?name=PUSH0&kind=opcode
evm_version = "cancun" # See https://www.evmdiff.com/features?name=PUSH0&kind=opcode
fuzz = { runs = 1_000 }
gas_reports = ["*"]
optimizer = true
Expand Down
2 changes: 1 addition & 1 deletion script/add_liq.s.sol
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
pragma solidity >=0.8.20;
pragma solidity >=0.8.25;

import { Script, console2 } from "forge-std/Script.sol";
import { Registry } from "@/Registry.sol";
Expand Down
2 changes: 1 addition & 1 deletion script/check.s.sol
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
pragma solidity >=0.8.20;
pragma solidity >=0.8.25;

import { Script, console2 } from "forge-std/Script.sol";
import { UnsETH } from "@/unsETH/UnsETH.sol";
Expand Down
2 changes: 1 addition & 1 deletion script/deploy.local.s.sol
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
pragma solidity >=0.8.20;
pragma solidity >=0.8.25;

import { Script, console2 } from "forge-std/Script.sol";
import { Registry } from "@/Registry.sol";
Expand Down
2 changes: 1 addition & 1 deletion src/Registry.sol
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
//
// Copyright (c) Tenderize Labs Ltd

pragma solidity >=0.8.20;
pragma solidity >=0.8.25;

import { OwnableUpgradeable } from "@openzeppelin/upgradeable/access/OwnableUpgradeable.sol";
import { Initializable } from "@openzeppelin/upgradeable/proxy/utils/Initializable.sol";
Expand Down
13 changes: 8 additions & 5 deletions src/adapters/Adapter.sol
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
pragma solidity >=0.8.20;
pragma solidity >=0.8.25;

library AdapterDelegateCall {
error AdapterDelegateCallFailed(string msg);
Expand All @@ -8,12 +8,15 @@ library AdapterDelegateCall {
(bool success, bytes memory returnData) = address(adapter).delegatecall(data);

if (!success) {
// Next 5 lines from https://ethereum.stackexchange.com/a/83577
if (returnData.length < 68) revert AdapterDelegateCallFailed("");
if (returnData.length < 4) {
revert AdapterDelegateCallFailed("Unknown error occurred");
}

// Bubble up the full return data
assembly {
returnData := add(returnData, 0x04)
let returndata_size := mload(returnData)
revert(add(returnData, 0x20), returndata_size)
}
revert AdapterDelegateCallFailed(abi.decode(returnData, (string)));
}

return returnData;
Expand Down
2 changes: 1 addition & 1 deletion src/adapters/ETHx/ETHxAdapter.sol
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
pragma solidity >=0.8.20;
pragma solidity >=0.8.25;

import { Adapter } from "@/adapters/Adapter.sol";
import { SafeTransferLib } from "solady/utils/SafeTransferLib.sol";
Expand Down
2 changes: 1 addition & 1 deletion src/adapters/eETH/EETHAdapter.sol
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
pragma solidity >=0.8.20;
pragma solidity >=0.8.25;

import { Adapter } from "@/adapters/Adapter.sol";
import { SafeTransferLib } from "solady/utils/SafeTransferLib.sol";
Expand Down
2 changes: 1 addition & 1 deletion src/adapters/lsETH/LsETHAdapter.sol
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
pragma solidity >=0.8.20;
pragma solidity >=0.8.25;

import { Adapter } from "@/adapters/Adapter.sol";
import { IRiver, IRedeemManager } from "@/adapters/lsETH/ILiquidCollective.sol";
Expand Down
2 changes: 1 addition & 1 deletion src/adapters/mETH/METHAdapter.sol
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
pragma solidity >=0.8.20;
pragma solidity >=0.8.25;

import { Adapter } from "@/adapters/Adapter.sol";
import { IStaking } from "@/adapters/mETH/IMantle.sol";
Expand Down
2 changes: 1 addition & 1 deletion src/adapters/stETH/StETHAdapter.sol
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
pragma solidity >=0.8.20;
pragma solidity >=0.8.25;

import { Adapter } from "@/Registry.sol";
import { SafeTransferLib } from "solady/utils/SafeTransferLib.sol";
Expand Down
2 changes: 1 addition & 1 deletion src/adapters/swETH/SwETHAdapter.sol
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
pragma solidity >=0.8.20;
pragma solidity >=0.8.25;

import { Adapter } from "@/adapters/Adapter.sol";
import { IswETH, IswEXIT } from "@/adapters/swETH/ISwell.sol";
Expand Down
2 changes: 1 addition & 1 deletion src/lpETH/LPToken.sol
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
//
// Copyright (c) Tenderize Labs Ltd

pragma solidity >=0.8.20;
pragma solidity >=0.8.25;

import { ERC20 } from "@solady/tokens/ERC20.sol";
import { Ownable } from "solady/auth/Ownable.sol";
Expand Down
48 changes: 40 additions & 8 deletions src/lpETH/LpETH.sol
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
//
// Copyright (c) Tenderize Labs Ltd

pragma solidity >=0.8.20;
pragma solidity >=0.8.25;

import { Registry } from "@/Registry.sol";
import { LPToken } from "@/lpETH/LPToken.sol";
Expand Down Expand Up @@ -69,6 +69,7 @@ abstract contract LpETHEvents {
error ErrorRecoveryMode();
error GaugeZero();
error ErrorInsufficientAmount();
error DepositedInCurrentTx();

event Deposit(address indexed from, uint256 amount, uint256 lpSharesMinted);
event Withdraw(address indexed to, uint256 amount, uint256 lpSharesBurnt, uint256 requestId);
Expand Down Expand Up @@ -135,6 +136,9 @@ contract LpETH is
using UnsETHQueue for UnsETHQueue.Data;
using WithdrawQueue for WithdrawQueue.Data;

// uint256(keccak256("DEPOSITED_IN_CURRENT_TX"))
uint256 private constant NONREENTRANT_TSTORE = 0x20;
uint256 private constant DEPOSITED_IN_CURRENT_TX_TSTORE = 0x40; // tstore slot
LPToken private immutable LPTOKEN = LPToken(address(0));
Registry private immutable REGISTRY = Registry(address(0));
UnsETH private immutable UNSETH = UnsETH(payable(0xA2FE2b9298c03AF9C5d885e62Bc04F77a7Ff91BF));
Expand All @@ -147,6 +151,19 @@ contract LpETH is

receive() external payable { }

modifier nonreentrant() {
assembly {
if tload(NONREENTRANT_TSTORE) { revert(0, 0) }
tstore(NONREENTRANT_TSTORE, 1)
}
_;
// Unlocks the guard, making the pattern composable.
// After the function exits, it can be called again, even in the same transaction.
assembly {
tstore(NONREENTRANT_TSTORE, 0)
}
}

/// @custom:oz-upgrades-unsafe-allow constructor
constructor(ConstructorConfig memory config) {
REGISTRY = config.registry;
Expand Down Expand Up @@ -178,13 +195,28 @@ contract LpETH is
if (lpShares < minLpShares) revert ErrorSlippage(lpShares, minLpShares);
if (lpShares == 0) revert ErrorDepositSharesZero();

LPTOKEN.mint(msg.sender, lpShares);
$.liabilities += msg.value;
LPTOKEN.mint(msg.sender, lpShares);

assembly {
tstore(DEPOSITED_IN_CURRENT_TX_TSTORE, 1)
}

emit Deposit(msg.sender, msg.value, lpShares);
}

function withdraw(uint256 amount, uint256 maxLpSharesBurnt) external returns (uint256 requestId) {
function withdraw(uint256 amount, uint256 maxLpSharesBurnt) external nonreentrant returns (uint256 requestId) {
bytes4 selector = LpETHEvents.DepositedInCurrentTx.selector;
assembly {
// Get the free memory pointer
let freeMemPtr := mload(0x40)
if eq(tload(DEPOSITED_IN_CURRENT_TX_TSTORE), 1) {
// Store the selector at the free memory pointer
mstore(freeMemPtr, selector)
// Revert with the selector stored in memory
revert(freeMemPtr, 0x04)
}
}
Data storage $ = _loadStorageSlot();

uint256 available = ud(amount).mul(UNIT_60x18.sub(ud($.unlocking).div(ud($.liabilities)))).unwrap();
Expand Down Expand Up @@ -219,7 +251,7 @@ contract LpETH is
out = _quote(asset, amount, p);
}

function swap(address asset, uint256 amount, uint256 minOut) external returns (uint256 out) {
function swap(address asset, uint256 amount, uint256 minOut) external nonreentrant returns (uint256 out) {
Data storage $ = _loadStorageSlot();
Adapter adapter = REGISTRY.adapters(asset);
if (address(adapter) == address(0)) revert ErrorInvalidAsset(asset);
Expand Down Expand Up @@ -261,7 +293,7 @@ contract LpETH is
emit Swap(msg.sender, asset, amount, fee, tokenId);
}

function redeemUnlock() external {
function redeemUnlock() external nonreentrant {
Data storage $ = _loadStorageSlot();

// get oldest item from unlock queue
Expand Down Expand Up @@ -295,7 +327,7 @@ contract LpETH is
emit UnlockRedeemed(msg.sender, unlock.tokenId, amountReceived, relayerReward, lpReward);
}

function batchRedeemUnlocks(uint256 n) external {
function batchRedeemUnlocks(uint256 n) external nonreentrant {
Data storage $ = _loadStorageSlot();
uint256 totalReceived;
uint256 totalExpected;
Expand Down Expand Up @@ -362,7 +394,7 @@ contract LpETH is
$.liabilities += lpReward;
}

function buyUnlock(uint256 expectedTokenId) external payable returns (uint256 tokenId) {
function buyUnlock(uint256 expectedTokenId) external payable nonreentrant returns (uint256 tokenId) {
Data storage $ = _loadStorageSlot();

// Can not purchase unlocks in recovery mode
Expand Down Expand Up @@ -413,7 +445,7 @@ contract LpETH is
emit UnlockBought(msg.sender, tokenId, request.amount, reward, lpCut);
}

function batchBuyUnlock(uint256 n, uint256 expectedStartId) external payable {
function batchBuyUnlock(uint256 n, uint256 expectedStartId) external payable nonreentrant {
Data storage $ = _loadStorageSlot();

// Can not purchase unlocks in recovery mode
Expand Down
2 changes: 1 addition & 1 deletion src/lpETH/UnsETHQueue.sol
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
//
// Copyright (c) Tenderize Labs Ltd

pragma solidity >=0.8.20;
pragma solidity >=0.8.25;

/**
* @notice This file implements the necessary functionality for a double-ended queue or deque.
Expand Down
2 changes: 1 addition & 1 deletion src/lpETH/WithdrawQueue.sol
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
//
// Copyright (c) Tenderize Labs Ltd

pragma solidity >=0.8.20;
pragma solidity >=0.8.25;

import { SafeTransferLib } from "@solady/utils/SafeTransferLib.sol";

Expand Down
29 changes: 29 additions & 0 deletions src/periphery/SwapRouter.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
pragma solidity ^0.8.25;

import { LpETH } from "@/lpETH/LpETH.sol";
import { ERC20 } from "@solady/tokens/ERC20.sol";
import { SafeTransferLib } from "@solady/utils/SafeTransferLib.sol";

address constant WRAPPED_STETH = 0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0;
address constant WRAPPED_EETH = 0xCd5fE23C85820F7B72D0926FC9b05b43E359b7ee;

LpETH constant LPETH = LpETH(payable(address(2)));

interface Unwrap {
function unwrap(uint256 amount) external returns (uint256);
}

contract SwapRouter {
using SafeTransferLib for address;

receive() external payable { }

function swap(address tokenIn, uint256 amount, uint256 minOut) external returns (uint256 out) {
tokenIn.safeTransferFrom(msg.sender, address(this), amount);
if (tokenIn == WRAPPED_STETH || tokenIn == WRAPPED_EETH) {
amount = Unwrap(tokenIn).unwrap(amount);
}
out = LPETH.swap(tokenIn, amount, minOut);
payable(msg.sender).transfer(out);
}
}
2 changes: 1 addition & 1 deletion src/unsETH/Base64.sol
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
//
// Copyright (c) Tenderize Labs Ltd

pragma solidity >=0.8.20;
pragma solidity >=0.8.25;

/**
* Based on Brecht Devos (Brechtpd) implementation - MIT licence
Expand Down
2 changes: 1 addition & 1 deletion src/unsETH/Renderer.sol
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
//
// Copyright (c) Tenderize Labs Ltd

pragma solidity >=0.8.20;
pragma solidity >=0.8.25;

import { Initializable } from "@openzeppelin/upgradeable/proxy/utils/Initializable.sol";
import { UUPSUpgradeable } from "@openzeppelin/upgradeable/proxy/utils/UUPSUpgradeable.sol";
Expand Down
2 changes: 1 addition & 1 deletion src/unsETH/UnsETH.sol
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import { Registry } from "@/Registry.sol";
import { ERC721Receiver } from "@/utils/ERC721Receiver.sol";
import { Adapter, AdapterDelegateCall } from "@/adapters/Adapter.sol";

pragma solidity >=0.8.20;
pragma solidity >=0.8.25;

// solhint-disable quotes

Expand Down
2 changes: 1 addition & 1 deletion src/utils/ERC721Receiver.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

// SPDX-License-Identifier: MIT

pragma solidity >=0.8.20;
pragma solidity >=0.8.25;

abstract contract ERC721Receiver {
function onERC721Received(address, address, uint256, bytes calldata) external pure returns (bytes4) {
Expand Down
2 changes: 1 addition & 1 deletion src/utils/SelfPermit.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

import { ERC20 } from "solady/tokens/ERC20.sol";

pragma solidity >=0.8.20;
pragma solidity >=0.8.25;

/// @title Self Permit
/// @notice Functionality to call permit on any EIP-2612-compliant token for use in the route
Expand Down
2 changes: 1 addition & 1 deletion test/Registry.t.sol
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
pragma solidity ^0.8.25;

import "forge-std/Test.sol";
import "@/adapters/Adapter.sol";
Expand Down
2 changes: 1 addition & 1 deletion test/UnsETH.t.sol
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
pragma solidity >=0.8.20;
pragma solidity >=0.8.25;

import { Test, console } from "forge-std/Test.sol";
import { VmSafe } from "forge-std/Vm.sol";
Expand Down
2 changes: 1 addition & 1 deletion test/adapters/EETHAdapter.t.sol
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
pragma solidity >=0.8.20;
pragma solidity >=0.8.25;

import { Test, console } from "forge-std/Test.sol";
import { VmSafe } from "forge-std/Vm.sol";
Expand Down
2 changes: 1 addition & 1 deletion test/adapters/ETHxAdapter.t.sol
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
pragma solidity >=0.8.20;
pragma solidity >=0.8.25;

import { Test, console } from "forge-std/Test.sol";
import { VmSafe } from "forge-std/Vm.sol";
Expand Down
2 changes: 1 addition & 1 deletion test/adapters/METHAdapter.t.sol
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
pragma solidity >=0.8.20;
pragma solidity >=0.8.25;

import { Test, console } from "forge-std/Test.sol";
import { VmSafe } from "forge-std/Vm.sol";
Expand Down
2 changes: 1 addition & 1 deletion test/adapters/StETHAdapter.t.sol
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
pragma solidity >=0.8.20;
pragma solidity >=0.8.25;

import { Test, console } from "forge-std/Test.sol";
import { VmSafe } from "forge-std/Vm.sol";
Expand Down
2 changes: 1 addition & 1 deletion test/adapters/SwETHAdapter.t.sol
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
pragma solidity >=0.8.20;
pragma solidity >=0.8.25;

import { Test, console } from "forge-std/Test.sol";
import { VmSafe } from "forge-std/Vm.sol";
Expand Down
Loading

0 comments on commit 16d20af

Please sign in to comment.