How to interact with the subdomain manager contract
The TON monorepo provides dns-manual-code.fc, a subdomain manager contract that stores DNS records for arbitrary subdomains. This page documents every message and get-method the contract exposes.
Bind the contract to a domain
Section titled “Bind the contract to a domain”After deploying the subdomain manager, bind it to a .ton domain by sending op::change_dns_record to the domain item contract:
key:SHA-256("dns_next_resolver")value: cell containingdns_next_resolver#ba93with the subdomain manager address
dns_next_resolver#ba93 resolver:MsgAddressInt = DNSRecord;All queries for *.domain.ton are then delegated to the subdomain manager.
Resolution chain
Section titled “Resolution chain”When resolving sub.domain.ton:
- Root DNS delegates to the .ton collection
- Collection resolves
domainto the item contract - Item contract returns
dns_next_resolverpointing to the subdomain manager - Subdomain manager resolves
suband returns the DNS record
Contract storage layout
Section titled “Contract storage layout”| Field | Size | Description |
|---|---|---|
contract_id | 32 bits | Contract identifier |
last_cleaned | 64 bits | Timestamp for replay protection cleanup |
public_key | 256 bits | Owner Ed25519 public key for message authentication |
root | dict | Prefix tree: domain name to category dictionary |
old_queries | dict | 64-bit query IDs for replay protection |
Message format
Section titled “Message format”All messages are external (signed by the owner private key).
| Field | Size | Description |
|---|---|---|
signature | 512 bits | Ed25519 signature over the rest of the message |
contract_id | 32 bits | Must match stored contract_id |
query_id | 64 bits | Must be >= now() << 32. Old queries are cleaned up after 64 seconds. The lower 32 bits form a unique message ID within a second. |
op | 6 bits | Operation code (see table below) |
| payload | variable | Operation-specific data |
Domain name encoding in messages
Section titled “Domain name encoding in messages”The name field in VSet, VDel, DSet, and DDel payloads uses an Either encoding:
- 1-bit flag: if
1, the name is stored in a cell reference; if0, the name is inlined - Inline format: 6-bit length (in bytes) followed by
length × 8bits of name data - The name must be at least 2 bytes (16 bits), and the last byte must be
0x00(null terminator)
Opcodes
Section titled “Opcodes”| Opcode | Hex | Name | Payload | Description |
|---|---|---|---|---|
| 0 | 0x00 | Noop | (none) | No operation (used as separator in chained operations) |
| 1 | 0x01 | SMsg | 8-bit mode + cell ref (message) | Send a raw internal message from the contract |
| 9 | 0x09 | CodeUpgrade | cell ref (new code) | Replace the contract code |
| 11 | 0x0B | VSet | uint256 category + domain name + maybe_ref value | Set a DNS record for a subdomain + category |
| 12 | 0x0C | VDel | uint256 category + domain name | Delete a DNS record for a subdomain + category |
| 21 | 0x15 | DSet | domain name + maybe_ref (category dict) | Replace the entire category dictionary for a subdomain |
| 22 | 0x16 | DDel | domain name | Delete all records for a subdomain |
| 31 | 0x1F | TSet | maybe_ref (new root dict) | Replace the entire domain table |
| 32 | 0x20 | TDel | (none) | Clear the entire domain table |
| 51 | 0x33 | OSet | uint256 (new public key) | Change the owner public key |
Chaining multiple operations
Section titled “Chaining multiple operations”The contract’s process_ops function iterates in a loop, allowing multiple operations in a single external message. After reading an op and its payload from the current slice, if remaining data or references exist, parsing continues. When the current slice is exhausted, the next cell reference is loaded and processing continues from there.
VSet, VDel, DSet, DDel, TSet, TDel, SMsg, CodeUpgrade, and Noop operations can all be batched into one signed message.
Set a subdomain record (VSet)
Section titled “Set a subdomain record (VSet)”To point sub.domain.ton to a wallet address:
- Encode the domain:
subbecomessub\0in TON DNS internal format (null-terminated, reversed component order) - Compute the category key:
SHA-256("wallet") - Build the value cell:
dns_smc_address#9fd3with the target wallet address - Send an external message with op
0x0B, the category, the encoded domain name, and the value as amaybe_ref
Delete a subdomain record (VDel)
Section titled “Delete a subdomain record (VDel)”Same as VSet but with op 0x0C and no value cell. Removes the record for the specified category + domain combination.
Domain name encoding
Section titled “Domain name encoding”TON DNS uses a reversed, null-terminated encoding. Domain components are split by ., each component is null-terminated, and the order is reversed.
| Domain | Encoded bytes |
|---|---|
sub | sub\0 |
a.b | b\0a\0 |
x.y.z | z\0y\0x\0 |
The dnsresolve get-method expects this encoding in the subdomain slice.
Get-methods
Section titled “Get-methods”| Method | Signature | Returns |
|---|---|---|
get_contract_id | () -> int | 32-bit contract identifier |
get_public_key | () -> int | 256-bit owner public key |
dnsresolve | (slice, int) -> (int, cell) | (bits_consumed, record_cell) |
dnsresolve parses the subdomain slice, looks up the domain in the root dictionary, and returns either:
- The full category dictionary if
categoryis0 - A specific record if
categorymatches a stored key - A
dns_next_resolverif the subdomain has remaining components to resolve
Related pages
Section titled “Related pages”- TON DNS: resolution architecture
- TON DNS reference: record type TL-B schemas, categories
- Domain item contract: register domains and set the
dns_next_resolverbinding