Files
cryptowallet/contracts/FeeSwapRouter_BSC.sol
ZOMBIIIIIII a81e29807c add project
2026-04-08 14:11:27 +03:00

155 lines
5.9 KiB
Solidity
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import "@openzeppelin/contracts/utils/Pausable.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
/// @title FeeSwapRouter_BSC
/// @notice Обёртка для PancakeSwap V3 Smart Router на BSC. Берёт 0.7% комиссию
/// с каждого свапа и отправляет на fee wallet. Остальные 99.3% + calldata
/// идут на Smart Router как есть.
/// @dev Работает с любой версией PancakeSwap (V2/V3/Smart Router),
/// потому что просто пересылает calldata без разбора.
contract FeeSwapRouter_BSC is Ownable, ReentrancyGuard, Pausable {
using SafeERC20 for IERC20;
// ── Захардкоженные адреса для BSC ──
/// @notice PancakeSwap V3 Smart Router на BSC
address public constant SMART_ROUTER = 0x13f4EA83D0bd40E75C8222255bc855a974568Dd4;
/// @notice Permit2 на BSC (общий для PancakeSwap и Uniswap)
address public constant PERMIT2 = 0x000000000022D473030F116dDEE9F6B43aC78BA3;
/// @notice Кошелёк, получающий 0.7% комиссию
address public constant FEE_RECIPIENT = 0xeDEb157eF86A4ecd1242762f339c2Bd5a0822718;
/// @notice Комиссия 0.7% = 70 basis points (нельзя изменить)
uint16 public constant FEE_BPS = 70;
/// @notice Делитель для basis points
uint16 private constant BPS_DENOMINATOR = 10_000;
// ── Events ──
event SwapWithFeeNative(
address indexed user,
uint256 totalIn,
uint256 feeAmount,
uint256 swapAmount,
address indexed router
);
event SwapWithFeeToken(
address indexed user,
address indexed tokenIn,
uint256 totalIn,
uint256 feeAmount,
uint256 swapAmount,
address indexed router
);
event EmergencyWithdrawNative(address indexed to, uint256 amount);
event EmergencyWithdrawToken(address indexed token, address indexed to, uint256 amount);
// ── Errors ──
error InsufficientValue();
error NativeTransferFailed();
error SwapFailed();
error ZeroAmount();
error ZeroAddress();
error WrongChain();
// ── Constructor ──
constructor() Ownable(msg.sender) {
if (block.chainid != 56) revert WrongChain();
}
/// @notice Свап BNB → токен через PancakeSwap Smart Router с 1% комиссией.
/// @param routerCalldata Calldata сгенерированная PancakeSwap SDK.
/// Передаётся как есть.
function swapNativeWithFee(
bytes calldata routerCalldata
) external payable nonReentrant whenNotPaused {
if (msg.value == 0) revert InsufficientValue();
uint256 feeAmount = (msg.value * FEE_BPS) / BPS_DENOMINATOR;
uint256 swapAmount = msg.value - feeAmount;
// 1% → fee wallet
if (feeAmount > 0) {
(bool feeSent, ) = FEE_RECIPIENT.call{value: feeAmount}("");
if (!feeSent) revert NativeTransferFailed();
}
// 99% + calldata → PancakeSwap Smart Router
(bool success, ) = SMART_ROUTER.call{value: swapAmount}(routerCalldata);
if (!success) revert SwapFailed();
emit SwapWithFeeNative(msg.sender, msg.value, feeAmount, swapAmount, SMART_ROUTER);
}
/// @notice Свап токен → BNB через PancakeSwap Smart Router с 1% комиссией.
/// @param tokenIn Адрес входного токена (USDT, DOGE, и т.д.)
/// @param amountIn Полная сумма токенов (включая 1% комиссию)
/// @param routerCalldata Calldata для Smart Router
function swapTokenWithFee(
address tokenIn,
uint256 amountIn,
bytes calldata routerCalldata
) external nonReentrant whenNotPaused {
if (amountIn == 0) revert ZeroAmount();
if (tokenIn == address(0)) revert ZeroAddress();
// Забираем токены у пользователя
IERC20(tokenIn).safeTransferFrom(msg.sender, address(this), amountIn);
uint256 feeAmount = (amountIn * FEE_BPS) / BPS_DENOMINATOR;
uint256 swapAmount = amountIn - feeAmount;
// 1% комиссию → fee wallet
if (feeAmount > 0) {
IERC20(tokenIn).safeTransfer(FEE_RECIPIENT, feeAmount);
}
// Approve 99% на Permit2 (PancakeSwap Swap Proxy)
IERC20(tokenIn).forceApprove(PERMIT2, swapAmount);
// Calldata → Smart Router
(bool success, ) = SMART_ROUTER.call(routerCalldata);
if (!success) revert SwapFailed();
emit SwapWithFeeToken(msg.sender, tokenIn, amountIn, feeAmount, swapAmount, SMART_ROUTER);
}
// ── Admin: Emergency ──
function pause() external onlyOwner { _pause(); }
function unpause() external onlyOwner { _unpause(); }
function emergencyWithdrawNative() external onlyOwner {
uint256 balance = address(this).balance;
if (balance == 0) revert ZeroAmount();
(bool sent, ) = owner().call{value: balance}("");
if (!sent) revert NativeTransferFailed();
emit EmergencyWithdrawNative(owner(), balance);
}
function emergencyWithdrawToken(address token) external onlyOwner {
if (token == address(0)) revert ZeroAddress();
uint256 balance = IERC20(token).balanceOf(address(this));
if (balance == 0) revert ZeroAmount();
IERC20(token).safeTransfer(owner(), balance);
emit EmergencyWithdrawToken(token, owner(), balance);
}
/// @notice Принимаем BNB от PancakeSwap (рефанды, выходные средства)
receive() external payable {}
}