Skip to main content
PulseScanner.io

Address

0x0ab0c9c78a63793e36cb861b8dceee8b4a65e2ce
Current Holdings
$0.00
TXs sent
0
First Active
2026-05-04
block 26,447,863
Last Active
today
block 27,562,292
Funded By
not identified

Net worth historyi

1,537 snapshots · to block 27,565,373coverage change 26 Augcoverage change 27 Augcoverage change 27 Augcoverage change 27 Augcoverage change 27 Aug
exact matchPMINTFeeRouterV5solc 0.8.19+commit.7dd6d404runtime exact · creation exact
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

import "https://raw.githubusercontent.com/OpenZeppelin/openzeppelin-contracts/v4.9.3/contracts/token/ERC20/IERC20.sol";
import "https://raw.githubusercontent.com/OpenZeppelin/openzeppelin-contracts/v4.9.3/contracts/token/ERC20/utils/SafeERC20.sol";
import "https://raw.githubusercontent.com/OpenZeppelin/openzeppelin-contracts/v4.9.3/contracts/security/ReentrancyGuard.sol";

interface IUniswapV2Router {
    function swapExactTokensForTokensSupportingFeeOnTransferTokens(
        uint256 amountIn, uint256 amountOutMin,
        address[] calldata path, address to, uint256 deadline
    ) external;
    function swapExactETHForTokensSupportingFeeOnTransferTokens(
        uint256 amountOutMin, address[] calldata path,
        address to, uint256 deadline
    ) external payable;
    function swapExactTokensForETHSupportingFeeOnTransferTokens(
        uint256 amountIn, uint256 amountOutMin,
        address[] calldata path, address to, uint256 deadline
    ) external;
    function getAmountsOut(uint256 amountIn, address[] calldata path)
        external view returns (uint256[] memory);
}

interface IUniswapV2Pair {
    function swap(uint256 amount0Out, uint256 amount1Out, address to, bytes calldata data) external;
    function getReserves() external view returns (uint112 r0, uint112 r1, uint32);
    function token0() external view returns (address);
    function token1() external view returns (address);
}

contract PMINTFeeRouterV5 is ReentrancyGuard {
    using SafeERC20 for IERC20;

    uint256 public constant FEE_BPS       = 30;
    uint256 public constant BPS_BASE      = 10_000;
    uint256 public constant MIN_BUYBURN   = 10 * 1e18; // 10 pDAI min before buyburn
    address public constant BURN_ADDR     = 0x000000000000000000000000000000000000dEaD;
    
    address public immutable pulseXRouter;
    address public immutable pmintToken;
    address public immutable pdai;
    address public immutable wpls;
    address public immutable plsPdaiPair;

    uint256 public pendingBuyback;

    event SwapExecuted(address indexed user, address tokenIn, address tokenOut, uint256 amountIn, uint256 feeAmount);
    event BuyBurnExecuted(uint256 pdaiSpent, uint256 pmintBurned);

    // Struct to bypass "Stack Too Deep" errors
    struct SwapCache {
        address tokenIn;
        address tokenOut;
        uint256 swapAmount;
        uint256 feeAmt;
        uint256 pdaiBalBefore;
    }

    constructor(
        address _router,
        address _pmintToken,
        address _pdai,
        address _wpls,
        address _plsPdaiPair
    ) {
        require(_router       != address(0));
        require(_pmintToken   != address(0));
        require(_pdai         != address(0));
        require(_wpls         != address(0));
        require(_plsPdaiPair  != address(0));
        pulseXRouter = _router;
        pmintToken   = _pmintToken;
        pdai         = _pdai;
        wpls         = _wpls;
        plsPdaiPair  = _plsPdaiPair;
    }

    // ─────────────────────────────────────────────────────────────────────────
    // TOKEN TO TOKEN 
    // ─────────────────────────────────────────────────────────────────────────
    function swapTokensForTokens(
        uint256 amountIn,
        uint256 amountOutMin,
        address[] calldata path,
        address to,
        uint256 deadline
    ) external nonReentrant {
        require(path.length >= 2, "Bad path");
        
        SwapCache memory v;
        v.tokenIn = path[0];
        v.tokenOut = path[path.length - 1];

        uint256 balBefore = IERC20(v.tokenIn).balanceOf(address(this));
        IERC20(v.tokenIn).safeTransferFrom(msg.sender, address(this), amountIn);
        v.swapAmount = IERC20(v.tokenIn).balanceOf(address(this)) - balBefore;

        if (v.tokenIn == pdai) {
            v.feeAmt = (v.swapAmount * FEE_BPS) / BPS_BASE;
            v.swapAmount -= v.feeAmt;
            _accumulateFee(v.feeAmt);
        }

        v.pdaiBalBefore = (v.tokenOut == pdai) ? IERC20(pdai).balanceOf(address(this)) : 0;

        _executeRouterSwap(v.swapAmount, amountOutMin, path, to, deadline, v.tokenOut == pdai);

        if (v.tokenOut == pdai) {
            uint256 pdaiOut = IERC20(pdai).balanceOf(address(this)) - v.pdaiBalBefore;
            if (pdaiOut > 0) {
                uint256 outDiv = (pdaiOut * FEE_BPS) / BPS_BASE;
                v.feeAmt += outDiv;
                _accumulateFee(outDiv);
                IERC20(pdai).safeTransfer(to, pdaiOut - outDiv);
            }
        }

        emit SwapExecuted(msg.sender, v.tokenIn, v.tokenOut, amountIn, v.feeAmt);
    }

    // ─────────────────────────────────────────────────────────────────────────
    // INTERNAL HELPERS
    // ─────────────────────────────────────────────────────────────────────────
    function _executeRouterSwap(
        uint256 swapAmount,
        uint256 amountOutMin,
        address[] calldata path,
        address to,
        uint256 deadline,
        bool outIsPDAI
    ) internal {
        IERC20(path[0]).approve(pulseXRouter, swapAmount);
        IUniswapV2Router(pulseXRouter).swapExactTokensForTokensSupportingFeeOnTransferTokens(
            swapAmount,
            amountOutMin,
            path,
            outIsPDAI ? address(this) : to,
            deadline
        );
        IERC20(path[0]).approve(pulseXRouter, 0);
    }

    function _getReserves(address tokenIn) internal view returns (uint256 rIn, uint256 rOut) {
        (uint112 r0, uint112 r1,) = IUniswapV2Pair(plsPdaiPair).getReserves();
        address t0 = IUniswapV2Pair(plsPdaiPair).token0();
        (rIn, rOut) = t0 == tokenIn ? (uint256(r0), uint256(r1)) : (uint256(r1), uint256(r0));
    }

    function _getAmountOut(uint256 amtIn, uint256 rIn, uint256 rOut) internal pure returns (uint256) {
        uint256 amtInWithFee = amtIn * 9971;
        return (amtInWithFee * rOut) / (rIn * 10000 + amtInWithFee);
    }

    // ─────────────────────────────────────────────────────────────────────────
    // PLS TO pDAI 
    // ─────────────────────────────────────────────────────────────────────────
    function swapPLSForPDAI(
        uint256 amountOutMin,
        address to,
        uint256 deadline
    ) external payable nonReentrant {
        require(msg.value > 0, "No PLS");
        require(block.timestamp <= deadline, "Expired");

        (bool ok,) = wpls.call{value: msg.value}("");
        require(ok, "WPLS wrap failed");

        (uint256 rIn, uint256 rOut) = _getReserves(wpls);
        uint256 pdaiOut = _getAmountOut(msg.value, rIn, rOut);
        require(pdaiOut >= amountOutMin, "Slippage");

        bool wplsIsT0 = IUniswapV2Pair(plsPdaiPair).token0() == wpls;
        IERC20(wpls).safeTransfer(plsPdaiPair, msg.value);

        uint256 pdaiBalBefore = IERC20(pdai).balanceOf(address(this));
        IUniswapV2Pair(plsPdaiPair).swap(
            wplsIsT0 ? 0 : pdaiOut,
            wplsIsT0 ? pdaiOut : 0,
            address(this), bytes("")
        );

        uint256 received = IERC20(pdai).balanceOf(address(this)) - pdaiBalBefore;
        require(received > 0, "No pDAI received");
        
        uint256 feeAmt = (received * FEE_BPS) / BPS_BASE;
        _accumulateFee(feeAmt);
        IERC20(pdai).safeTransfer(to, received - feeAmt);

        emit SwapExecuted(msg.sender, address(0), pdai, msg.value, feeAmt);
    }

    // ─────────────────────────────────────────────────────────────────────────
    // pDAI TO PLS 
    // ─────────────────────────────────────────────────────────────────────────
    function swapPDAIForPLS(
        uint256 amountIn,
        uint256 amountOutMin,
        address to,
        uint256 deadline
    ) external nonReentrant {
        require(block.timestamp <= deadline, "Expired");
        _swapPDAIForPLSInternal(amountIn, amountOutMin, to);
    }

    function _swapPDAIForPLSInternal(
        uint256 amountIn,
        uint256 amountOutMin,
        address to
    ) internal {
        uint256 balBefore = IERC20(pdai).balanceOf(address(this));
        IERC20(pdai).safeTransferFrom(msg.sender, address(this), amountIn);
        uint256 received = IERC20(pdai).balanceOf(address(this)) - balBefore;

        uint256 feeAmt = (received * FEE_BPS) / BPS_BASE;
        uint256 swapAmount  = received - feeAmt;
        _accumulateFee(feeAmt);

        (uint256 rIn, uint256 rOut) = _getReserves(pdai);
        uint256 wplsOut = _getAmountOut(swapAmount, rIn, rOut);
        require(wplsOut >= amountOutMin, "Slippage");

        bool pdaiIsT0 = IUniswapV2Pair(plsPdaiPair).token0() == pdai;
        IERC20(pdai).safeTransfer(plsPdaiPair, swapAmount);
        IUniswapV2Pair(plsPdaiPair).swap(
            pdaiIsT0 ? 0 : wplsOut,
            pdaiIsT0 ? wplsOut : 0,
            address(this), bytes("")
        );

        _unwrapAndSendPLS(to);
        emit SwapExecuted(msg.sender, pdai, address(0), amountIn, feeAmt);
    }

    function _unwrapAndSendPLS(address to) internal {
        uint256 plsBalBefore = address(this).balance;
        
        uint256 wplsBalance  = IERC20(wpls).balanceOf(address(this));
        (bool ok,) = wpls.call(abi.encodeWithSignature("withdraw(uint256)", wplsBalance));
        require(ok, "WPLS unwrap failed");
        
        uint256 plsReceived = address(this).balance - plsBalBefore;
        (bool sent,) = to.call{value: plsReceived}("");
        require(sent, "PLS send failed");
    }

    // ─────────────────────────────────────────────────────────────────────────
    // GENERIC TOKEN TO TOKEN VIA MULTI HOP
    // ─────────────────────────────────────────────────────────────────────────
    function swapTokensMultiHop(
        uint256 amountIn,
        uint256 amountOutMin,
        address[] calldata path,
        address to,
        uint256 deadline
    ) external nonReentrant {
        require(path.length >= 3, "Need multi hop path");

        SwapCache memory v;
        v.tokenIn = path[0];
        v.tokenOut = path[path.length - 1];

        uint256 balBefore = IERC20(v.tokenIn).balanceOf(address(this));
        IERC20(v.tokenIn).safeTransferFrom(msg.sender, address(this), amountIn);
        v.swapAmount = IERC20(v.tokenIn).balanceOf(address(this)) - balBefore;

        if (v.tokenIn == pdai) {
            v.feeAmt = (v.swapAmount * FEE_BPS) / BPS_BASE;
            v.swapAmount -= v.feeAmt;
            _accumulateFee(v.feeAmt);
        }

        v.pdaiBalBefore = (v.tokenOut == pdai) ? IERC20(pdai).balanceOf(address(this)) : 0;

        _executeRouterSwap(v.swapAmount, amountOutMin, path, to, deadline, v.tokenOut == pdai);

        if (v.tokenOut == pdai) {
            uint256 pdaiOut = IERC20(pdai).balanceOf(address(this)) - v.pdaiBalBefore;
            if (pdaiOut > 0) {
                uint256 outDiv = (pdaiOut * FEE_BPS) / BPS_BASE;
                v.feeAmt += outDiv;
                _accumulateFee(outDiv);
                IERC20(pdai).safeTransfer(to, pdaiOut - outDiv);
            }
        }

        emit SwapExecuted(msg.sender, v.tokenIn, v.tokenOut, amountIn, v.feeAmt);
    }

    // ─────────────────────────────────────────────────────────────────────────
    // INTERNAL & BUYBACK LOGIC
    // ─────────────────────────────────────────────────────────────────────────
    function _accumulateFee(uint256 amount) internal {
        if (amount > 0) {
            pendingBuyback += amount;
        }
    }

    function flushBuyback() external nonReentrant {
        uint256 amount = pendingBuyback;
        require(amount >= MIN_BUYBURN, "Below minimum");
        pendingBuyback = 0;

        address[] memory path = new address[](2);
        path[0] = pdai;
        path[1] = pmintToken;

        uint256 pmintBefore = IERC20(pmintToken).balanceOf(address(this));

        IERC20(pdai).approve(pulseXRouter, amount);
        IUniswapV2Router(pulseXRouter).swapExactTokensForTokensSupportingFeeOnTransferTokens(
            amount,
            0,
            path,
            address(this),
            block.timestamp
        );
        IERC20(pdai).approve(pulseXRouter, 0);

        uint256 pmintReceived = IERC20(pmintToken).balanceOf(address(this)) - pmintBefore;
        if (pmintReceived > 0) {
            IERC20(pmintToken).safeTransfer(BURN_ADDR, pmintReceived);
        }
        
        emit BuyBurnExecuted(amount, pmintReceived);
    }

    receive() external payable {}
}