HCS-7: Smart Hashinals: A Micro-DSL for Deterministic Topic Selection.
Status: Draft
This standard is current in Draft mode, and should not be used in production.
Authors
- Kantorcodes https://twitter.com/kantorcodes
Abstract
This standard is a sub-standard of HCS-6 which introduces a way to inscribe and tokenize Hashinals whose metadata
can be updated based on smart contract state by utilizing an indexed Topic ID and WASM processing module.
Motivation
HCS-6 introduced a novel way of updating metadata of a Hashinal dynamically by registering new sequence numbers. Some use-cases require switching between metadata based on complex conditions that can be verified on-chain, such as:
- Number of minted NFTs
- Uniswap V3 pool price thresholds
- Token balance requirements
- Time-weighted average prices
- Complex mathematical calculations
These kinds of use cases, while possible with HCS-6, would require submitting many messages and maintaining off-chain state. HCS-7, through trustless, programmatic, verifiable execution solves for this by combining on-chain state with WASM processing.
Specification
Topic Creation and Structure
-
Topic Requirements
- Memo format:
hcs-7:indexed:{ttl}
- TTL must be at least 3600 (1 hour)
- No admin key (as per HCS-2)
- Must be created after HCS-7 launch
- Memo format:
-
Metadata Format
- Follows HCS-5 rules with protocol number
7
- HRL format:
hcs://7/{topicId}
topicId
must be a valid HCS-7 Registry Topic ID
- Follows HCS-5 rules with protocol number
Message Types and Fields
1. EVM Configuration
{
"p": "hcs-7",
"op": "register-config",
"t": "evm",
"c": {
"contractAddress": "0x1d67aaf7f7e8d806bbeba24c4dea24808e1158b8",
"abi": {
"inputs": [],
"name": "minted",
"outputs": [
{
"name": "",
"type": "uint64"
}
],
"stateMutability": "view",
"type": "function"
}
},
"m": "LaunchPage Test Mint"
}
Field Descriptions:
p
: Protocol identifier (must be "hcs-7")op
: Operation type (must be "register-config")t
: Configuration type (must be "evm")c.contractAddress
: Ethereum contract addressc.abi
: Contract ABI with following fields:name
: Method name to callinputs
: Array of input parametersoutputs
: Array of return typesstateMutability
: Must be "view" or "pure"type
: Must be "function"
2. WASM Configuration
{
"p": "hcs-7",
"op": "register-config",
"t": "wasm",
"c": {
"wasmTopicId": "0.0.5263817",
"inputType": {
"stateData": {
"minted": "number"
}
},
"outputType": {
"type": "string",
"format": "topic-id"
}
},
"m": "minted-even-odd-router"
}
Field Descriptions:
p
: Protocol identifier (must be "hcs-7")op
: Operation type (must be "register-config")t
: Configuration type (must be "wasm")c.wasmTopicId
: Topic ID containing WASM modulec.inputType
: JSON Schema for input datastateData
: Contract return value types
c.outputType
: Output specification- Must return valid topic ID
Required WASM Functions: The WASM module must implement two functions:
-
process_state(state_json: &str, messages_json: &str) -> String
- Processes the state data and messages to determine the output topic ID
state_json
: JSON string containing the state data from EVM callsmessages_json
: JSON string containing the array of registered messages- Returns: A valid topic ID as a string
-
get_params() -> String
- Returns a JSON string describing the required parameters and their types
- Example return value:
{
"minted": "number",
"tokensRemaining": "number"
}
Example WASM Code:
#[wasm_bindgen]
pub fn process_state(state_json: &str, messages_json: &str) -> String {
// Parse JSON inputs
let state: Value = match serde_json::from_str(state_json) {
Ok(data) => data,
Err(_) => return String::new(),
};
let messages: Vec<Value> = match serde_json::from_str(messages_json) {
Ok(data) => data,
Err(_) => return String::new(),
};
// Get minted from state, return empty if invalid
let minted = match parse_minted(&state) {
Some(p) => p,
None => return String::new()
};
// Process data - check if minted is even/odd
let is_even = minted % 2u32 == BigUint::from(0u32);
let topic_id = if is_even {
// Find first matching even registration
messages.iter()
.find(|msg| {
msg.get("t_id").is_some() &&
msg.get("d")
.and_then(|d| d.get("tags"))
.and_then(|tags| tags.as_array())
.map(|tags| tags.iter().any(|t| t.as_str() == Some("even")))
.unwrap_or(false)
})
.and_then(|msg| msg.get("t_id").and_then(|id| id.as_str()))
.map(|s| s.to_string())
} else {
// Find first matching odd registration
messages.iter()
.find(|msg| {
msg.get("t_id").is_some() &&
msg.get("d")
.and_then(|d| d.get("tags"))
.and_then(|tags| tags.as_array())
.map(|tags| tags.iter().any(|t| t.as_str() == Some("odd")))
.unwrap_or(false)
})
.and_then(|msg| msg.get("t_id").and_then(|id| id.as_str()))
.map(|s| s.to_string())
};
// Return topic ID or empty string if none found
topic_id.unwrap_or_default()
}
3. Metadata Registration Messages
{
"p": "hcs-7",
"op": "register",
"t_id": "0.0.3717738",
"m": "blue",
"d": {
"weight": 1,
"tags": ["odd"]
}
}
Field Descriptions:
p
: Protocol identifier (must be "hcs-7")op
: Operation type (must be "register")t_id
: HCS-1 topic ID containing metadatam
: Message descriptiond
: Additional routing dataweight
: Priority if multiple matchestags
: Routing identifiers
Implementation Flow
-
Setup Phase
- Create an indexed HCS-7 topic with memo format
hcs-7:indexed:{ttl}
- Deploy WASM module to a separate topic for processing state data
- Submit initial EVM configuration message with contract address and ABI
- Submit initial WASM configuration message referencing the WASM module topic
- Create HCS-1 topics for each metadata variant (e.g., odd/even)
- Register each HCS-1 topic with appropriate tags and weights
- Validate all configurations are properly submitted and indexed
- Create an indexed HCS-7 topic with memo format
-
Query Flow
- Read topic messages to get both EVM and WASM configurations
- If EVM config exists, call the smart contract method specified in the EVM config to get state data
- Collect all registered HCS-1 Topic messages with their additional data
- Pass both the state data and message history to the WASM module
- Use the returned Topic ID to serve the appropriate content
-
Processing Requirements
- Smart contract calls must be view/pure
- WASM execution under 50ms
- Valid topic ID must be returned
- Error handling required
Error Handling
-
Smart Contract Errors
- Invalid calls: Skip state update
- Network issues: Use cached state
- Contract not found: Fail validation
-
WASM Processing Errors
- Invalid input: Return empty
- Timeout: Fail gracefully
- No match: Use default topic
-
Metadata Errors
- Topic not found: Fail validation
- Invalid format: Skip registration
- Network issues: Use cache
Limitations
-
Smart Contract Limitations
- Must be public view/pure functions
- No state mutations allowed
- Must be verified and accessible
- Gas costs considered
-
WASM Limitations
- 50ms processing limit
- Must handle all error cases
- Limited to topic ID selection
- Memory constraints
-
Infrastructure Requirements
- Cache management required
- Network reliability important
- State consistency needed
- Error recovery plans
Conclusion
HCS-7 extends HCS-6 by enabling smart contract driven metadata selection through WASM processing. This creates a powerful system for dynamic NFTs while maintaining decentralization and trustlessness.