TruthSequencedSeller24hProtected

Description:

Decentralized Finance (DeFi) protocol contract providing Swap functionality.

Blockchain: Ethereum

Source Code: View Code On The Blockchain

Solidity Source Code:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

/// @notice Minimal ERC20 interface (only what we use)
interface IERC20 {
    function approve(address spender, uint256 value) external returns (bool);
    function transfer(address to, uint256 value) external returns (bool);
    function balanceOf(address account) external view returns (uint256);
}

/// @notice Uniswap V2 router interface (only the functions we call)
interface IUniswapV2Router02 {
    function getAmountsIn(uint256 amountOut, address[] calldata path)
        external
        view
        returns (uint256[] memory amounts);

    function swapTokensForExactTokens(
        uint256 amountOut,          // exact USDC we want to receive
        uint256 amountInMax,        // max TRUTH we'll spend (slippage buffer)
        address[] calldata path,    // [TRUTH, USDC]
        address to,
        uint256 deadline
    ) external returns (uint256[] memory amounts);
}

/**
 * @title TruthSequencedSeller24hProtected
 * @notice Sells TRUTH -> USDC in a fixed 16-step schedule totaling $1,600 per rolling 24h window.
 *         Call `executeStep()` ~every 90 minutes via a private relay (e.g., Flashbots Protect).
 *         Owner can pause, change keeper, tweak slippage, and sweep tokens.
 */
contract TruthSequencedSeller24hProtected {
    // ---- access control ----
    address public owner;
    address public keeper; // automation EOA or service
    bool    public paused;

    // ---- core deps ----
    IERC20  public TRUTH;   // token being sold
    IERC20  public USDC;    // stablecoin received (assumed 6 decimals)
    IUniswapV2Router02 public router;

    // ---- config ----
    uint256 public constant TARGET_PER_WINDOW_USD = 1_600_000; // $1,600 total cap per 24h window

    // 16-step schedule that sums to exactly 1,600 USDC (6 decimals)
    uint256[] public steps = [
        117_000, 83_000, 111_000, 89_000,
        102_000, 98_000, 123_000, 77_000,
        109_000, 91_000, 140_000, 60_000,
        101_000, 99_000, 125_000, 75_000
    ];

    uint256 public slippageBps = 100; // 1% slippage

    // ---- rolling window state ----
    uint256 public windowStart;      // unix timestamp when current window began
    uint256 public soldInWindowUsd;  // total USDC (6 decimals) sold in this window
    uint256 public stepIndex;        // next index in steps[]

    uint256 private _locked;
    modifier nonReentrant() {
        require(_locked == 0, "Reentrancy blocked");
        _locked = 1;
        _;
        _locked = 0;
    }

    // ---- events ----
    event StepExecuted(uint256 stepUsd, uint256 truthIn, uint256 usdcOut, uint256 nextIndex);
    event WindowReset(uint256 newStart);
    event KeeperUpdated(address keeper);
    event SlippageUpdated(uint256 bps);
    event Paused(bool isPaused);

    // ---- modifiers ----
    modifier onlyOwner()        { require(msg.sender == owner, "Not owner"); _; }
    modifier onlyKeeperOrOwner(){ require(msg.sender == keeper || msg.sender == owner, "Not keeper/owner"); _; }
    modifier notPaused()        { require(!paused, "Paused"); _; }

    // ---- constructor ----
    constructor(
        address _router,
        address _truth,
        address _usdc,
        address _keeper
    ) {
        owner  = msg.sender;
        router = IUniswapV2Router02(_router);
        TRUTH  = IERC20(_truth);
        USDC   = IERC20(_usdc);
        keeper = _keeper;

        // Approve router once for spending TRUTH held by this contract
        TRUTH.approve(_router, type(uint256).max);

        // Window begins on first execution
        windowStart = 0;
    }

    // ---- admin ----
    function setKeeper(address _k) external onlyOwner {
        keeper = _k;
        emit KeeperUpdated(_k);
    }

    function setSlippage(uint256 _bps) external onlyOwner {
        require(_bps <= 1000, "Too high"); // Max 10%
        slippageBps = _bps;
        emit SlippageUpdated(_bps);
    }

    function setPaused(bool _p) external onlyOwner {
        paused = _p;
        emit Paused(_p);
    }

    function sweep(address token, uint256 amount, address to) external onlyOwner {
        IERC20(token).transfer(to, amount);
    }

    // ---- views ----
    function remainingUsd() public view returns (uint256) {
        if (_isNewWindow(block.timestamp)) return TARGET_PER_WINDOW_USD;
        if (soldInWindowUsd >= TARGET_PER_WINDOW_USD) return 0;
        return TARGET_PER_WINDOW_USD - soldInWindowUsd;
    }

    function nextStepUsd() public view returns (uint256) {
        return steps[stepIndex % steps.length];
    }

    // ---- main execution ----
    function executeStep() external notPaused onlyKeeperOrOwner nonReentrant {
        // Initialize or roll 24h window
        if (windowStart == 0) {
            windowStart = block.timestamp;
            emit WindowReset(windowStart);
        }
        if (_isNewWindow(block.timestamp)) {
            _resetWindow();
        }

        uint256 remaining = remainingUsd();
        require(remaining > 0, "Daily target reached");

        // Choose step but never exceed 24h cap
        uint256 stepUsd = nextStepUsd();
        if (stepUsd > remaining) stepUsd = remaining;

        // Quote TRUTH needed for exact USDC out
        address[] memory P = _path(address(TRUTH), address(USDC));
        uint256[] memory amtsIn = router.getAmountsIn(stepUsd, P);
        uint256 requiredTruth = amtsIn[0];

        // Add slippage buffer
        uint256 amountInMax = (requiredTruth * (10_000 + slippageBps)) / 10_000;

        require(TRUTH.balanceOf(address(this)) >= amountInMax, "Insufficient TRUTH");

        // Perform swap
        uint256[] memory res = router.swapTokensForExactTokens(
            stepUsd,
            amountInMax,
            P,
            address(this),
            block.timestamp + 300
        );

        soldInWindowUsd += stepUsd;
        stepIndex = (stepIndex + 1) % steps.length;

        emit StepExecuted(stepUsd, res[0], res[res.length - 1], stepIndex);
    }

    // ---- internal helpers ----
    function _isNewWindow(uint256 ts) internal view returns (bool) {
        return (windowStart != 0) && (ts >= windowStart + 1 days);
    }

    function _resetWindow() internal {
        windowStart = block.timestamp;
        soldInWindowUsd = 0;
        stepIndex = 0;
        emit WindowReset(windowStart);
    }

    function _path(address a, address b) internal pure returns (address[] memory P) {
        P = new  address[](2) ;
        P[0] = a;
        P[1] = b;
    }
}

Tags:
DeFi, Swap|addr:0xce7be430073939b59e81bf9865da1c60bd3235c3|verified:true|block:23727671|tx:0xf189639c287b3a5378bae2221f8a4a89c595e97852737886212fee00e61afa88|first_check:1762339440

Submitted on: 2025-11-05 11:44:02

Comments

Log in to comment.

No comments yet.