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": []
}
}}
Submitted on: 2025-10-14 16:03:55
Comments
Log in to comment.
No comments yet.