|
| 1 | +// SPDX-License-Identifier: BSD-3-Clause |
| 2 | +pragma solidity 0.8.25; |
| 3 | + |
| 4 | +import { SafeERC20Upgradeable, IERC20Upgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol"; |
| 5 | +import { AddressUpgradeable } from "@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol"; |
| 6 | + |
| 7 | +import { IWBNB } from "../Interfaces.sol"; |
| 8 | + |
| 9 | +contract SwapHelper { |
| 10 | + using SafeERC20Upgradeable for IERC20Upgradeable; |
| 11 | + using AddressUpgradeable for address; |
| 12 | + |
| 13 | + uint256 internal constant REENTRANCY_LOCK_UNLOCKED = 1; |
| 14 | + uint256 internal constant REENTRANCY_LOCK_LOCKED = 2; |
| 15 | + |
| 16 | + /// @notice Wrapped native asset |
| 17 | + IWBNB public immutable WRAPPED_NATIVE; |
| 18 | + |
| 19 | + /// @dev Reentrancy lock to prevent reentrancy attacks |
| 20 | + uint256 private reentrancyLock; |
| 21 | + |
| 22 | + /// @notice Error thrown when reentrancy is detected |
| 23 | + error Reentrancy(); |
| 24 | + |
| 25 | + /// @notice In the locked state, allow contract to call itself, but block all external calls |
| 26 | + modifier externalLock() { |
| 27 | + bool isExternal = msg.sender != address(this); |
| 28 | + |
| 29 | + if (isExternal) { |
| 30 | + if (reentrancyLock == REENTRANCY_LOCK_LOCKED) revert Reentrancy(); |
| 31 | + reentrancyLock = REENTRANCY_LOCK_LOCKED; |
| 32 | + } |
| 33 | + |
| 34 | + _; |
| 35 | + |
| 36 | + if (isExternal) reentrancyLock = REENTRANCY_LOCK_UNLOCKED; |
| 37 | + } |
| 38 | + |
| 39 | + constructor(address wrappedNative_) { |
| 40 | + WRAPPED_NATIVE = IWBNB(wrappedNative_); |
| 41 | + } |
| 42 | + |
| 43 | + /// @notice Multicall function to execute multiple calls in a single transaction |
| 44 | + /// @param data Array of calldata to execute |
| 45 | + function multicall(bytes[] calldata data) external payable { |
| 46 | + for (uint256 i = 0; i < data.length; i++) { |
| 47 | + address(this).functionCall(data[i]); |
| 48 | + } |
| 49 | + } |
| 50 | + |
| 51 | + /// @notice Generic call function to execute a call to an arbitrary address |
| 52 | + /// @param target Address to call |
| 53 | + /// @param data Calldata to execute |
| 54 | + function genericCall(address target, bytes calldata data) external externalLock { |
| 55 | + target.functionCall(data); |
| 56 | + } |
| 57 | + |
| 58 | + /// @notice Wraps native asset into an ERC-20 token |
| 59 | + /// @param amount Amount of native asset to wrap |
| 60 | + function wrap(uint256 amount) external externalLock { |
| 61 | + WRAPPED_NATIVE.deposit{ value: amount }(); |
| 62 | + } |
| 63 | + |
| 64 | + /// @notice Sweeps an ERC-20 token to a specified address |
| 65 | + /// @param token ERC-20 token to sweep |
| 66 | + /// @param to Address to send the token to |
| 67 | + function sweep(IERC20Upgradeable token, address to) external externalLock { |
| 68 | + token.safeTransfer(to, token.balanceOf(address(this))); |
| 69 | + } |
| 70 | + |
| 71 | + /// @notice Approves the maximum amount of an ERC-20 token to a specified address |
| 72 | + /// @param token ERC-20 token to approve |
| 73 | + /// @param spender Address to approve the token to |
| 74 | + function approveMax(IERC20Upgradeable token, address spender) external externalLock { |
| 75 | + token.forceApprove(spender, 0); |
| 76 | + token.forceApprove(spender, type(uint256).max); |
| 77 | + } |
| 78 | +} |
0 commit comments