Mint top-level domains by paying a price based on name length:
Observation: domains (and subdomains where applicable) don't expire when refresh interval ends (default is 54 weeks), you stil own them after that time but if you didn't refresh them (by paying base price, even for 1 letter domains) until refresh interval expires, then anyone can get the domain after that time with force buy by paying double the price of the domain registration.
Domain owners control subdomain policies:
Subdomain revenue splitting
Top-level domains:
Subscription-based subdomains can be renewed anytime. Renewal price matches initial subscription/mint price and follows the revenue split of 80% to domain owner and 20% to platform.
Period Type | Duration | Functionality |
---|---|---|
Force Buy Interval | 14 days | Allows force buys after initial registration |
Refresh Interval | 54 weeks | Allows domain refresh after force buy interval ends |
Main Main Domain: Set a primary domain for your address, used to get domain for an address.
Admin Roles: Designate separate admin and payment addresses for domain management.
Use these faucets for test networks:
from web3 import Web3
import argparse
# example on how to execute it
# example_script_to_get_ip.py --rpc https://ethereum-sepolia-rpc.publicnode.com --contract 0xf78db67621CDd201C47a09987DEF172529371cfc --domain apple.onchain.wild
# example_script_to_get_ip.py --rpc https://ethereum-sepolia-rpc.publicnode.com --contract 0xf78db67621CDd201C47a09987DEF172529371cfc --domain cryptokid.wild
# Minimal ABI required for the functions we'll use
ABI = [
{
"inputs": [{"name": "name", "type": "string"}],
"name": "getTokenIdByName",
"outputs": [{"name": "", "type": "uint256"}],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [{"name": "tokenId", "type": "uint256"}],
"name": "_tokenData",
"outputs": [
{"name": "name", "type": "string"},
{"name": "forceBuyPrice", "type": "uint256"},
{"name": "lastForceBuyTime", "type": "uint256"},
{"name": "subscriptionExpirationTime", "type": "uint256"},
{"name": "mintTimestamp", "type": "uint256"},
{"name": "adminAddress", "type": "address"},
{"name": "paymentAddress", "type": "address"},
{"name": "customData", "type": "bytes"}
],
"stateMutability": "view",
"type": "function"
}
]
def get_ip_for_domain(rpc_url, contract_address, domain_name):
"""
Retrieves the IP address associated with a domain name from the blockchain.
Args:
rpc_url: URL of the Ethereum RPC endpoint
contract_address: Address of the WildDNS contract
domain_name: Full domain name (e.g., "subdomain.example.wild")
Returns:
IP address string if found, or None if not set or invalid
"""
# Connect to blockchain
w3 = Web3(Web3.HTTPProvider(rpc_url))
if not w3.is_connected():
raise ConnectionError("Failed to connect to RPC server")
# Create contract instance
contract = w3.eth.contract(address=contract_address, abi=ABI)
# Remove .wild TLD if present
if domain_name.endswith('.wild'):
domain_name = domain_name[:-5]
try:
# Get token ID
token_id = contract.functions.getTokenIdByName(domain_name).call()
if token_id == 0:
print(f"Domain not found: {domain_name}")
return None
# Get token data
token_data = contract.functions._tokenData(token_id).call()
custom_data = token_data[7] # customData is at index 7
# Check if custom data is an IPv4 address (4 bytes)
if len(custom_data) == 4:
# Convert bytes to IPv4 string
return '.'.join(str(byte) for byte in custom_data)
elif custom_data:
print(f"Custom data is not an IPv4 address: {custom_data.hex()}")
else:
print("No IP address set for this domain")
except Exception as e:
print(f"Error: {str(e)}")
return None
if __name__ == "__main__":
parser = argparse.ArgumentParser(description='Get IP address for a WildDNS domain')
parser.add_argument('--rpc', required=True, help='Ethereum RPC URL')
parser.add_argument('--contract', required=True, help='Contract address')
parser.add_argument('--domain', required=True, help='Domain name (e.g., "example.wild" or "sub.example.wild")')
args = parser.parse_args()
ip_address = get_ip_for_domain(args.rpc, args.contract, args.domain)
if ip_address:
print(f"IP address for {args.domain}: {ip_address}")
import requests
import json
import argparse
# Function to encode ABI data for getTokenIdByName
def encode_get_token_id_by_name(domain_name):
# Remove .wild suffix if present
if domain_name.endswith('.wild'):
domain_name = domain_name[:-5]
# Function signature (first 4 bytes of keccak256('getTokenIdByName(string)'))
function_sig = "0xdb7a298e"
# Encode domain name
domain_bytes = domain_name.encode('utf-8')
domain_hex = domain_bytes.hex()
# ABI encoding for string
# Offset to string data (always 0x20 for single string param)
offset = "0000000000000000000000000000000000000000000000000000000000000020"
# String length (padded to 32 bytes)
length = hex(len(domain_bytes))[2:].zfill(64)
# String content (padded to 32 bytes multiple)
content = domain_hex.ljust((len(domain_hex) + 63) // 64 * 64, '0')
return function_sig + offset + length + content
# Function to encode ABI data for _tokenData
def encode_token_data(token_id):
# Function signature (first 4 bytes of keccak256('_tokenData(uint256)'))
function_sig = "0x2c2cdd60"
# Encode token ID (padded to 32 bytes)
token_id_hex = hex(token_id)[2:].zfill(64)
return function_sig + token_id_hex
# Function to parse _tokenData response and extract IP
def parse_token_data_response(response_hex):
# Remove 0x prefix
if response_hex.startswith("0x"):
response_hex = response_hex[2:]
# The customData field is the 8th element in the tuple
# First we get the offset to customData (bytes at position 7*64 = 448 to 512)
custom_data_offset = int(response_hex[448:512], 16) * 2 # Convert to hex position
# At the offset, first 64 characters (32 bytes) is the length of customData
custom_data_length_hex = response_hex[custom_data_offset:custom_data_offset+64]
custom_data_length = int(custom_data_length_hex, 16)
# Next comes the actual customData (padded to 32 bytes)
custom_data_start = custom_data_offset + 64
custom_data_end = custom_data_start + custom_data_length * 2
custom_data_hex = response_hex[custom_data_start:custom_data_end]
# Convert hex to bytes
custom_data_bytes = bytes.fromhex(custom_data_hex)
# If we have exactly 4 bytes, it's an IPv4 address
if len(custom_data_bytes) == 4:
return '.'.join(str(byte) for byte in custom_data_bytes)
return None
def get_ip_for_domain(rpc_url, contract_address, domain_name):
# Step 1: Get token ID for domain
data = encode_get_token_id_by_name(domain_name)
payload = {
"jsonrpc": "2.0",
"method": "eth_call",
"params": [{
"to": contract_address,
"data": data
}, "latest"],
"id": 1
}
try:
response = requests.post(rpc_url, json=payload)
result = response.json()
if 'error' in result:
print(f"Error getting token ID: {result['error']['message']}")
return None
token_id_hex = result['result']
if token_id_hex == "0x0000000000000000000000000000000000000000000000000000000000000000":
print(f"Domain not found: {domain_name}")
return None
token_id = int(token_id_hex, 16)
except Exception as e:
print(f"Token ID request failed: {str(e)}")
return None
# Step 2: Get token data
data = encode_token_data(token_id)
payload = {
"jsonrpc": "2.0",
"method": "eth_call",
"params": [{
"to": contract_address,
"data": data
}, "latest"],
"id": 2
}
try:
response = requests.post(rpc_url, json=payload)
result = response.json()
if 'error' in result:
print(f"Error getting token data: {result['error']['message']}")
return None
token_data_hex = result['result']
ip_address = parse_token_data_response(token_data_hex)
return ip_address
except Exception as e:
print(f"Token data request failed: {str(e)}")
return None
if __name__ == "__main__":
parser = argparse.ArgumentParser(description='Get IP address for a WildDNS domain')
parser.add_argument('--rpc', required=True, help='Ethereum RPC URL')
parser.add_argument('--contract', required=True, help='Contract address')
parser.add_argument('--domain', required=True, help='Domain name (e.g., "example.wild" or "sub.example.wild")')
args = parser.parse_args()
ip_address = get_ip_for_domain(args.rpc, args.contract, args.domain)
if ip_address:
print(f"IP address for {args.domain}: {ip_address}")
else:
print("No IP address found for this domain")
function mint(string memory _name) external payable { require(GLOBAL_MINT_ENABLED, "Minting disabled"); _validateName(_name); uint256 mintPrice = _calculateMintPrice(_name); require(msg.value == mintPrice, "Incorrect payment"); // ... (token creation logic) }
function createSubdomain(uint256 parentTokenId_, string memory subLabel) external payable { require(policy.subdomainsEnabled, "Subdomains disabled"); // ... (access control and payment checks) // Revenue split: 80% to parent owner, 20% to contract }
function forceBuy(uint256 tokenId) external payable { require(GLOBAL_FORCE_BUY_ENABLED, "Force buy disabled"); // ... (price and timing validation) // Domain-specific payment distribution }
function renewSubscription(uint256 tokenId) external payable { require(isSubdomain(tokenId), "Not subdomain"); // ... (payment and expiration update) // Same revenue split as subdomain creation }