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

288 lines
11 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;
// ── Адреса (TRON base58 → hex) ──
// SunSwap V2 Router: TKzxdSv2FZKQrEqkKVgp5DcwEXBEKMg2Ax → 0x6e0617948fe030a7e4970f8389d4ad295f249b7e
// USDT (TRC-20): TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t → 0xa614f803b6fd780986a42c78ec9c7f77e6ded13c
// WTRX: TNUC9Qb1rRpS5CbWLmNMxXBjyFoydXjWFR → 0x891cdb91d149f23b1a45d9c5ca78a88d0cb44c18
// Fee Recipient: TYTfrem65362TFyQSARTheeYza1GQA37Ug → 0xf6b4D4E650Fc67982894f37ba97Ab2496781ddb6
interface ITRC20 {
function balanceOf(address account) external view returns (uint256);
function transfer(address to, uint256 amount) external returns (bool);
function transferFrom(address from, address to, uint256 amount) external returns (bool);
function approve(address spender, uint256 amount) external returns (bool);
}
/// @title FeeSwapRouter_TRX
/// @notice Обёртка для SunSwap V2 на TRON. Берёт 0.7% комиссию
/// с каждого свапа и бриджа, отправляет на fee wallet.
/// Остальные 99.3% + calldata идут на SunSwap как есть.
/// @dev Generic calldata forwarder — работает с любым роутером.
/// На TRON нет OpenZeppelin, поэтому ReentrancyGuard и Ownable
/// реализованы вручную.
contract FeeSwapRouter_TRX {
// ── Reentrancy Guard ──
uint256 private constant NOT_ENTERED = 1;
uint256 private constant ENTERED = 2;
uint256 private _status = NOT_ENTERED;
modifier nonReentrant() {
require(_status != ENTERED, "ReentrancyGuard: reentrant call");
_status = ENTERED;
_;
_status = NOT_ENTERED;
}
// ── Ownable ──
address private _owner;
bool private _paused;
modifier onlyOwner() {
require(msg.sender == _owner, "Ownable: caller is not the owner");
_;
}
modifier whenNotPaused() {
require(!_paused, "Pausable: paused");
_;
}
function owner() public view returns (address) { return _owner; }
function paused() public view returns (bool) { return _paused; }
// ── Захардкоженные адреса для TRON ──
/// @notice SunSwap V2 Smart Router на TRON
/// TRON: TKzxdSv2FZKQrEqkKVgp5DcwEXBEKMg2Ax
address public constant SUNSWAP_ROUTER = 0x6E0617948FE030a7E4970f8389d4Ad295f249B7e;
/// @notice Кошелёк, получающий 0.7% комиссию
/// TRON: TYTfrem65362TFyQSARTheeYza1GQA37Ug
address public constant FEE_RECIPIENT = 0xf6b4D4E650Fc67982894f37ba97Ab2496781ddb6;
/// @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 BridgeWithFeeNative(
address indexed user,
uint256 totalIn,
uint256 feeAmount
);
event BridgeWithFeeToken(
address indexed user,
address indexed tokenIn,
uint256 totalIn,
uint256 feeAmount
);
event EmergencyWithdrawNative(address indexed to, uint256 amount);
event EmergencyWithdrawToken(address indexed token, address indexed to, uint256 amount);
event Paused(address account);
event Unpaused(address account);
// ── Constructor ──
constructor() {
_owner = msg.sender;
_paused = false;
}
// ═══════════════════════════════════════════════
// Swap: TRX → Token
// Пользователь отправляет TRX. Контракт берёт 0.7%,
// остальное + calldata пересылает на SunSwap Router.
// ═══════════════════════════════════════════════
/// @notice Свап TRX → токен через SunSwap с 0.7% комиссией.
/// @param routerCalldata Calldata для SunSwap Router
function swapNativeWithFee(
bytes calldata routerCalldata
) external payable nonReentrant whenNotPaused {
require(msg.value > 0, "Zero value");
uint256 feeAmount = (msg.value * FEE_BPS) / BPS_DENOMINATOR;
uint256 swapAmount = msg.value - feeAmount;
// 0.7% → fee wallet
if (feeAmount > 0) {
(bool feeSent, ) = FEE_RECIPIENT.call{value: feeAmount}("");
require(feeSent, "Fee transfer failed");
}
// 99.3% + calldata → SunSwap Router
(bool success, ) = SUNSWAP_ROUTER.call{value: swapAmount}(routerCalldata);
require(success, "Swap failed");
emit SwapWithFeeNative(msg.sender, msg.value, feeAmount, swapAmount, SUNSWAP_ROUTER);
}
// ═══════════════════════════════════════════════
// Swap: Token → TRX
// Пользователь approve-ит токены на этот контракт.
// Контракт берёт 0.7% комиссию в токенах, approve-ит
// 99.3% на SunSwap, и пересылает calldata.
// ═══════════════════════════════════════════════
/// @notice Свап токен → TRX через SunSwap с 0.7% комиссией.
/// @param tokenIn Адрес входного TRC-20 токена (USDT и т.д.)
/// @param amountIn Полная сумма токенов (включая 0.7% комиссию)
/// @param routerCalldata Calldata для SunSwap Router
function swapTokenWithFee(
address tokenIn,
uint256 amountIn,
bytes calldata routerCalldata
) external nonReentrant whenNotPaused {
require(amountIn > 0, "Zero amount");
require(tokenIn != address(0), "Zero address");
// Забираем токены у пользователя
require(
ITRC20(tokenIn).transferFrom(msg.sender, address(this), amountIn),
"TransferFrom failed"
);
uint256 feeAmount = (amountIn * FEE_BPS) / BPS_DENOMINATOR;
uint256 swapAmount = amountIn - feeAmount;
// 0.7% комиссию → fee wallet
if (feeAmount > 0) {
require(
ITRC20(tokenIn).transfer(FEE_RECIPIENT, feeAmount),
"Fee transfer failed"
);
}
// Approve 99.3% на SunSwap Router
ITRC20(tokenIn).approve(SUNSWAP_ROUTER, swapAmount);
// Calldata → SunSwap Router
(bool success, ) = SUNSWAP_ROUTER.call(routerCalldata);
require(success, "Swap failed");
emit SwapWithFeeToken(msg.sender, tokenIn, amountIn, feeAmount, swapAmount, SUNSWAP_ROUTER);
}
// ═══════════════════════════════════════════════
// Bridge Fee: TRX (native)
// Берёт 0.7% с TRX перед бриджем.
// Остаток возвращается пользователю для бриджа.
// ═══════════════════════════════════════════════
/// @notice Взять 0.7% комиссию с TRX перед бриджем.
/// Остаток возвращается msg.sender.
function bridgeNativeFee() external payable nonReentrant whenNotPaused {
require(msg.value > 0, "Zero value");
uint256 feeAmount = (msg.value * FEE_BPS) / BPS_DENOMINATOR;
uint256 remaining = msg.value - feeAmount;
// 0.7% → fee wallet
if (feeAmount > 0) {
(bool feeSent, ) = FEE_RECIPIENT.call{value: feeAmount}("");
require(feeSent, "Fee transfer failed");
}
// Остаток → обратно пользователю
if (remaining > 0) {
(bool sent, ) = msg.sender.call{value: remaining}("");
require(sent, "Return transfer failed");
}
emit BridgeWithFeeNative(msg.sender, msg.value, feeAmount);
}
/// @notice Взять 0.7% комиссию с TRC-20 токена перед бриджем.
/// Остаток возвращается msg.sender.
/// @param tokenIn Адрес TRC-20 токена
/// @param amountIn Полная сумма (включая 0.7%)
function bridgeTokenFee(
address tokenIn,
uint256 amountIn
) external nonReentrant whenNotPaused {
require(amountIn > 0, "Zero amount");
require(tokenIn != address(0), "Zero address");
require(
ITRC20(tokenIn).transferFrom(msg.sender, address(this), amountIn),
"TransferFrom failed"
);
uint256 feeAmount = (amountIn * FEE_BPS) / BPS_DENOMINATOR;
uint256 remaining = amountIn - feeAmount;
// 0.7% → fee wallet
if (feeAmount > 0) {
require(
ITRC20(tokenIn).transfer(FEE_RECIPIENT, feeAmount),
"Fee transfer failed"
);
}
// Остаток → обратно пользователю
if (remaining > 0) {
require(
ITRC20(tokenIn).transfer(msg.sender, remaining),
"Return transfer failed"
);
}
emit BridgeWithFeeToken(msg.sender, tokenIn, amountIn, feeAmount);
}
// ── Admin: Emergency ──
function pause() external onlyOwner {
_paused = true;
emit Paused(msg.sender);
}
function unpause() external onlyOwner {
_paused = false;
emit Unpaused(msg.sender);
}
function emergencyWithdrawNative() external onlyOwner {
uint256 balance = address(this).balance;
require(balance > 0, "Zero balance");
(bool sent, ) = _owner.call{value: balance}("");
require(sent, "Transfer failed");
emit EmergencyWithdrawNative(_owner, balance);
}
function emergencyWithdrawToken(address token) external onlyOwner {
require(token != address(0), "Zero address");
uint256 balance = ITRC20(token).balanceOf(address(this));
require(balance > 0, "Zero balance");
require(ITRC20(token).transfer(_owner, balance), "Transfer failed");
emit EmergencyWithdrawToken(token, _owner, balance);
}
/// @notice Принимаем TRX (рефанды, возвраты)
receive() external payable {}
}