EthTimeVault

Description:

Smart contract deployed on Ethereum with Factory features.

Blockchain: Ethereum

Source Code: View Code On The Blockchain

Solidity Source Code:

{{
  "language": "Solidity",
  "sources": {
    "lockup2.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

/// @title ETH Time Vault (withdraw-all only, linear penalty before end)
contract EthTimeVault {
    /* ─────────────── Immutable & Storage ─────────────── */

    address public immutable creator;
    address public owner;

    uint256 public immutable startTime;
    uint256 public immutable endTime;

    // Sum of all ETH ever received (deposits)
    uint256 public totalReceived;

    // ETH actually paid to owner across withdrawals (excludes penalties)
    uint256 public withdrawnByOwner;

    /* ─────────────── Events ─────────────── */

    event Deposit(address indexed from, uint256 amount);
    event WithdrawAll(address indexed owner, uint256 ownerAmount, uint256 penaltyToCreator);
    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

    /* ─────────────── Minimal Reentrancy Guard ─────────────── */

    uint256 private _status; // 0 = not entered, 1 = entered
    modifier nonReentrant() {
        require(_status == 0, "Reentrancy");
        _status = 1;
        _;
        _status = 0;
    }

    /* ─────────────── Constructor ─────────────── */

    /// @param _startTime Unix timestamp when linear unlock begins (can be <= now)
    /// @param _endTime   Unix timestamp when 100% is unlocked (must be > _startTime)
    constructor(uint256 _startTime, uint256 _endTime) {
        require(_endTime > _startTime, "endTime must be > startTime");
        creator = msg.sender;
        owner   = msg.sender;
        startTime = _startTime;
        endTime   = _endTime;
    }

    /* ─────────────── ETH Intake ─────────────── */

    receive() external payable {
        if (msg.value > 0) {
            totalReceived += msg.value;
            emit Deposit(msg.sender, msg.value);
        }
    }

    fallback() external payable {
        if (msg.value > 0) {
            totalReceived += msg.value;
            emit Deposit(msg.sender, msg.value);
        }
    }

    /* ─────────────── Ownership ─────────────── */

    /// @notice Owner OR Creator can change the owner.
    function transferOwnership(address newOwner) external {
        require(msg.sender == owner || msg.sender == creator, "Not authorized");
        require(newOwner != address(0), "newOwner=0");
        emit OwnershipTransferred(owner, newOwner);
        owner = newOwner;
    }

    /* ─────────────── Withdraw-All Logic ─────────────── */

    /// @notice Withdraw the entire current ETH balance.
    /// If before endTime, the unlocked fraction goes to owner and the remainder is a penalty sent to creator.
    /// After endTime, the owner receives everything.
    function withdrawAll() external nonReentrant {
        require(msg.sender == owner, "Not owner");

        uint256 amount = address(this).balance;
        require(amount > 0, "No balance");

        uint256 ownerAmount;
        uint256 penalty;

        if (block.timestamp >= endTime) {
            // Fully unlocked
            ownerAmount = amount;
            penalty = 0;
        } else {
            // Split by current unlocked entitlement
            uint256 claimable = claimableNow(); // entitlement remaining to owner
            if (amount <= claimable) {
                ownerAmount = amount;
                penalty = 0;
            } else {
                ownerAmount = claimable;
                penalty = amount - claimable; // penalty to creator
            }
        }

        // Effects
        if (ownerAmount > 0) {
            withdrawnByOwner += ownerAmount;
        }

        // Interactions
        if (ownerAmount > 0) {
            (bool okOwner, ) = payable(owner).call{value: ownerAmount}("");
            require(okOwner, "Owner transfer failed");
        }
        if (penalty > 0) {
            (bool okCreator, ) = payable(creator).call{value: penalty}("");
            require(okCreator, "Creator transfer failed");
        }

        emit WithdrawAll(owner, ownerAmount, penalty);
    }

    /* ─────────────── Views ─────────────── */

    /// @return progress A value in [0, 1e18] representing linear time progress
    function progressRay() public view returns (uint256 progress) {
        if (block.timestamp <= startTime) return 0;
        if (block.timestamp >= endTime) return 1e18;
        uint256 elapsed = block.timestamp - startTime;
        uint256 duration = endTime - startTime;
        return (elapsed * 1e18) / duration;
    }

    /// @notice Total unlocked entitlement based on all ETH ever received
    function unlockedTotal() public view returns (uint256) {
        uint256 p = progressRay(); // 0..1e18
        return (totalReceived * p) / 1e18;
    }

    /// @notice How much the owner can still take right now without penalty (across all time)
    function claimableNow() public view returns (uint256) {
        uint256 u = unlockedTotal();
        if (u <= withdrawnByOwner) return 0;
        return u - withdrawnByOwner;
    }

    /// @notice Convenience: current ETH balance
    function balance() external view returns (uint256) {
        return address(this).balance;
    }
}
"
    }
  },
  "settings": {
    "optimizer": {
      "enabled": false,
      "runs": 200
    },
    "outputSelection": {
      "*": {
        "*": [
          "evm.bytecode",
          "evm.deployedBytecode",
          "devdoc",
          "userdoc",
          "metadata",
          "abi"
        ]
      }
    },
    "remappings": []
  }
}}

Tags:
Factory|addr:0xeb7e687a6940267bc8aac88140faf3e5ee4b2d89|verified:true|block:23575519|tx:0x641471cb74e7abec738bce152d9d0380d55d46d951507e97734a9d39c40dabde|first_check:1760450635

Submitted on: 2025-10-14 16:03:55

Comments

Log in to comment.

No comments yet.