Skip to main content
PulseScanner.io

Address

0x4e734f9c451355eff01419c97f8d4ccfce4ff978
Current Holdings
$0.3853
TXs sent
not counted
First Active
2025-10-23
block 24,835,612
Last Active
330 days ago
block 24,835,835
Funded By
0x9f5b…a39a

Net worth historyi

4 snapshots · to block 27,505,720coverage change 26 Augcoverage change 27 Augcoverage change 27 Augcoverage change 27 Augcoverage change 27 Aug
exact matchpSunDAIVaultASA_AutonomousUXFullFinalsolc 0.8.20+commit.a1b79de6runtime exact · creation exact
// SPDX-License-Identifier: MIT
pragma solidity 0.8.20;

/**
 * ╔══════════════════════════════════════════════════════════════════╗
 * ║           pSunDAIVault ASA Autonomous Stable Asset               ║
 * ║                                                                  ║
 * ║   Immutable Stable Vault — PulseChain Autonomous ELite Edition   ║
 * ║   - Powered by pSunDAIoraclePLSX_AutonomousUXFinal               ║
 * ║   - Self-refreshing oracle, always-on, fully autonomous          ║
 * ║   - One-click UX: Deposit+Mint, Repay+Withdraw, Auto-Repair      ║
 * ║   - Built for perfect UX and safety — no keys, no admin          ║
 * ║                                                                  ║
 * ║   Dev:     ELITE TEAM6                                           ║
 * ║   Website: https://www.sundaitoken.com                           ║
 * ║   Docs:    https://github.com/ELITEv5/AutonomousStableAssets     ║
 * ║   License: MIT | Autonomous | Immutable                          ║
 * ╚══════════════════════════════════════════════════════════════════╝
 */

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import "@openzeppelin/contracts/utils/math/Math.sol";

import "./pSunDAIV8.sol";
import "./pSunDAIoraclePLSX_AutonomousUXFinal.sol";

interface IWPLS {
    function deposit() external payable;
    function withdraw(uint256 wad) external;
}

contract pSunDAIVaultASA_AutonomousUXFullFinal is ReentrancyGuard {
    using SafeERC20 for IERC20;
    using Math for uint256;

    IERC20 public immutable wpls;
    pSunDAIV8 public immutable psundai;
    pSunDAIoraclePLSX_AutonomousUXFinal public immutable oracle;

    string public constant VERSION = "pSunDAIVaultASA_AutonomousUXFullFinal";
    uint256 public constant COLLATERAL_RATIO = 150;
    uint256 public constant LIQUIDATION_RATIO = 110;
    uint256 public constant MIN_ACTION_AMOUNT = 1e14;
    uint256 public constant WITHDRAW_COOLDOWN = 300;
    uint256 public constant MIN_PRICE = 1e12;
    uint256 public constant MAX_PRICE = 1000e18;
    uint256 public constant MIN_LIQUIDATION_BPS = 2000;
    uint256 public constant MIN_BONUS_BPS = 200;
    uint256 public constant MAX_BONUS_BPS = 500;
    uint256 public constant AUCTION_TIME = 3 hours;
    uint256 public constant LIQUIDATION_COOLDOWN = 600;
    uint256 public constant MIN_SYSTEM_HEALTH = 130; // 130% minimum system-wide collateral ratio


    struct Vault {
        uint256 collateral;
        uint256 debt;
        uint256 lastDepositTime;
        uint256 lastWithdrawTime;
        uint256 lastLiquidationTime;
        uint256 lastDebtAccrual;

    }

    mapping(address => Vault) public vaults;
    uint256 public lastSafePrice = 1e18;
    // ---- Global system tracking ----
uint256 public totalCollateral;
uint256 public totalDebt;

// ---- Stability fee configuration ----
uint256 public constant STABILITY_FEE_BPS = 50; // 0.5% annual fee
uint256 public constant SECONDS_PER_YEAR = 31_536_000; // 365 days

    /// @notice Returns true if oracle has been stale for > 24h
function _oracleStale() internal view returns (bool) {
    (, uint256 ts) = oracle.peekPriceView();
    return (block.timestamp - ts) > 24 hours;
}

/// @notice Accrues time-based stability fee on vault debt
function _accrueInterest(Vault storage v) internal {
    if (v.debt == 0) {
        v.lastDebtAccrual = block.timestamp;
        return;
    }
    uint256 elapsed = block.timestamp - v.lastDebtAccrual;
    if (elapsed == 0) return;
    uint256 fee = (v.debt * STABILITY_FEE_BPS * elapsed) / (SECONDS_PER_YEAR * 10_000);

// Ensure positive rounding (avoid underflowing small values)
if (fee == 0 && elapsed > 0) fee = 1;

v.debt += fee;
totalDebt += fee;
v.lastDebtAccrual = block.timestamp;

}





    event Deposit(address indexed user, uint256 amount, uint256 ratio);
    event Withdraw(address indexed user, uint256 amount, uint256 ratio);
    event Mint(address indexed user, uint256 amount, uint256 ratio);
    event Repay(address indexed user, uint256 amount, uint256 ratio);
    event Liquidation(address indexed user, uint256 repayAmount, address indexed liquidator, uint256 reward, uint256 ratio);
    event EmergencyWithdraw(address indexed user, uint256 amount);

    constructor(address _wpls, address _psundai, address _oracle) {
        require(_wpls != address(0) && _psundai != address(0) && _oracle != address(0), "Invalid address");
        wpls = IERC20(_wpls);
        psundai = pSunDAIV8(_psundai);
        oracle = pSunDAIoraclePLSX_AutonomousUXFinal(_oracle);
    }

    function _checkedPrice() internal returns (uint256) {
    (uint256 p,) = oracle.getPriceWithTimestamp();

    // Bootstrap: accept oracle if still at default $1
    if (lastSafePrice == 1e18 && p < 1e17) {
        lastSafePrice = p;
        return p;
    }

    if (p == 0 || p < (lastSafePrice * 90) / 100 || p > (lastSafePrice * 110) / 100) {
        p = lastSafePrice;
    }

    if (p < MIN_PRICE || p > MAX_PRICE) {
        p = lastSafePrice;
    }

    lastSafePrice = p;
    return p;
}



    function depositPLS() external payable nonReentrant {
        require(msg.value >= MIN_ACTION_AMOUNT, "Invalid amount");
        IWPLS(address(wpls)).deposit{value: msg.value}();
        _addCollateral(msg.sender, msg.value);
    }

    function deposit(uint256 amount) external nonReentrant {
        require(amount >= MIN_ACTION_AMOUNT, "Invalid amount");
        wpls.safeTransferFrom(msg.sender, address(this), amount);
        _addCollateral(msg.sender, amount);
    }

    function _addCollateral(address user, uint256 amount) internal {
        Vault storage v = vaults[user];
        v.collateral += amount;
        v.lastDepositTime = block.timestamp;
        totalCollateral += amount;
        emit Deposit(user, amount, _collateralRatio(user));
    }

    function depositAndAutoMintPLS() external payable nonReentrant {
        require(msg.value >= MIN_ACTION_AMOUNT, "Invalid amount");
        require(!_oracleStale(), "Mint paused: oracle stale >24h");
        IWPLS(address(wpls)).deposit{value: msg.value}();
        _addCollateral(msg.sender, msg.value);
        uint256 price = _checkedPrice();
        uint256 collateralValueUSD = (msg.value * price) / 1e18;
        uint256 mintAmount = (collateralValueUSD * 100) / 155;
        if (mintAmount == 0) return;
        Vault storage v = vaults[msg.sender];
        v.debt += mintAmount;
        totalDebt += mintAmount;
        psundai.mint(msg.sender, mintAmount);
        emit Mint(msg.sender, mintAmount, _collateralRatio(msg.sender));
    }

    function mint(uint256 amount) external nonReentrant {
        Vault storage v = vaults[msg.sender];
        require(amount > 0, "Invalid mint");
        require(!_oracleStale(), "Mint paused: oracle stale >24h");
        _accrueInterest(v);
if (v.debt == 0) v.lastDebtAccrual = block.timestamp;
require(systemHealth() >= MIN_SYSTEM_HEALTH, "Mint paused: system undercollateralized");
        uint256 price = _checkedPrice();
        require(_isSafePrice(v.collateral, v.debt + amount, price), "Not enough collateral");
        v.debt += amount;
        psundai.mint(msg.sender, amount);
        totalDebt += amount;
        emit Mint(msg.sender, amount, _collateralRatio(msg.sender));
    }

    function repay(uint256 amount) external nonReentrant {
    Vault storage v = vaults[msg.sender];
    require(amount > 0 && v.debt >= amount, "Invalid repay");
    _accrueInterest(v);

    psundai.burn(msg.sender, amount);
    v.debt -= amount;
    totalDebt -= amount;

    emit Repay(msg.sender, amount, _collateralRatio(msg.sender));
}


    function withdrawPLS(uint256 amount) external nonReentrant {
        Vault storage v = vaults[msg.sender];
        require(amount > 0 && v.collateral >= amount, "Invalid withdraw");
        require(block.timestamp > v.lastDepositTime + WITHDRAW_COOLDOWN, "Cooldown");
        v.collateral -= amount;
        totalCollateral -= amount;
        require(_isSafe(v.collateral, v.debt), "Unsafe");
        v.lastWithdrawTime = block.timestamp;
        IWPLS(address(wpls)).withdraw(amount);
        payable(msg.sender).transfer(amount);
        emit Withdraw(msg.sender, amount, _collateralRatio(msg.sender));
    }

    function withdrawWPLS(uint256 amount) external nonReentrant {
        Vault storage v = vaults[msg.sender];
        require(amount > 0 && v.collateral >= amount, "Invalid withdraw");
        require(block.timestamp > v.lastDepositTime + WITHDRAW_COOLDOWN, "Cooldown");
        v.collateral -= amount;
        require(_isSafe(v.collateral, v.debt), "Unsafe");
        v.lastWithdrawTime = block.timestamp;
        wpls.safeTransfer(msg.sender, amount);
        emit Withdraw(msg.sender, amount, _collateralRatio(msg.sender));
    }

    function repayAndAutoWithdraw(uint256 repayAmount) external nonReentrant {
    Vault storage v = vaults[msg.sender];
    _accrueInterest(v);
    require(repayAmount > 0 && v.debt >= repayAmount, "Invalid repay");

    // Burn user’s pSunDAI
    psundai.burn(msg.sender, repayAmount);

    // Update vault and system debt accounting
    v.debt -= repayAmount;
    totalDebt -= repayAmount;

    // --- Dust forgiveness ---
    // If residual debt is less than 1e12 (≈ 0.000000000001 pSunDAI), treat as fully repaid
    if (v.debt <= 1e12) {
        totalDebt -= v.debt; // remove the tiny residual debt from system total
        v.debt = 0;
    }

    uint256 price = _checkedPrice();

    // --- Full repayment case ---
    if (v.debt == 0) {
        uint256 amt = v.collateral;
        totalCollateral -= amt;

        // Reset vault cleanly for gas refund
        delete vaults[msg.sender];

        IWPLS(address(wpls)).withdraw(amt);
        payable(msg.sender).transfer(amt);

        emit EmergencyWithdraw(msg.sender, amt);
        return;
    }

    // --- Partial repayment case ---
    uint256 requiredCollateral = (v.debt * COLLATERAL_RATIO * 1e18) / (price * 100);
    if (v.collateral > requiredCollateral) {
        uint256 withdrawable = v.collateral - requiredCollateral;
        v.collateral = requiredCollateral;
        totalCollateral -= withdrawable;

        IWPLS(address(wpls)).withdraw(withdrawable);
        payable(msg.sender).transfer(withdrawable);

        emit Withdraw(msg.sender, withdrawable, _collateralRatio(msg.sender));
    }

    emit Repay(msg.sender, repayAmount, _collateralRatio(msg.sender));
}




    


    function repayToHealth(address user) external view returns (uint256 repayNeeded) {
        Vault storage v = vaults[user];
        if (v.debt == 0) return 0;
        (uint256 p, uint256 ts) = oracle.peekPriceView();
        uint256 price = (block.timestamp - ts > 300 || p == 0) ? lastSafePrice : p;
        uint256 requiredDebt = (v.collateral * price * 100) / (COLLATERAL_RATIO * 1e18);
        if (v.debt > requiredDebt) repayNeeded = v.debt - requiredDebt;
    }

    function autoRepayToHealth() external nonReentrant {
        Vault storage v = vaults[msg.sender];
        if (v.debt == 0) return;
        uint256 price = _checkedPrice();
        uint256 requiredDebt = (v.collateral * price * 100) / (COLLATERAL_RATIO * 1e18);
        if (v.debt > requiredDebt) {
            uint256 repayAmt = v.debt - requiredDebt;
            psundai.burn(msg.sender, repayAmt);
            v.debt -= repayAmt;
            emit Repay(msg.sender, repayAmt, _collateralRatio(msg.sender));
        }
    }

    function liquidate(address user, uint256 repayAmount) external nonReentrant {
    Vault storage v = vaults[user];
    _accrueInterest(v);

    // --- Preconditions ---
    require(!_isSafe(v.collateral, v.debt), "Vault safe");
    require(v.debt > 0, "No debt");
    require(repayAmount > 0 && repayAmount <= v.debt, "Invalid repay");
    require(repayAmount * 10000 >= v.debt * MIN_LIQUIDATION_BPS, "Below min repay");
    require(block.timestamp > v.lastLiquidationTime + LIQUIDATION_COOLDOWN, "Cooldown");
    require(!_oracleStale(), "Liquidations paused: oracle stale >24h");

    // --- Price + Collateral math ---
    uint256 price = _checkedPrice();
    uint256 baseCollateral = Math.mulDiv(repayAmount, 1e18, price);

    // Bonus dynamically increases over time since last withdrawal
    uint256 timeElapsed = block.timestamp - v.lastWithdrawTime;
    if (timeElapsed > AUCTION_TIME) timeElapsed = AUCTION_TIME;

    uint256 bonusBps = MIN_BONUS_BPS + ((MAX_BONUS_BPS - MIN_BONUS_BPS) * timeElapsed / AUCTION_TIME);
    uint256 bonus = (baseCollateral * bonusBps) / 10000;
    uint256 totalReward = baseCollateral + bonus;

    // Cap reward to vault’s available collateral
    if (totalReward > v.collateral) totalReward = v.collateral;

    // --- Apply state changes ---
    psundai.burn(msg.sender, repayAmount);
    v.debt -= repayAmount;
    totalDebt -= repayAmount;

    v.collateral -= totalReward;
    totalCollateral -= totalReward;

    v.lastLiquidationTime = block.timestamp;

    // --- Transfer liquidator reward ---
    IWPLS(address(wpls)).withdraw(totalReward);
    payable(msg.sender).transfer(totalReward);

    emit Liquidation(user, repayAmount, msg.sender, totalReward, _collateralRatio(user));
}


    function canLiquidate(address user) public view returns (bool) {
        Vault storage v = vaults[user];
        if (v.debt == 0) return false;
        (uint256 p,) = oracle.peekPriceView();
        uint256 price = p > 0 ? p : lastSafePrice;
        uint256 ratio = (v.collateral * price * 100) / (v.debt * 1e18);
        return ratio < LIQUIDATION_RATIO;
    }

    function liquidationInfo(address user) external view returns (uint256 debt, uint256 minRepay, uint256 reward, uint256 bonusBps) {
        Vault storage v = vaults[user];
        if (v.debt == 0) return (0, 0, 0, 0);
        (uint256 p, uint256 ts) = oracle.peekPriceView();
        uint256 price = (block.timestamp - ts > 300 || p == 0) ? lastSafePrice : p;
        uint256 minRepayAmt = (v.debt * MIN_LIQUIDATION_BPS) / 10000;
        uint256 baseCollateral = Math.mulDiv(minRepayAmt, 1e18, price);
        uint256 timeElapsed = block.timestamp - v.lastWithdrawTime;
        if (timeElapsed > AUCTION_TIME) timeElapsed = AUCTION_TIME;
        bonusBps = MIN_BONUS_BPS + ((MAX_BONUS_BPS - MIN_BONUS_BPS) * timeElapsed / AUCTION_TIME);
        uint256 bonus = (baseCollateral * bonusBps) / 10000;
        reward = baseCollateral + bonus;
        return (v.debt, minRepayAmt, reward, bonusBps);
    }

    function systemStatus(address user) external view returns (bool oracleHealthy, uint256 price, uint256 ratio, uint256 mintable, uint256 collateralUSD) {
        Vault storage v = vaults[user];
        (uint256 p, uint256 ts) = oracle.peekPriceView();
        price = (block.timestamp - ts > 300 || p == 0) ? lastSafePrice : p;
        oracleHealthy = (block.timestamp - ts <= 600);
        ratio = v.debt == 0 ? type(uint256).max : (v.collateral * price * 100) / (v.debt * 1e18);
        uint256 collateralValueUSD = (v.collateral * price) / 1e18;
        uint256 safeDebtLimit = (collateralValueUSD * 100) / COLLATERAL_RATIO;
        mintable = safeDebtLimit > v.debt ? safeDebtLimit - v.debt : 0;
        collateralUSD = collateralValueUSD;
    }
    /// @notice Returns overall system collateralization ratio
function systemHealth() public view returns (uint256 systemRatio) {
    if (totalDebt == 0) return type(uint256).max;
    (uint256 p,) = oracle.peekPriceView();
    uint256 price = p > 0 ? p : lastSafePrice;
    systemRatio = (totalCollateral * price * 100) / (totalDebt * 1e18);
}


    function _collateralRatio(address user) internal view returns (uint256) {
        Vault storage v = vaults[user];
        if (v.debt == 0) return type(uint256).max;
        uint256 price = lastSafePrice > 0 ? lastSafePrice : _peekSafePrice();
        return (v.collateral * price * 100) / (v.debt * 1e18);
    }

    function _isSafe(uint256 col, uint256 debt) internal returns (bool) {
        if (debt == 0) return true;
        uint256 price = _checkedPrice();
        return col * price * 100 >= debt * COLLATERAL_RATIO * 1e18;
    }

    function _isSafePrice(uint256 col, uint256 debt, uint256 price) internal pure returns (bool) {
        if (debt == 0) return true;
        return col * price * 100 >= debt * COLLATERAL_RATIO * 1e18;
    }

    function _peekSafePrice() internal view returns (uint256) {
        (uint256 p,) = oracle.peekPriceView();
        return p > 0 ? p : lastSafePrice;
    }
        /**
     * @notice Emergency unlock if oracle fails or vault is idle too long
     * @dev Only works for fully repaid vaults after 30 days of inactivity
     */
    function emergencyUnlock() external nonReentrant {
        Vault storage v = vaults[msg.sender];
        require(v.debt == 0, "Repay debt first");
        require(v.collateral > 0, "No collateral");
        require(block.timestamp > v.lastDepositTime + 30 days, "Vault recently active");

        uint256 amt = v.collateral;
        v.collateral = 0;

        IWPLS(address(wpls)).withdraw(amt);
        payable(msg.sender).transfer(amt);

        emit EmergencyWithdraw(msg.sender, amt);
    }

    /**
     * @notice Frontend helper: returns full vault + system info for a user
     * @param user The address of the vault owner
     * @return collateral Collateral amount (WPLS)
     * @return debt Current debt (pSunDAI)
     * @return collateralUSD USD value of collateral based on oracle
     * @return ratio Collateralization ratio (x100 = %)
     * @return mintable Safe amount of pSunDAI mintable right now
     * @return oracleHealthy True if oracle updated recently
     * @return price Latest oracle price (1e18 precision)
     * @return systemRatio Global system collateralization ratio (x100 = %)
     */
    function vaultInfo(address user)
        external
        view
        returns (
            uint256 collateral,
            uint256 debt,
            uint256 collateralUSD,
            uint256 ratio,
            uint256 mintable,
            bool oracleHealthy,
            uint256 price,
            uint256 systemRatio
        )
    {
        Vault storage v = vaults[user];
        collateral = v.collateral;
        debt = v.debt;

        (uint256 p, uint256 ts) = oracle.peekPriceView();
        price = (block.timestamp - ts > 300 || p == 0) ? lastSafePrice : p;
        oracleHealthy = (block.timestamp - ts <= 600);

        collateralUSD = (collateral * price) / 1e18;
        ratio = debt == 0 ? type(uint256).max : (collateral * price * 100) / (debt * 1e18);

        uint256 safeDebtLimit = (collateralUSD * 100) / COLLATERAL_RATIO;
        mintable = safeDebtLimit > debt ? safeDebtLimit - debt : 0;

        systemRatio = systemHealth();
    }

    receive() external payable {}
    fallback() external payable {}
}