Overview
ETH Balance
0 ETH
ETH Value
$0.00 (@ $1,906/ETH)
Token Holdings
—
More Info
Additional Info
Loading…
Loading…
Loading…
Loading…
Loading…
Contract Name
MarketHoursToken (Stock Coin)
Compiler
solidity · v0.8.26+commit.8a97fa7a
Optimization
Yes · 20000 runs
EVM Version
shanghai
Source
Mirrored from Robinhood Chain explorer
Contract Source Code
Outline
C MarketHoursToken
ƒ transfer
ƒ approve
ƒ transferFrom
ƒ _transfer
ƒ isMarketOpen
ƒ nextOpen
ƒ nextClose
ƒ isOpenAt
ƒ nextOpenAfter
ƒ _easternOffset
ƒ _nthSundayUtc
ƒ _daysFromCivil
ƒ _yearOf
MarketHoursToken.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.26;
/// @title Market Hours Token
/// @notice An ERC-20 that keeps stock-market hours. Transfers clear only
/// Monday-Friday, 9:30 AM - 4:00 PM America/New_York (the NYSE regular
/// session), DST-aware, computed on-chain from block.timestamp.
/// Outside the session every transfer reverts with the timestamp of the
/// next opening bell. No pause switch, no fee, no allowlist, no upgrade
/// path: nobody can freeze, block, tax or seize anything, ever.
/// There is NO admin, NO owner and NO escape hatch: not even the
/// deployer can switch the market hours off. The clock is the contract.
/// @dev US DST rule hardcoded as legislated since 2007: EDT from the second
/// Sunday of March 02:00 to the first Sunday of November 02:00. If
/// Congress ever changes the rule, this token keeps the old one. The
/// clock is therefore only correct from 2007-03-11 onward; it answers
/// earlier instants with today's rule and can be an hour out on them.
/// @dev MarketClosed does not survive a router. Uniswap's TransferHelper
/// wraps the token call and re-reverts with Error("TF") on a buy and
/// Error("STF") on a sell, so a swap attempted outside the session
/// surfaces a generic transfer failure rather than this token's own
/// error. Only a direct transfer/transferFrom returns
/// MarketClosed(opensAtUtc), selector 0x9dc30b8e. This is router
/// behaviour and applies to any token that restricts transfers; it
/// cannot be changed from here, because TransferHelper discards custom
/// errors by design. Interfaces should call isMarketOpen() before
/// offering a trade rather than interpreting a revert reason.
/// @dev Integration notes: a zero-value transfer is gated like any other, so
/// accounting pokes fail outside the session; approve() works around the
/// clock but there is no EIP-2612 permit; transfers to address(0)
/// succeed while totalSupply is immutable, so burning that way leaves
/// totalSupply overstating the float.
contract MarketHoursToken {
string public name;
string public symbol;
uint8 public constant decimals = 18;
uint256 public immutable totalSupply;
mapping(address => uint256) public balanceOf;
mapping(address => mapping(address => uint256)) public allowance;
/// @notice Eastern-time day number of the last session that traded.
uint256 public lastSessionDay;
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
/// @notice Rung by the first transfer that clears on each trading day.
/// @dev Any address can ring it with a zero-value transfer at its own
/// gas cost, so treat it as "the session opened", not as a trade.
/// It cannot be suppressed or repeated within a session.
event OpeningBell(uint256 indexed easternDay);
error MarketClosed(uint256 opensAtUtc);
uint256 private constant OPEN = 9 hours + 30 minutes; // 09:30 ET
uint256 private constant CLOSE = 16 hours; // 16:00 ET
constructor(string memory name_, string memory symbol_, uint256 supply) {
name = name_;
symbol = symbol_;
totalSupply = supply;
balanceOf[msg.sender] = supply;
emit Transfer(address(0), msg.sender, supply);
}
/// @notice Send tokens. Reverts MarketClosed(opensAtUtc) outside the session, a zero-value transfer included.
function transfer(address to, uint256 value) external returns (bool) {
_transfer(msg.sender, to, value);
return true;
}
function approve(address spender, uint256 value) external returns (bool) {
allowance[msg.sender][spender] = value;
emit Approval(msg.sender, spender, value);
return true;
}
function transferFrom(address from, address to, uint256 value) external returns (bool) {
uint256 allowed = allowance[from][msg.sender];
if (allowed != type(uint256).max) allowance[from][msg.sender] = allowed - value;
_transfer(from, to, value);
return true;
}
function _transfer(address from, address to, uint256 value) internal {
{
if (!isOpenAt(block.timestamp)) revert MarketClosed(nextOpenAfter(block.timestamp));
uint256 day = (block.timestamp - _easternOffset(block.timestamp)) / 1 days;
if (day != lastSessionDay) {
lastSessionDay = day;
emit OpeningBell(day);
}
}
balanceOf[from] -= value;
balanceOf[to] += value;
emit Transfer(from, to, value);
}
// ------------------------------------------------------------------
// The clock. All pure; UIs and bots can call these for any timestamp.
// ------------------------------------------------------------------
function isMarketOpen() external view returns (bool) {
return isOpenAt(block.timestamp);
}
/// @notice UTC timestamp of the next opening bell (now, if the market is open).
function nextOpen() external view returns (uint256) {
return nextOpenAfter(block.timestamp);
}
/// @notice UTC timestamp of the next closing bell.
function nextClose() external view returns (uint256) {
uint256 t = nextOpenAfter(block.timestamp);
uint256 day = (t - _easternOffset(t)) / 1 days;
return day * 1 days + CLOSE + _easternOffset(t);
}
/// @notice True during the regular session at the given UTC instant.
/// @dev Accurate from 2007-03-11 onward (see the DST note on the
/// contract). Reverts for ts < 18000: subtracting the Eastern
/// offset underflows, so do not call it with 0 as a sentinel.
function isOpenAt(uint256 ts) public pure returns (bool) {
uint256 eastern = ts - _easternOffset(ts);
uint256 dow = (eastern / 1 days + 4) % 7; // 0 = Sunday
if (dow == 0 || dow == 6) return false;
uint256 s = eastern % 1 days;
return s >= OPEN && s < CLOSE;
}
/// @notice The next opening bell at or after ts; ts itself when open.
/// @dev Always lands on an instant where isOpenAt is true and the second
/// before it is false. The trailing revert cannot be reached: any
/// weekday after `day` opens later than ts, and any 7 consecutive
/// days contain a weekday.
function nextOpenAfter(uint256 ts) public pure returns (uint256) {
if (isOpenAt(ts)) return ts;
uint256 day = (ts - _easternOffset(ts)) / 1 days;
for (uint256 i = 0; i < 8; i++) {
uint256 d = day + i;
uint256 dow = (d + 4) % 7;
if (dow == 0 || dow == 6) continue;
uint256 openEastern = d * 1 days + OPEN;
// Offset probed 12h past the bell: the nearest DST switch (a Sunday
// 06:00/07:00 UTC) is always further away, so this is exact.
uint256 openUtc = openEastern + _easternOffset(openEastern + 12 hours);
if (openUtc > ts) return openUtc;
}
revert(); // unreachable: any 8-day window holds a weekday bell
}
/// @dev Seconds behind UTC: 4h during EDT, 5h during EST.
function _easternOffset(uint256 ts) internal pure returns (uint256) {
uint256 y = _yearOf(ts);
uint256 dstStart = _nthSundayUtc(y, 3, 2) + 7 hours; // 02:00 EST
uint256 dstEnd = _nthSundayUtc(y, 11, 1) + 6 hours; // 02:00 EDT
return (ts >= dstStart && ts < dstEnd) ? 4 hours : 5 hours;
}
/// @dev 00:00 UTC of the n-th Sunday of the given month.
function _nthSundayUtc(uint256 y, uint256 m, uint256 n) private pure returns (uint256) {
uint256 first = _daysFromCivil(y, m, 1);
uint256 dow = (first + 4) % 7; // 0 = Sunday
uint256 date = 1 + ((7 - dow) % 7) + 7 * (n - 1);
return _daysFromCivil(y, m, date) * 1 days;
}
// Howard Hinnant's civil-date algorithms.
function _daysFromCivil(uint256 y, uint256 m, uint256 d) private pure returns (uint256) {
unchecked {
y -= m <= 2 ? 1 : 0;
uint256 era = y / 400;
uint256 yoe = y - era * 400;
uint256 doy = (153 * (m > 2 ? m - 3 : m + 9) + 2) / 5 + d - 1;
uint256 doe = yoe * 365 + yoe / 4 - yoe / 100 + doy;
return era * 146097 + doe - 719468;
}
}
function _yearOf(uint256 ts) private pure returns (uint256) {
unchecked {
uint256 z = ts / 1 days + 719468;
uint256 era = z / 146097;
uint256 doe = z - era * 146097;
uint256 yoe = (doe - doe / 1460 + doe / 36524 - doe / 146096) / 365;
uint256 doy = doe - (365 * yoe + yoe / 4 - yoe / 100);
uint256 mp = (5 * doy + 2) / 153;
return yoe + era * 400 + (mp >= 10 ? 1 : 0);
}
}
}Contract ABI
[
{
"type": "constructor",
"inputs": [
{
"name": "name_",
"type": "string",
"internalType": "string"
},
{
"name": "symbol_",
"type": "string",
"internalType": "string"
},
{
"name": "supply",
"type": "uint256",
"internalType": "uint256"
}
],
"stateMutability": "nonpayable"
},
{
"name": "MarketClosed",
"type": "error",
"inputs": [
{
"name": "opensAtUtc",
"type": "uint256",
"internalType": "uint256"
}
]
},
{
"name": "Approval",
"type": "event",
"inputs": [
{
"name": "owner",
"type": "address",
"indexed": true,
"internalType": "address"
},
{
"name": "spender",
"type": "address",
"indexed": true,
"internalType": "address"
},
{
"name": "value",
"type": "uint256",
"indexed": false,
"internalType": "uint256"
}
],
"anonymous": false
},
{
"name": "OpeningBell",
"type": "event",
"inputs": [
{
"name": "easternDay",
"type": "uint256",
"indexed": true,
"internalType": "uint256"
}
],
"anonymous": false
},
{
"name": "Transfer",
"type": "event",
"inputs": [
{
"name": "from",
"type": "address",
"indexed": true,
"internalType": "address"
},
{
"name": "to",
"type": "address",
"indexed": true,
"internalType": "address"
},
{
"name": "value",
"type": "uint256",
"indexed": false,
"internalType": "uint256"
}
],
"anonymous": false
},
{
"name": "allowance",
"type": "function",
"inputs": [
{
"name": "",
"type": "address",
"internalType": "address"
},
{
"name": "",
"type": "address",
"internalType": "address"
}
],
"outputs": [
{
"name": "",
"type": "uint256",
"internalType": "uint256"
}
],
"stateMutability": "view"
},
{
"name": "approve",
"type": "function",
"inputs": [
{
"name": "spender",
"type": "address",
"internalType": "address"
},
{
"name": "value",
"type": "uint256",
"internalType": "uint256"
}
],
"outputs": [
{
"name": "",
"type": "bool",
"internalType": "bool"
}
],
"stateMutability": "nonpayable"
},
{
"name": "balanceOf",
"type": "function",
"inputs": [
{
"name": "",
"type": "address",
"internalType": "address"
}
],
"outputs": [
{
"name": "",
"type": "uint256",
"internalType": "uint256"
}
],
"stateMutability": "view"
},
{
"name": "decimals",
"type": "function",
"inputs": [],
"outputs": [
{
"name": "",
"type": "uint8",
"internalType": "uint8"
}
],
"stateMutability": "view"
},
{
"name": "isMarketOpen",
"type": "function",
"inputs": [],
"outputs": [
{
"name": "",
"type": "bool",
"internalType": "bool"
}
],
"stateMutability": "view"
},
{
"name": "isOpenAt",
"type": "function",
"inputs": [
{
"name": "ts",
"type": "uint256",
"internalType": "uint256"
}
],
"outputs": [
{
"name": "",
"type": "bool",
"internalType": "bool"
}
],
"stateMutability": "pure"
},
{
"name": "lastSessionDay",
"type": "function",
"inputs": [],
"outputs": [
{
"name": "",
"type": "uint256",
"internalType": "uint256"
}
],
"stateMutability": "view"
},
{
"name": "name",
"type": "function",
"inputs": [],
"outputs": [
{
"name": "",
"type": "string",
"internalType": "string"
}
],
"stateMutability": "view"
},
{
"name": "nextClose",
"type": "function",
"inputs": [],
"outputs": [
{
"name": "",
"type": "uint256",
"internalType": "uint256"
}
],
"stateMutability": "view"
},
{
"name": "nextOpen",
"type": "function",
"inputs": [],
"outputs": [
{
"name": "",
"type": "uint256",
"internalType": "uint256"
}
],
"stateMutability": "view"
},
{
"name": "nextOpenAfter",
"type": "function",
"inputs": [
{
"name": "ts",
"type": "uint256",
"internalType": "uint256"
}
],
"outputs": [
{
"name": "",
"type": "uint256",
"internalType": "uint256"
}
],
"stateMutability": "pure"
},
{
"name": "symbol",
"type": "function",
"inputs": [],
"outputs": [
{
"name": "",
"type": "string",
"internalType": "string"
}
],
"stateMutability": "view"
},
{
"name": "totalSupply",
"type": "function",
"inputs": [],
"outputs": [
{
"name": "",
"type": "uint256",
"internalType": "uint256"
}
],
"stateMutability": "view"
},
{
"name": "transfer",
"type": "function",
"inputs": [
{
"name": "to",
"type": "address",
"internalType": "address"
},
{
"name": "value",
"type": "uint256",
"internalType": "uint256"
}
],
"outputs": [
{
"name": "",
"type": "bool",
"internalType": "bool"
}
],
"stateMutability": "nonpayable"
},
{
"name": "transferFrom",
"type": "function",
"inputs": [
{
"name": "from",
"type": "address",
"internalType": "address"
},
{
"name": "to",
"type": "address",
"internalType": "address"
},
{
"name": "value",
"type": "uint256",
"internalType": "uint256"
}
],
"outputs": [
{
"name": "",
"type": "bool",
"internalType": "bool"
}
],
"stateMutability": "nonpayable"
}
]Creation code isn’t shown — this contract was deployed by a factory (internal CREATE), so it has no standalone creation transaction.
Deployed Bytecode
0x60806040526004361015610011575f80fd5b5f3560e01c806306fdde031461066957806309038a56146105d3578063095ea7b31461054e57806318160ddd146105145780631b5017ec146104f657806323b872dd1461041b578063313ce5671461040057806339d33f1b146103e357806370a082311461039e57806395d89b41146101c7578063a9059cbb14610196578063d4ce85f31461017b578063dd62ed3e1461010d578063f8cdb8ea146100e55763fd6bacbe146100be575f80fd5b346100e1575f6003193601126100e15760206100d942610844565b604051908152f35b5f80fd5b346100e15760206003193601126100e1576020610103600435610906565b6040519015158152f35b346100e15760406003193601126100e1576101266107e4565b73ffffffffffffffffffffffffffffffffffffffff610143610807565b91165f52600360205273ffffffffffffffffffffffffffffffffffffffff60405f2091165f52602052602060405f2054604051908152f35b346100e1575f6003193601126100e157602061010342610906565b346100e15760406003193601126100e1576101bc6101b26107e4565b6024359033610adf565b602060405160018152f35b346100e1575f6003193601126100e1576040515f600154908160011c60018316928315610394575b602082108414610367578185528493908115610307575060011461028d575b5003601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01681019067ffffffffffffffff821181831017610260576040829052819061025c908261077e565b0390f35b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b60015f90815291507fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf65b8183106102eb57505081016020017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe061020e565b60209193508060019154838588010152019101909183926102b7565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660208581019190915291151560051b840190910191507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0905061020e565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602260045260245ffd5b90607f16906101ef565b346100e15760206003193601126100e15773ffffffffffffffffffffffffffffffffffffffff6103cc6107e4565b165f526002602052602060405f2054604051908152f35b346100e1575f6003193601126100e1576020600454604051908152f35b346100e1575f6003193601126100e157602060405160128152f35b346100e15760606003193601126100e1576101bc6104376107e4565b61043f610807565b6044359173ffffffffffffffffffffffffffffffffffffffff8116805f52600360205260405f2073ffffffffffffffffffffffffffffffffffffffff33165f5260205260405f2054847fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82036104b8575b505050610adf565b6104c19161082a565b905f52600360205260405f2073ffffffffffffffffffffffffffffffffffffffff33165f5260205260405f20558480846104b0565b346100e15760206003193601126100e15760206100d9600435610844565b346100e1575f6003193601126100e15760206040517f0000000000000000000000000000000000000000033b2e3c9fd0803ce80000008152f35b346100e15760406003193601126100e1576105676107e4565b73ffffffffffffffffffffffffffffffffffffffff60243591335f52600360205260405f208282165f526020528260405f205560405192835216907f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560203392a3602060405160018152f35b346100e1575f6003193601126100e1576105ec42610844565b620151806106026105fc8361096c565b8361082a565b04906201518082029180830462015180149015171561063c5761e100820180921161063c576020916106366100d99261096c565b90610837565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b346100e1575f6003193601126100e1576040515f8054908160011c60018316928315610774575b60208210841461036757818552849390811561030757506001146106fc575003601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01681019067ffffffffffffffff821181831017610260576040829052819061025c908261077e565b5f80805291507f290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e5635b81831061075857505081016020017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe061020e565b6020919350806001915483858801015201910190918392610724565b90607f1690610690565b919091602081528251928360208301525f5b8481106107ce5750507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f845f6040809697860101520116010190565b8060208092840101516040828601015201610790565b6004359073ffffffffffffffffffffffffffffffffffffffff821682036100e157565b6024359073ffffffffffffffffffffffffffffffffffffffff821682036100e157565b9190820391821161063c57565b9190820180921161063c57565b61084d81610906565b61090357620151806108616105fc8361096c565b04905f5b60088110610871575f80fd5b61087b8184610837565b6004810180821161063c576007900680159081156108f8575b506108ef576201518081029080820462015180149015171561063c5761859881019081811161063c5762012e58019081811161063c576106366108d69261096c565b8281116108e857506001905b01610865565b9250505090565b506001906108e2565b60069150145f610894565b90565b610919906109138161096c565b9061082a565b6201518081046004810180911161063c57600790068015908115610961575b5061095c576201518090066185988110159081610953575090565b61e10091501090565b505f90565b60069150145f610938565b62015180810462023ab1620afa6c8201049062023ab182029003600a60996002620afa6c61016d81860162023ab0810490836105b4618eac83049204890301010304946064860486841c8761016d020103900301600502010410155f14610ad45761019060ff60015b16920201016109e5600382610be8565b6004810180911161063c57600790066007036007811161063c576007900680600101908160011161063c5760080180911161063c57610a2690600383610c8d565b906201518082029180830462015180149015171561063c57616270820180921161063c57610a55600b82610be8565b6004810180911161063c57600790066007036007811161063c57600790066001018060011161063c57600b610a8992610c8d565b906201518082029180830462015180149015171561063c57615460820180921161063c578210159182610aca575b505015610ac45761384090565b61465090565b1090505f80610ab7565b61019060ff5f6109d5565b610ae842610906565b15610bb457602073ffffffffffffffffffffffffffffffffffffffff807fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9362015180610b3d610b374261096c565b4261082a565b046004548103610b86575b501693845f526002835260405f20610b6187825461082a565b90551693845f526002825260405f20610b7b828254610837565b9055604051908152a3565b806004557f48d801bbc61ae4ecf9e6f3b54217fb9d83cd84956f94a67d591d8e48175b56ed5f80a25f610b48565b610bbd42610844565b7f9dc30b8e000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b9062023ab17ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff5059492600560028085119283155f14610c855760ff60015b1690039461019086049561019087029003935f14610c7d577ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd015b609902010490606481049061016d8160021c91020103019102010190565b600901610c5f565b60ff5f610c24565b9062023ab17fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff927ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff5059494600560028086119384155f14610d4e5760ff60015b1690039561019087049661019088029003945f14610d46577ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd015b60990201040190606481049061016d8160021c9102010301910201010190565b600901610d26565b60ff5f610ceb56fea164736f6c634300081a000a
Loading…
0 tokens · $0 total value
| Token | Symbol | Balance | Price | Value |
|---|---|---|---|---|
| no token holdings | ||||
A 1200×630 shareable card — also used as the social preview when this page is linked on X, Discord, etc.
Address
0x4ec7150fc4f2090a8f3352df6cf4d8a206749304
Type
ERC-20 Token
Bytecode Size
3427 bytes
Balance
0 ETH
A contract address hosts a smart contract — a set of code functions that can execute independently of any centralized server. Learn more in our Knowledge Base.