diff --git a/src/Helios.sol b/src/Helios.sol index 12880d0..b8dccb8 100644 --- a/src/Helios.sol +++ b/src/Helios.sol @@ -3,8 +3,8 @@ pragma solidity ^0.8.19; import {Math2} from "./libraries/Math2.sol"; import {ERC6909} from "./utils/ERC6909.sol"; -import {ReentrancyGuard} from "./utils/ReentrancyGuard.sol"; -import {SafeTransferLib} from "./libraries/SafeTransferLib.sol"; +import {ReentrancyGuard} from "lib/solady/src/utils/ReentrancyGuard.sol"; +import {SafeTransferLib} from "lib/solady/src/utils/SafeTransferLib.sol"; /// @notice Simple xyk-style exchange for ERC20 tokens. /// LP shares are tokenized using the ERC6909 interface. diff --git a/src/libraries/SafeTransferLib.sol b/src/libraries/SafeTransferLib.sol deleted file mode 100644 index 6d107dd..0000000 --- a/src/libraries/SafeTransferLib.sol +++ /dev/null @@ -1,374 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; - -/// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values. -/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/SafeTransferLib.sol) -/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SafeTransferLib.sol) -/// -/// @dev Note: -/// - For ETH transfers, please use `forceSafeTransferETH` for DoS protection. -/// - For ERC20s, this implementation won't check that a token has code, -/// responsibility is delegated to the caller. -library SafeTransferLib { - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* CUSTOM ERRORS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev The ETH transfer has failed. - error ETHTransferFailed(); - - /// @dev The ERC20 `transferFrom` has failed. - error TransferFromFailed(); - - /// @dev The ERC20 `transfer` has failed. - error TransferFailed(); - - /// @dev The ERC20 `approve` has failed. - error ApproveFailed(); - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* CONSTANTS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev Suggested gas stipend for contract receiving ETH that disallows any storage writes. - uint256 internal constant GAS_STIPEND_NO_STORAGE_WRITES = 2300; - - /// @dev Suggested gas stipend for contract receiving ETH to perform a few - /// storage reads and writes, but low enough to prevent griefing. - uint256 internal constant GAS_STIPEND_NO_GRIEF = 100000; - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* ETH OPERATIONS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - // If the ETH transfer MUST succeed with a reasonable gas budget, use the force variants. - // - // The regular variants: - // - Forwards all remaining gas to the target. - // - Reverts if the target reverts. - // - Reverts if the current contract has insufficient balance. - // - // The force variants: - // - Forwards with an optional gas stipend - // (defaults to `GAS_STIPEND_NO_GRIEF`, which is sufficient for most cases). - // - If the target reverts, or if the gas stipend is exhausted, - // creates a temporary contract to force send the ETH via `SELFDESTRUCT`. - // Future compatible with `SENDALL`: https://eips.ethereum.org/EIPS/eip-4758. - // - Reverts if the current contract has insufficient balance. - // - // The try variants: - // - Forwards with a mandatory gas stipend. - // - Instead of reverting, returns whether the transfer succeeded. - - /// @dev Sends `amount` (in wei) ETH to `to`. - function safeTransferETH(address to, uint256 amount) internal { - /// @solidity memory-safe-assembly - assembly { - if iszero(call(gas(), to, amount, codesize(), 0x00, codesize(), 0x00)) { - mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`. - revert(0x1c, 0x04) - } - } - } - - /// @dev Sends all the ETH in the current contract to `to`. - function safeTransferAllETH(address to) internal { - /// @solidity memory-safe-assembly - assembly { - // Transfer all the ETH and check if it succeeded or not. - if iszero(call(gas(), to, selfbalance(), codesize(), 0x00, codesize(), 0x00)) { - mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`. - revert(0x1c, 0x04) - } - } - } - - /// @dev Force sends `amount` (in wei) ETH to `to`, with a `gasStipend`. - function forceSafeTransferETH(address to, uint256 amount, uint256 gasStipend) internal { - /// @solidity memory-safe-assembly - assembly { - if lt(selfbalance(), amount) { - mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`. - revert(0x1c, 0x04) - } - if iszero(call(gasStipend, to, amount, codesize(), 0x00, codesize(), 0x00)) { - mstore(0x00, to) // Store the address in scratch space. - mstore8(0x0b, 0x73) // Opcode `PUSH20`. - mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`. - if iszero(create(amount, 0x0b, 0x16)) { revert(codesize(), codesize()) } // For gas estimation. - } - } - } - - /// @dev Force sends all the ETH in the current contract to `to`, with a `gasStipend`. - function forceSafeTransferAllETH(address to, uint256 gasStipend) internal { - /// @solidity memory-safe-assembly - assembly { - if iszero(call(gasStipend, to, selfbalance(), codesize(), 0x00, codesize(), 0x00)) { - mstore(0x00, to) // Store the address in scratch space. - mstore8(0x0b, 0x73) // Opcode `PUSH20`. - mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`. - if iszero(create(selfbalance(), 0x0b, 0x16)) { revert(codesize(), codesize()) } // For gas estimation. - } - } - } - - /// @dev Force sends `amount` (in wei) ETH to `to`, with `GAS_STIPEND_NO_GRIEF`. - function forceSafeTransferETH(address to, uint256 amount) internal { - /// @solidity memory-safe-assembly - assembly { - if lt(selfbalance(), amount) { - mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`. - revert(0x1c, 0x04) - } - if iszero(call(GAS_STIPEND_NO_GRIEF, to, amount, codesize(), 0x00, codesize(), 0x00)) { - mstore(0x00, to) // Store the address in scratch space. - mstore8(0x0b, 0x73) // Opcode `PUSH20`. - mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`. - if iszero(create(amount, 0x0b, 0x16)) { revert(codesize(), codesize()) } // For gas estimation. - } - } - } - - /// @dev Force sends all the ETH in the current contract to `to`, with `GAS_STIPEND_NO_GRIEF`. - function forceSafeTransferAllETH(address to) internal { - /// @solidity memory-safe-assembly - assembly { - // forgefmt: disable-next-item - if iszero(call(GAS_STIPEND_NO_GRIEF, to, selfbalance(), codesize(), 0x00, codesize(), 0x00)) { - mstore(0x00, to) // Store the address in scratch space. - mstore8(0x0b, 0x73) // Opcode `PUSH20`. - mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`. - if iszero(create(selfbalance(), 0x0b, 0x16)) { revert(codesize(), codesize()) } // For gas estimation. - } - } - } - - /// @dev Sends `amount` (in wei) ETH to `to`, with a `gasStipend`. - function trySafeTransferETH(address to, uint256 amount, uint256 gasStipend) - internal - returns (bool success) - { - /// @solidity memory-safe-assembly - assembly { - success := call(gasStipend, to, amount, codesize(), 0x00, codesize(), 0x00) - } - } - - /// @dev Sends all the ETH in the current contract to `to`, with a `gasStipend`. - function trySafeTransferAllETH(address to, uint256 gasStipend) - internal - returns (bool success) - { - /// @solidity memory-safe-assembly - assembly { - success := call(gasStipend, to, selfbalance(), codesize(), 0x00, codesize(), 0x00) - } - } - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* ERC20 OPERATIONS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev Sends `amount` of ERC20 `token` from `from` to `to`. - /// Reverts upon failure. - /// - /// The `from` account must have at least `amount` approved for - /// the current contract to manage. - function safeTransferFrom(address token, address from, address to, uint256 amount) internal { - /// @solidity memory-safe-assembly - assembly { - let m := mload(0x40) // Cache the free memory pointer. - mstore(0x60, amount) // Store the `amount` argument. - mstore(0x40, to) // Store the `to` argument. - mstore(0x2c, shl(96, from)) // Store the `from` argument. - mstore(0x0c, 0x23b872dd000000000000000000000000) // `transferFrom(address,address,uint256)`. - // Perform the transfer, reverting upon failure. - if iszero( - and( // The arguments of `and` are evaluated from right to left. - or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing. - call(gas(), token, 0, 0x1c, 0x64, 0x00, 0x20) - ) - ) { - mstore(0x00, 0x7939f424) // `TransferFromFailed()`. - revert(0x1c, 0x04) - } - mstore(0x60, 0) // Restore the zero slot to zero. - mstore(0x40, m) // Restore the free memory pointer. - } - } - - /// @dev Sends all of ERC20 `token` from `from` to `to`. - /// Reverts upon failure. - /// - /// The `from` account must have their entire balance approved for - /// the current contract to manage. - function safeTransferAllFrom(address token, address from, address to) - internal - returns (uint256 amount) - { - /// @solidity memory-safe-assembly - assembly { - let m := mload(0x40) // Cache the free memory pointer. - mstore(0x40, to) // Store the `to` argument. - mstore(0x2c, shl(96, from)) // Store the `from` argument. - mstore(0x0c, 0x70a08231000000000000000000000000) // `balanceOf(address)`. - // Read the balance, reverting upon failure. - if iszero( - and( // The arguments of `and` are evaluated from right to left. - gt(returndatasize(), 0x1f), // At least 32 bytes returned. - staticcall(gas(), token, 0x1c, 0x24, 0x60, 0x20) - ) - ) { - mstore(0x00, 0x7939f424) // `TransferFromFailed()`. - revert(0x1c, 0x04) - } - mstore(0x00, 0x23b872dd) // `transferFrom(address,address,uint256)`. - amount := mload(0x60) // The `amount` is already at 0x60. We'll need to return it. - // Perform the transfer, reverting upon failure. - if iszero( - and( // The arguments of `and` are evaluated from right to left. - or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing. - call(gas(), token, 0, 0x1c, 0x64, 0x00, 0x20) - ) - ) { - mstore(0x00, 0x7939f424) // `TransferFromFailed()`. - revert(0x1c, 0x04) - } - mstore(0x60, 0) // Restore the zero slot to zero. - mstore(0x40, m) // Restore the free memory pointer. - } - } - - /// @dev Sends `amount` of ERC20 `token` from the current contract to `to`. - /// Reverts upon failure. - function safeTransfer(address token, address to, uint256 amount) internal { - /// @solidity memory-safe-assembly - assembly { - mstore(0x14, to) // Store the `to` argument. - mstore(0x34, amount) // Store the `amount` argument. - mstore(0x00, 0xa9059cbb000000000000000000000000) // `transfer(address,uint256)`. - // Perform the transfer, reverting upon failure. - if iszero( - and( // The arguments of `and` are evaluated from right to left. - or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing. - call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20) - ) - ) { - mstore(0x00, 0x90b8ec18) // `TransferFailed()`. - revert(0x1c, 0x04) - } - mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten. - } - } - - /// @dev Sends all of ERC20 `token` from the current contract to `to`. - /// Reverts upon failure. - function safeTransferAll(address token, address to) internal returns (uint256 amount) { - /// @solidity memory-safe-assembly - assembly { - mstore(0x00, 0x70a08231) // Store the function selector of `balanceOf(address)`. - mstore(0x20, address()) // Store the address of the current contract. - // Read the balance, reverting upon failure. - if iszero( - and( // The arguments of `and` are evaluated from right to left. - gt(returndatasize(), 0x1f), // At least 32 bytes returned. - staticcall(gas(), token, 0x1c, 0x24, 0x34, 0x20) - ) - ) { - mstore(0x00, 0x90b8ec18) // `TransferFailed()`. - revert(0x1c, 0x04) - } - mstore(0x14, to) // Store the `to` argument. - amount := mload(0x34) // The `amount` is already at 0x34. We'll need to return it. - mstore(0x00, 0xa9059cbb000000000000000000000000) // `transfer(address,uint256)`. - // Perform the transfer, reverting upon failure. - if iszero( - and( // The arguments of `and` are evaluated from right to left. - or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing. - call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20) - ) - ) { - mstore(0x00, 0x90b8ec18) // `TransferFailed()`. - revert(0x1c, 0x04) - } - mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten. - } - } - - /// @dev Sets `amount` of ERC20 `token` for `to` to manage on behalf of the current contract. - /// Reverts upon failure. - function safeApprove(address token, address to, uint256 amount) internal { - /// @solidity memory-safe-assembly - assembly { - mstore(0x14, to) // Store the `to` argument. - mstore(0x34, amount) // Store the `amount` argument. - mstore(0x00, 0x095ea7b3000000000000000000000000) // `approve(address,uint256)`. - // Perform the approval, reverting upon failure. - if iszero( - and( // The arguments of `and` are evaluated from right to left. - or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing. - call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20) - ) - ) { - mstore(0x00, 0x3e3f8f73) // `ApproveFailed()`. - revert(0x1c, 0x04) - } - mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten. - } - } - - /// @dev Sets `amount` of ERC20 `token` for `to` to manage on behalf of the current contract. - /// If the initial attempt to approve fails, attempts to reset the approved amount to zero, - /// then retries the approval again (some tokens, e.g. USDT, requires this). - /// Reverts upon failure. - function safeApproveWithRetry(address token, address to, uint256 amount) internal { - /// @solidity memory-safe-assembly - assembly { - mstore(0x14, to) // Store the `to` argument. - mstore(0x34, amount) // Store the `amount` argument. - mstore(0x00, 0x095ea7b3000000000000000000000000) // `approve(address,uint256)`. - // Perform the approval, retrying upon failure. - if iszero( - and( // The arguments of `and` are evaluated from right to left. - or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing. - call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20) - ) - ) { - mstore(0x34, 0) // Store 0 for the `amount`. - mstore(0x00, 0x095ea7b3000000000000000000000000) // `approve(address,uint256)`. - pop(call(gas(), token, 0, 0x10, 0x44, codesize(), 0x00)) // Reset the approval. - mstore(0x34, amount) // Store back the original `amount`. - // Retry the approval, reverting upon failure. - if iszero( - and( - or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing. - call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20) - ) - ) { - mstore(0x00, 0x3e3f8f73) // `ApproveFailed()`. - revert(0x1c, 0x04) - } - } - mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten. - } - } - - /// @dev Returns the amount of ERC20 `token` owned by `account`. - /// Returns zero if the `token` does not exist. - function balanceOf(address token, address account) internal view returns (uint256 amount) { - /// @solidity memory-safe-assembly - assembly { - mstore(0x14, account) // Store the `account` argument. - mstore(0x00, 0x70a08231000000000000000000000000) // `balanceOf(address)`. - amount := - mul( - mload(0x20), - and( // The arguments of `and` are evaluated from right to left. - gt(returndatasize(), 0x1f), // At least 32 bytes returned. - staticcall(gas(), token, 0x10, 0x24, 0x20, 0x20) - ) - ) - } - } -} diff --git a/src/utils/ReentrancyGuard.sol b/src/utils/ReentrancyGuard.sol deleted file mode 100644 index 4b8b0df..0000000 --- a/src/utils/ReentrancyGuard.sol +++ /dev/null @@ -1,55 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; - -/// @notice Reentrancy guard mixin. -/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/ReentrancyGuard.sol) -abstract contract ReentrancyGuard { - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* CUSTOM ERRORS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev Unauthorized reentrant call. - error Reentrancy(); - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* STORAGE */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev Equivalent to: `uint72(bytes9(keccak256("_REENTRANCY_GUARD_SLOT")))`. - /// 9 bytes is large enough to avoid collisions with lower slots, - /// but not too large to result in excessive bytecode bloat. - uint256 private constant _REENTRANCY_GUARD_SLOT = 0x929eee149b4bd21268; - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* REENTRANCY GUARD */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev Guards a function from reentrancy. - modifier nonReentrant() virtual { - /// @solidity memory-safe-assembly - assembly { - if eq(sload(_REENTRANCY_GUARD_SLOT), 2) { - mstore(0x00, 0xab143c06) // `Reentrancy()`. - revert(0x1c, 0x04) - } - sstore(_REENTRANCY_GUARD_SLOT, 2) - } - _; - /// @solidity memory-safe-assembly - assembly { - sstore(_REENTRANCY_GUARD_SLOT, 1) - } - } - - /// @dev Guards a view function from read-only reentrancy. - modifier nonReadReentrant() virtual { - /// @solidity memory-safe-assembly - assembly { - if eq(sload(_REENTRANCY_GUARD_SLOT), 2) { - mstore(0x00, 0xab143c06) // `Reentrancy()`. - revert(0x1c, 0x04) - } - } - _; - } -}