Skip to main content
PulseScanner.io

Address

0x4e793cc5dedf6eae1bdd3240d6811a80aff358f3
Current Holdings
$0.00
TXs sent
not counted
First Active
2026-01-15
block 25,538,304
Last Active
243 days ago
block 25,538,639
Funded By
not identified

Net worth historyi

No net-worth snapshots recorded yet
exact matchSentinelMinterV4solc 0.8.20+commit.a1b79de6runtime exact · creation exact
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import "@openzeppelin/contracts@4.9.3/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts@4.9.3/security/ReentrancyGuard.sol";
import "@openzeppelin/contracts@4.9.3/access/Ownable.sol";

/// @title Sentinel Minter V4 - SALV Burn → SEN Mint Bridge (Fresh Deploy)
/// @notice Direct burn to burn address. Deploy new to point to SentinelV2.
/// @dev Identical logic to V3 but fresh instance so sentinel can be set.
contract SentinelMinterV4 is ReentrancyGuard, Ownable {
    IERC20 public immutable salvToken = IERC20(0x7fBc0BDf643341b42cf691f3Dbbb193e41c3Cf9d);
    address public immutable burnAddress = 0x0000000000000000000000000000000000000369;

    uint256 public immutable genesisTimestamp;
    uint256 public constant HALVING_INTERVAL = 365 days;
    uint256 public constant MAX_HALVINGS = 4;

    uint256 public constant INITIAL_MINT_NUMERATOR = 105;
    uint256 public constant MINT_DENOMINATOR = 100;

    uint256 public constant MIN_BURN_AMOUNT = 100 * 10**18;

    address public sentinel;

    event Minted(address indexed user, uint256 salvBurned, uint256 senMinted, uint256 currentPhase);

    modifier whenSentinelSet() {
        require(sentinel != address(0), "Sentinel address not set yet");
        _;
    }

    constructor() {
        genesisTimestamp = block.timestamp;
    }

    function mintFromSalv(uint256 salvAmount) external nonReentrant whenSentinelSet {
        require(salvAmount >= MIN_BURN_AMOUNT, "Burn amount below minimum (100 SALV)");
        require(salvToken.balanceOf(msg.sender) >= salvAmount, "Insufficient SALV balance");
        require(salvToken.allowance(msg.sender, address(this)) >= salvAmount, "Approve SALV first");

        salvToken.transferFrom(msg.sender, burnAddress, salvAmount);

        uint256 phase = getCurrentHalvingPhase();
        uint256 mintDivisor = (1 << phase);
        if (phase >= MAX_HALVINGS) mintDivisor = 1 << MAX_HALVINGS;

        uint256 senAmount = (salvAmount * INITIAL_MINT_NUMERATOR) / MINT_DENOMINATOR / mintDivisor;

        require(senAmount > 0, "Resulting SEN amount too small");

        (bool success, ) = sentinel.call(
            abi.encodeWithSignature("mint(address,uint256)", msg.sender, senAmount)
        );
        require(success, "Failed to mint SEN");

        emit Minted(msg.sender, salvAmount, senAmount, phase);
    }

    function getCurrentHalvingPhase() public view returns (uint256) {
        uint256 timeElapsed = block.timestamp - genesisTimestamp;
        uint256 phase = timeElapsed / HALVING_INTERVAL;
        return phase > MAX_HALVINGS ? MAX_HALVINGS : phase;
    }

    function setSentinel(address _sentinel) external onlyOwner {
        require(_sentinel != address(0), "Invalid Sentinel address");
        require(sentinel == address(0), "Sentinel already set");
        sentinel = _sentinel;
    }

    function rescueERC20(IERC20 token, address to, uint256 amount) external onlyOwner {
        require(address(token) != address(salvToken), "Cannot rescue SALV");
        token.transfer(to, amount);
    }
}