Contract detail
HupStatus
HupStatus is a verified Robinhood Chain smart contract at 0xc407722d…90a89efd with 0 indexed transactions.
- Address type
- Smart contract
- Transactions
- 0
- History coverage
- No indexed activity found
- Emitted events
- 1
- Verification
- Verified
- Compiler
- v0.8.35+commit.47b9dedd
- Verification match
- partial
- Creation transaction
- 0x7a377427…f02f35d9
- Created at block
- #1038817
- Language
- solidity
- EVM version
- default
- License
- none
- Compiler optimization
- Enabled · 200 runs
Verified contract
Contract source code
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.35;
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/utils/Pausable.sol";
import "@openzeppelin/contracts/metatx/ERC2771Context.sol";
import "./IHupStatus.sol";
/**
* @title Hup Status
* @author Hup Labs
* @notice Minimal onchain status protocol for managing user statuses with optional expiration.
* @dev Uses IHupStatus for shared events, errors, structs, and state getters. Supports rotatable ERC2771 trusted
* forwarders for meta-transactions, Ownable for admin permissions, and Pausable for emergency
* controls. Rich discovery, search, feeds, bookmarks, views, and global post routing are expected
* to be handled offchain by indexers.
* @custom:version 1.0.0
* @custom:chain multichain
* @custom:website https://hup.social
* @custom:security-contact security@hup.social
* @custom:emoji 💬
*/
contract HupStatus is IHupStatus, Ownable, Pausable, ERC2771Context {
// --- STATE VARIABLES ---
/// @notice The maximum allowed byte length for a user's status message. Set by the contract owner.
uint256 public override maxLength;
/// @notice Stores the current, active StatusData struct for each unique user address.
/// @dev This public mapping allows any external caller (including the frontend) to retrieve any user's status data directly using their address.
mapping(address => StatusData) public override statuses;
/// @dev Internal tracker for the mutable trusted forwarder address.
address private _trustedForwarder;
// --- CONSTRUCTOR ---
/**
* @notice Initializes the contract and sets the maximum allowed length for user status messages.
* @dev The contract deploys with the caller as the initial owner and sets the initial content length limit.
* @param _maxLength The initial maximum byte length for status content.
* @param _trustedForwarderAddress The address of the EIP-2771 compatible meta transaction relayer.
*/
constructor(uint256 _maxLength, address _trustedForwarderAddress)
Ownable(_msgSender()) // Use _msgSender() to ensure correct owner is set even in gasless deployment
ERC2771Context(_trustedForwarderAddress)
{
maxLength = _maxLength;
_trustedForwarder = _trustedForwarderAddress;
}
// --- CORE MUTATIVE LOGIC ---
/**
* @notice Creates or updates the calling user's single status message.
* @dev Sets an optional expiration timestamp based on the provided duration in hours. Requires the content to be non-empty and under the configured maxLength. This function is restricted by the Pausable modifier `whenNotPaused`.
* @param _statusContent The new text content for the status. Cannot be empty.
* @param _statusType The category or type of the status (e.g., "status", "mood").
* @param _metadata Optional string for structured data (e.g., a link to an IPFS JSON file).
* @param _periodHours The duration in **hours** for which the status should remain active. Use 0 for a permanent status.
*/
function updateStatus(
string calldata _statusContent,
string calldata _statusType,
string calldata _metadata,
uint256 _periodHours
) external override whenNotPaused {
uint256 contentLen = bytes(_statusContent).length;
if (contentLen == 0) revert StatusContentEmpty();
if (contentLen > maxLength) revert StatusLengthExceeded(contentLen, maxLength);
uint256 expirationTimestamp;
// Calculate the actual expiration timestamp if a duration is provided.
if (_periodHours == 0) {
expirationTimestamp = 0; // 0 indicates the status is permanent.
} else {
// Converts input hours to seconds and adds to the current timestamp.
expirationTimestamp = block.timestamp + (_periodHours * 1 hours);
}
// Use _msgSender() to get the original signer's address, supporting meta transactions.
address user = _msgSender();
// Store the new StatusData, overwriting the previous one.
statuses[user] = StatusData({
content: _statusContent,
contentType: _statusType,
metadata: _metadata,
expirationTimestamp: expirationTimestamp,
timestamp: block.timestamp
});
// Emit an event to log the update, which is crucial for off-chain indexing.
emit StatusUpdated(user, _statusContent, _statusType, _metadata, _periodHours, block.timestamp);
}
/**
* @notice Clears the content of the calling user's current status (soft delete).
* @dev Performs a soft delete by setting the `content` field to an empty string (""). This signals off-chain applications to stop displaying the status. This function is restricted by the Pausable modifier `whenNotPaused`.
*/
function clearStatus() external override whenNotPaused {
// Use _msgSender() to get the original signer's address, supporting meta transactions.
address user = _msgSender();
// Only modify the content field, effectively 'deleting' the status visually.
statuses[user].content = "";
emit StatusCleared(user, block.timestamp);
}
// --- ADMIN CONFIGURATION ---
/**
* @notice Pauses the contract, preventing users from creating or clearing statuses.
* @dev Only the contract owner can call this function. Calls the Pausable internal `_pause()`.
*/
function pause() external override onlyOwner {
_pause();
}
/**
* @notice Unpauses the contract, allowing users to resume status creation and clearing.
* @dev Only the contract owner can call this function. Calls the Pausable internal `_unpause()`.
*/
function unpause() external override onlyOwner {
_unpause();
}
/**
* @notice Updates the maximum allowed byte length for a status message.
* @dev This prevents users from posting excessively long data on-chain. Only callable by the owner.
* @param _maxLength The new maximum byte length limit for the status content.
*/
function updateMaxLength(uint256 _maxLength) external override onlyOwner {
uint256 oldValue = maxLength;
maxLength = _maxLength;
emit MaxLengthUpdated(oldValue, _maxLength);
}
/**
* @notice Sets a new trusted forwarder address, updating EIP-2771 compatibility.
* @dev Only callable by the owner.
* @param _newTrustedForwarder The new trusted forwarder address.
*/
function setTrustedForwarder(address _newTrustedForwarder) external override onlyOwner {
if (_newTrustedForwarder == address(0)) revert InvalidAddress();
_trustedForwarder = _newTrustedForwarder;
emit TrustedForwarderUpdated(_newTrustedForwarder);
}
// --- OWNERSHIP ACTIONS ---
/**
* @notice Allows the current owner to transfer control of the contract to a newOwner.
* @dev Resolves conflict between Ownable and IHupStatus interfaces.
*/
function transferOwnership(address newOwner) public override(Ownable, IHupStatus) onlyOwner {
Ownable.transferOwnership(newOwner);
}
/**
* @notice Leaves the contract without an owner. Once the contract is renounced,
* there is no way to regain ownership.
* @dev Resolves conflict between Ownable and IHupStatus interfaces.
*/
function renounceOwnership() public override(Ownable, IHupStatus) onlyOwner {
Ownable.renounceOwnership();
}
// --- INTERNAL & OVERRIDE HELPERS ---
/**
* @dev See EIP-2771. Returns true if the address is the trusted forwarder.
*/
function isTrustedForwarder(address forwarder) public view override(ERC2771Context, IHupStatus) returns (bool) {
return forwarder == _trustedForwarder;
}
/**
* @dev Returns the owner address. Inherited from Ownable.
*/
function owner() public view override(Ownable, IHupStatus) returns (address) {
return Ownable.owner();
}
/**
* @dev Returns true if the contract is paused. Inherited from Pausable.
*/
function paused() public view override(Pausable, IHupStatus) returns (bool) {
return Pausable.paused();
}
/**
* @dev Returns the original signer of the transaction, supporting meta-transactions.
*/
function _msgSender() internal view override(Context, ERC2771Context) returns (address) {
return ERC2771Context._msgSender();
}
/**
* @dev Returns the input call data, supporting meta-transactions.
*/
function _msgData() internal view override(Context, ERC2771Context) returns (bytes calldata) {
return ERC2771Context._msgData();
}
/**
* @dev Returns the context suffix length, supporting meta-transactions.
*/
function _contextSuffixLength() internal view override(Context, ERC2771Context) returns (uint256) {
return ERC2771Context._contextSuffixLength();
}
}