API Overview
Reference for the Lime ENS offchain resolver gateway: CCIP-Read resolution, subdomain reservation, records, eligibility checks, and account endpoints.
Base URL: http://localhost:8080 (default port; use your deployed gateway URL in production)
Content-Type: application/json
CORS: Enabled for all origins (*)
Endpoints
1. Health Check
Check server status and signer configuration.
GET /healthResponse (200 OK):
{
"status": "ok",
"signer": "0x...",
"timestamp": 1706544000,
"indexer": {
"enabled": true,
"synced": true,
"last_block": 12345678,
"current_block": 12345679,
"behind": 1
}
}When the onchain indexer is disabled (no ETH_RPC_URL or RESOLVER_ADDRESS), the indexer object is omitted. When the indexer is enabled but RPC fails, indexer includes "synced": false and "error": "..." instead of current_block / behind.
| Field | Type | Description |
|---|---|---|
| status | string | "ok" or "error" |
| signer | string | Backend signer address (hex) |
| timestamp | number | Unix timestamp |
| indexer | object | Optional. Onchain indexer sync state: enabled, synced, last_block, current_block, behind; or error when RPC fails. Omitted when indexer is disabled. |
2. CCIP-Read (ENS Resolution)
EIP-3668 CCIP-Read endpoint for offchain ENS resolution. Called by the HybridResolver contract.
POST /Request body:
{
"data": "0x...",
"extraData": "0x...",
"sender": "0x..."
}| Field | Type | Required | Description |
|---|---|---|---|
| data | string | Yes | Hex-encoded callData (name + function selector) |
| extraData | string | No | Hex-encoded (callData, targetAddress) |
| sender | string | No | Alternative: sender address |
Response (200 OK):
{
"data": "0x..."
}The data field contains ABI-encoded (bytes result, uint64 expires, bytes sig).
API v1 — Subdomain Reservation
3. Check Name Availability
Check if a subdomain is available and whether the wallet can register it. No authentication required.
GET /api/v1/names/checkQuery parameters (one backend instance per network; no chainId):
| Parameter | Type | Required | Description |
|---|---|---|---|
| name | string | Yes | ENS name (e.g., test.free.eth) |
| address | string | No | Wallet address for permission check |
Example:
GET /api/v1/names/check?name=test.free.eth&address=0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb5Response (200 OK):
{
"available": true,
"can_register": true,
"resolution": "offchain"
}| Field | Type | Description |
|---|---|---|
| available | boolean | Whether the name is available |
| can_register | boolean | null | Whether the wallet can register (null if wallet required) |
| reason | string | Optional: name_taken, wallet_required |
| resolution | string | "offchain" | "onchain" (reserved for future) |
Response variants:
| Scenario | available | can_register | reason |
|---|---|---|---|
| Name taken | false | — | name_taken |
Free, parent has * | true | true | — |
| Free, parent restricted | true | null | wallet_required |
| Free, wallet can register | true | true | — |
| Free, wallet cannot | true | false | — |
Error (400 Bad Request):
{
"error": "parent domain not under backend control"
}4. Import Resolver by ENS Name
If the ENS resolver for this name is the backend “lime” resolver, the backend will import the name into its ens_records table using the ENS owner address.
For a new import, the row is created with expires_at null (no backend-managed expiry; on-chain ENS controls registration). Renew in this API only applies to names that have a non-null backend expiry (subnames from Reserve).
If the resolver is any other address, the backend returns an error containing the current resolver and resolved type.
GET /api/v1/names/resolverQuery parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
| name | string | Yes | ENS name (e.g., lime.eth) |
Example:
GET /api/v1/names/resolver?name=lime.ethResponse (200 OK, only when resolver type is lime):
{
"record": {
"name": "lime.eth",
"node": "0x...",
"address": "0x...",
"expires_at": null
},
"owner": "0x...",
"resolver": "0x4255f8BC8E92729abe8d4343d6d656D4C69Dd564",
"type": "lime",
"already_imported": false
}When the name’s node already exists in backend ens_records before this request (case 3: lime resolver, row already present), the import still succeeds (200 OK). The backend refreshes owner_address and the default address to the current ENS owner, replaces manager rights for that node with the ENS owner, and upserts multichain ETH (60) as for a new import.
already_importedistruemessageexplains that the name was already in lime and owner/managers were synced
| Field | Type | Description |
|---|---|---|
| record | object | Imported ens_records row (lightweight projection) |
| owner | string | ENS owner address |
| resolver | string | Resolver address from ENS Registry |
| type | string | Resolver type: lime | standart | custom |
| already_imported | boolean | true if an ens_records row for this node existed before this call |
| message | string | Present when already_imported is true: owner + managers aligned with ENS |
Resolver type rules:
lime: resolver equals backendRESOLVER_ADDRESSstandart: resolver equals ENS standard resolver addresscustom: any other resolver address
standart spelling is kept intentionally for API compatibility.
Errors:
400:namequery parameter is missing409: resolver is notlime(includes currentresolverandtype)502: failed to read resolver/owner from Ethereum RPC503: Ethereum RPC is not configured (ETH_RPC_URLis empty)
5. Reserve Name
Register a subdomain. Requires EIP-712 signature from the owner.
POST /api/v1/names/reserveRequest body:
{
"name": "test.free.eth",
"owner": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb5",
"chainId": 11155111,
"signature": "0x...",
"domain": {
"name": "ENS Offchain Resolver",
"version": "1",
"chainId": 11155111,
"verifyingContract": "0x..."
},
"timestamp": 1706544000
}| Field | Type | Required | Description |
|---|---|---|---|
| name | string | Yes | ENS name to register |
| owner | string | Yes | Owner wallet address (hex) |
| chainId | number | No | Network ID (default: 1) |
| signature | string | Yes | EIP-712 signature (65 bytes, hex) |
| domain | object | Yes | EIP-712 domain separator |
| timestamp | number | Yes | Unix timestamp (replay protection, 15 min window) |
EIP-712 Typed Data (ReserveName):
- Domain:
name,version,chainId,verifyingContract - Message:
ReserveName(string name, address owner, uint64 timestamp, uint256 chainId)
Response (201 Created):
{
"status": "ok",
"name": "test.free.eth"
}Error (400 Bad Request):
{
"error": "name is taken"
}{
"error": "wallet does not have permission to register this name"
}{
"error": "signature expired or invalid timestamp"
}6. Renew Name
Sets the expiration for a registered name (same rule as reserve: now + parent's subdomain_registration_duration_days, not an extension on top of the previous expiry). Signer must be the owner or a manager of the parent domain. Uses EIP-712 typed data RenewName.
POST /api/v1/names/renewRequest body (same shape as Reserve):
{
"name": "test.free.eth",
"owner": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb5",
"chainId": 11155111,
"signature": "0x...",
"domain": {
"name": "ENS Offchain Resolver",
"version": "1",
"chainId": 11155111,
"verifyingContract": "0x..."
},
"timestamp": 1706544000
}| Field | Type | Required | Description |
|---|---|---|---|
| name | string | Yes | ENS name to renew |
| owner | string | Yes | Owner wallet address (hex) |
| chainId | number | No | Network ID (default: 1) |
| signature | string | Yes | EIP-712 signature for RenewName (65 bytes, hex) |
| domain | object | Yes | EIP-712 domain separator |
| timestamp | number | Yes | Unix timestamp (replay protection, 15 min window) |
EIP-712 Typed Data (RenewName):
- Domain:
name,version,chainId,verifyingContract - Message:
RenewName(string name, address owner, uint64 timestamp, uint256 chainId)
Response (200 OK):
{
"status": "ok",
"name": "test.free.eth"
}Errors (400 Bad Request):
record not found— name does not exist or is expired (expired names can still be renewed)name cannot be renewed: no backend-managed expiry (imported or root name)—expires_atis null (imported ENS name, root, or any record without backend expiry)wallet is not owner or manager of this name— signer is not owner of the name nor manager of the parentsignature expired or invalid timestamp/signature does not match owner
7. Get All Records (Profile / Display)
Returns all ENS records for a name. Intended for profile/display pages. Missing values are returned as null (aligned with ENS API and frontend best practices).
GET /api/v1/names/recordsQuery parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
| name | string | Yes | ENS name (e.g., test.free.eth) |
Example:
GET /api/v1/names/records?name=test.free.ethResponse (200 OK):
{
"name": "test.free.eth",
"node": "0x...",
"address": "0x284cc50eb6C93c55BA4963D0dC5097C54db71580",
"addresses": {
"0": {
"address": "bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh",
"onchain": false
},
"60": {
"address": "0x284cc50eb6C93c55BA4963D0dC5097C54db71580",
"onchain": true
},
"501": {
"address": "HN7cABqLq46Es1jh92dQQisAq662SmxELLLsHHe4YWrH",
"onchain": false
},
"614": {
"address": "0x284cc50eb6C93c55BA4963D0dC5097C54db71580",
"onchain": false
}
},
"content_hash": null,
"pubkey": null,
"text": {
"url": "https://example.com",
"avatar": null,
"description": null,
"notice": null,
"keywords": null,
"com.twitter": null,
"com.github": null,
"email": null,
"location": null,
"phone": null,
"name": null
},
"abi": [
{ "type": "function", "name": "transfer", "inputs": [{ "name": "to", "type": "address" }, { "name": "amount", "type": "uint256" }], "outputs": [], "stateMutability": "nonpayable" }
],
"zonehash": null,
"reverse_name": null,
"managers": [],
"eligibility_url": null,
"expires_at": "2026-01-29T12:00:00Z",
"resolution": "hybrid"
}| Field | Type | Description |
|---|---|---|
| name | string | ENS name |
| node | string | Namehash as hex (0x…) |
| address | string | null | Default ETH address (hex) |
| addresses | object | coinType (string) → { "address": string, "onchain": boolean }. address is the value (hex, bech32, base58, etc.); onchain is true if from onchain setAddr (HybridResolver), false if offchain (DB). Common keys: 0 = Bitcoin, 60 = Ethereum, 501 = Solana, 614 = Optimism. Empty {} if none. |
| content_hash | string | null | Decoded URI: ipfs://<cid>, ipns://<cid>, or 0x...; null if not set. Resolver uses raw EIP-1577 bytes from storage. |
| pubkey | object | null | { "x": "0x...", "y": "0x..." } (hex); null if not set or zero |
| text | object | Standard ENS text keys + custom keys; value or null for each key |
| abi | array | ABI as flat array of fragments. Empty [] if none. No content_types/offset wrapper. |
| zonehash | string | null | Zone hash (hex); null if not set |
| reverse_name | string | null | Reverse resolution name; null if not set |
| expires_at | string | null | Expiration (ISO8601); null if never expires |
| resolution | string | "offchain" | "hybrid" | "onchain" — classification of merged address sources (same rules as Get Account Names): hybrid if some coin types use onchain setAddr and others use offchain DB; onchain if the name is primary (exactly two labels, e.g. foo.eth or lime.dev) and every present address is onchain; offchain if the name is a subname (three or more labels) and no onchain addresses. A primary name with only offchain addresses is hybrid. Single-label names fall back to hybrid. |
| managers | array | Manager addresses for this name (who can manage it); may include "*" for open access. Empty [] if none. Each name has its own list; changes via POST /api/v1/names/manage do not affect other sub-names. |
| eligibility_url | string | null | External API URL used for subdomain eligibility checks for this name’s node (name_registration_config). null if not configured. Same value you set with POST /api/v1/names/manage. |
Error (400 Bad Request):
{ "error": "name is required" }Error (404 Not Found):
{ "error": "record not found" }7.1 Update Records (POST)
Update ENS record fields for a name. The signer must be the owner or manager of the name. Only fields present in the body are updated; omitted fields are left unchanged. Use null to clear a field (e.g. remove a text key). Replay protection: timestamp must be within 15 minutes. To change manager addresses or eligibility URL for the name, use POST /api/v1/names/manage (owner only; see 7.2 below).
POST /api/v1/names/recordsRequest body:
| Field | Type | Required | Description |
|---|---|---|---|
| name | string | Yes | ENS name to update (e.g. test.free.eth) |
| chainId | number | Yes | Network ID (1 = Mainnet, 11155111 = Sepolia) |
| timestamp | number | Yes | Unix timestamp (replay protection; valid 15 min window) |
| domain | object | Yes | EIP-712 domain separator (name, version, chainId, verifyingContract) |
| signature | string | Yes | EIP-712 signature (65 bytes hex) of UpdateRecords(name, chainId, timestamp, payloadHash) |
| address | string | null | No | Default ETH address (hex); omit = no change, null = clear |
| addresses | object | No | coinType (string) → address: 0 = BTC, 60 = ETH, 501 = Solana, 614 = Optimism; omit = no change; empty string = delete that coin type |
| content_hash | string | null | No | Content hash: hex 0x... (EIP-1577 bytes), or ipfs://<cid>, or ipns://<cid>; omit = no change, null or "" = clear |
| pubkey | object | null | No | { "x": "0x...", "y": "0x..." } (32 bytes each); omit = no change, null = clear |
| text | object | No | key → value; omit = no change; key with null = delete that key |
| abi | array | No | ABI: ENSIP-4 [{ "content_types": "0x...", "offset": "0x...", "abi": [...] }] or flat [{ "type": "function", "name": "foo", "inputs": [...] }, ...] (stored as one record with content_types=1 = JSON per ENSIP-4, offset=0; so resolver ABI(node, 1) returns it); omit = no change; empty [] = clear all |
| zonehash | string | null | No | Zone hash (hex); omit = no change, null = clear |
| reverse_name | string | null | No | Reverse name; omit = no change, null or "" = clear |
EIP-712 Typed Data (UpdateRecords):
- Domain: same as Reserve (
name,version,chainId,verifyingContract). - Message:
UpdateRecords(string name, uint256 chainId, uint64 timestamp, bytes32 payloadHash). - payloadHash:
keccak256(canonicalJSON(payload))wherepayloadis the JSON object containing only the record fields being updated (address,addresses,content_hash,pubkey,text,abi,zonehash,reverse_name). Keys must be sorted alphabetically for canonical encoding.
Example request (set default address, multichain addresses for BTC / Solana / Optimism, and text):
{
"name": "test.free.eth",
"chainId": 11155111,
"timestamp": 1706544000,
"domain": {
"name": "ENS Offchain Resolver",
"version": "1",
"chainId": 11155111,
"verifyingContract": "0x..."
},
"signature": "0x...",
"address": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb5",
"addresses": {
"0": "bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh",
"60": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb5",
"501": "HN7cABqLq46Es1jh92dQQisAq662SmxELLLsHHe4YWrH",
"614": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb5"
},
"text": { "url": "https://example.com", "avatar": null }
}Coin types (SLIP-44) for addresses:
| coinType | Chain | Address format example |
|---|---|---|
| 0 | Bitcoin | bc1q... (SegWit/bech32) or bc1p... (Taproot/bech32m) |
| 60 | Ethereum | 0x... (20 bytes hex) |
| 501 | Solana | Base58 (e.g. HN7cABqLq46...) |
| 614 | Optimism | 0x... (same EVM format as Ethereum) |
Response (200 OK):
{ "status": "ok", "name": "test.free.eth" }Errors (400 Bad Request):
{ "error": "name and signature are required" }{ "error": "signature expired or invalid timestamp" }{ "error": "wallet is not owner or manager of this name" }Error (404 Not Found):
{ "error": "record not found" }7.2 Manage name settings (POST)
Update managers and/or eligibility_url for a name. The signer must be the owner of the name (wallet matching address or owner_address on the record); delegated managers cannot call this endpoint. Replay protection: timestamp must be within 15 minutes.
POST /api/v1/names/manageRequest body:
| Field | Type | Required | Description |
|---|---|---|---|
| name | string | Yes | ENS name (e.g. test.free.eth) |
| chainId | number | Yes | Network ID (1 = Mainnet, 11155111 = Sepolia) |
| timestamp | number | Yes | Unix timestamp (replay protection; valid 15 min window) |
| domain | object | Yes | EIP-712 domain separator (name, version, chainId, verifyingContract) |
| signature | string | Yes | EIP-712 signature (65 bytes hex) of ManageName(name, chainId, timestamp, payloadHash) |
| managers | array | No | Full replacement list of manager addresses; may include "*" for open access. Omit = no change. Does not affect other sub-names. |
| eligibility_url | string | null | No | URL used for subdomain eligibility checks. Omit = no change; null or "" clears the stored URL. |
EIP-712 Typed Data (ManageName):
- Domain: same as Reserve / Update Records (
name,version,chainId,verifyingContract). - Message:
ManageName(string name, uint256 chainId, uint64 timestamp, bytes32 payloadHash). - payloadHash:
keccak256(canonicalJSON(payload))wherepayloadcontains only the management fields present in the body (eligibility_url,managers). Keys must be sorted alphabetically for canonical encoding. If neither field is sent,payloadis the empty object{}.
Response (200 OK):
{ "status": "ok", "name": "test.free.eth" }Errors (400 Bad Request):
name and signature are required/invalid JSON/signature expired or invalid timestamp/invalid signatureonly the owner can manage name settingsinvalid manager address: ... (must be 0x... or *)invalid eligibility_url: ...
Error (404 Not Found):
{ "error": "record not found" }8. Get Account Names
Returns all ENS names where the wallet address is owner or manager (for “My names” / account page). Each owned name includes its registration end date (expires_at) and a resolution field describing how address records for that name are sourced (merged view matches GET /api/v1/names/records addresses / onchain flags). Non-address data (text, content hash, ABI, etc.) is always stored offchain in this backend and does not change resolution.
resolution values
| Value | Meaning |
|---|---|
offchain | The name is a subname (three or more dot-separated labels, e.g. a.b.eth, padel.lime.dev) and every present address is offchain (DB only; no onchain setAddr for any coin type). |
hybrid | Mixed sources: at least one coin type is onchain and at least one is offchain — or any case that is neither pure offchain nor pure onchain below (e.g. a primary two-label name with only offchain addresses, or a single-label name). |
onchain | The name is primary (exactly two labels, e.g. foo.eth or lime.dev) and every present address is onchain (no offchain-only coin types in the merged set). |
GET /api/v1/account/namesQuery parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
| address | string | Yes | Wallet address (hex, with or without 0x) |
| chainId | number | No | Reserved for future use (one backend instance per network today). |
Example:
GET /api/v1/account/names?address=0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb5Response (200 OK):
{
"owned": [
{ "name": "test.free.eth", "expires_at": "2026-01-29T12:00:00Z", "resolution": "hybrid" },
{ "name": "log01.eth", "expires_at": null, "resolution": "hybrid" }
],
"managed": ["free.eth"]
}| Field | Type | Description |
|---|---|---|
| owned | object[] | Names where the address is owner; each has name, expires_at (ISO8601 or null), and resolution ("offchain" | "hybrid" | "onchain"). Expired names are excluded. |
| managed | string[] | Parent domain names where the address is manager (can register subdomains). Names the address already owns are excluded. |
Error (400 Bad Request):
{ "error": "address is required" }{ "error": "invalid address length" }9. Get Statistics
Returns aggregate statistics (names claimed, total record fields). Extensible for future metrics.
GET /api/v1/statisticsResponse (200 OK):
{
"names_claimed": 42,
"records_count": 156
}| Field | Type | Description |
|---|---|---|
| names_claimed | number | Total number of claimed names (rows in ens_records). |
| records_count | number | Total non-null record fields: multichain addresses, text records, content hash, pubkey, zonehash, reverse name, ABI, DNS, and default address in ens_records. |
10. Get setAddr Signature (Onchain Resolver)
Returns all data needed to form a setAddr(node, coinType, addressBytes, deadline, nonce, signature) transaction on the onchain resolver (HybridResolver / OnchainAddrResolver). By default addressBytes are taken from offchain data (ENS records / multichain addresses). With remove=true, addressBytes is the zero address (0x0000..., 20 bytes) to clear/remove the onchain record. The client never passes nonce. The backend obtains the current nonce from the indexer (contract’s NonceIncremented event); for a node that has not had setAddr yet, the backend uses nonce 0. The response always includes nonce so the client can call resolver.setAddr(..., nonce, signature) with the returned value. Signature is valid for 12 minutes (backend window); the contract also enforces a maximum deadline TTL of 7 days.
GET /api/v1/signature/address?name=lime.eth&coinType=60
GET /api/v1/signature/address?name=lime.eth&coinType=60&remove=true
POST /api/v1/signature/addressQuery parameters (GET):
| Parameter | Type | Required | Description |
|---|---|---|---|
| name | string | Yes | ENS name (e.g. lime.eth) |
| coinType | number | Yes | SLIP-44 coin type (e.g. 60 for ETH) |
| remove | boolean | No | If true or 1, returns addressBytes = 0x0 (20 zero bytes) to remove the onchain record |
Request body (POST):
{
"name": "lime.eth",
"coinType": 60,
"remove": false
}| Field | Type | Required | Description |
|---|---|---|---|
| name | string | Yes | ENS name |
| coinType | number | Yes | SLIP-44 coin type |
| remove | boolean | No | If true, returns addressBytes = 0x0 to clear the onchain record |
Response (200 OK):
{
"node": "0x...",
"coinType": 60,
"addressBytes": "0x...",
"deadline": 1706544720,
"nonce": 0,
"signature": "0x..."
}| Field | Type | Description |
|---|---|---|
| node | string | Namehash as hex (bytes32) |
| coinType | number | Coin type (uint256) |
| addressBytes | string | Address bytes (hex). Zero address 0x0000... when remove=true |
| deadline | number | Unix timestamp; signature valid for 12 minutes (backend window) |
| nonce | number | Nonce used in the signature; must equal contract’s nonces[node] when calling setAddr |
| signature | string | Backend signature (r+s+v, 65 bytes hex) for the onchain resolver |
The client does not pass nonce; the backend provides it in the response (from the indexer or 0 for a new node). Call resolver.setAddr(node, coinType, addressBytes, deadline, nonce, signature) with the returned values. Use remove=true to get a signature for clearing the onchain address (addressBytes = 0x0).
Errors (400 Bad Request):
name is required/coinType is requiredstored nonce unavailable— backend could not read nonce from the indexer DBno address found for name "..." and coinType N— no offchain record for this name and coin type (only whenremoveis not set)
11. Verify External Eligibility
Calls an external API to check whether a wallet address is eligible (e.g., whitelisted, meets a condition). The backend makes a GET request to the provided URL with the wallet_address query parameter and expects the external API to return JSON {"result": true} or {"result": false}.
Security: The endpoint validates both the URL (blocks private/loopback IPs, restricts to http/https schemes, prevents SSRF via DNS resolution check) and the wallet address (must be 0x + 40 hex characters).
POST /api/v1/eligibility/verifyRequest body:
{
"api_url": "https://example.com/api/check-eligibility",
"wallet_address": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb5"
}| Field | Type | Required | Description |
|---|---|---|---|
| api_url | string | Yes | External API URL (http/https only; no private/loopback IPs) |
| wallet_address | string | Yes | Wallet address (0x + 40 hex characters) |
External API contract: The backend appends ?wallet_address=0x... to api_url (existing query parameters are preserved) and sends a GET request. The external API must return a 2xx status and JSON body:
{
"result": true
}| Field | Type | Description |
|---|---|---|
| result | boolean | true if eligible, false otherwise |
Response (200 OK):
{
"eligible": true,
"wallet_address": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb5",
"api_url": "https://example.com/api/check-eligibility"
}| Field | Type | Description |
|---|---|---|
| eligible | boolean | Whether the wallet passed the external check |
| wallet_address | string | The wallet address that was verified |
| api_url | string | The external API URL that was called |
Errors (400 Bad Request):
{ "error": "api_url and wallet_address are required" }{ "error": "invalid wallet address format: must be 0x followed by 40 hex characters" }{ "error": "invalid api_url: unsupported scheme \"ftp\": only http and https are allowed" }{ "error": "invalid api_url: requests to private/reserved addresses are not allowed" }{ "error": "external API returned status 404" }Security measures:
- URL scheme restricted to
httpandhttps - Blocks
localhost,127.0.0.0/8,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16, link-local, and unspecified addresses - Post-DNS-resolution IP check prevents SSRF via DNS rebinding
- Credentials in URL (
user:pass@host) are rejected - Response body limited to 1 MB
- HTTP timeout: 10 seconds; max 3 redirects
EIP-712 Signing (Reserve / Renew)
The client must sign the typed data with the owner’s private key. For Reserve use type ReserveName; for Renew use type RenewName with the same message shape (name, owner, timestamp, chainId).
Example (ethers.js v6):
const domain = {
name: "ENS Offchain Resolver",
version: "1",
chainId: 11155111,
verifyingContract: "0x..."
};
const types = {
ReserveName: [
{ name: "name", type: "string" },
{ name: "owner", type: "address" },
{ name: "timestamp", type: "uint64" },
{ name: "chainId", type: "uint256" }
]
};
const value = {
name: "test.free.eth",
owner: "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb5",
timestamp: Math.floor(Date.now() / 1000),
chainId: 11155111
};
const signature = await signer.signTypedData(domain, types, value);Chain IDs
| Network | chainId |
|---|---|
| Ethereum Mainnet | 1 |
| Sepolia | 11155111 |
Error Format
All errors return JSON:
{
"error": "Error message"
}HTTP status codes:
200— Success201— Created (reserve);200— OK (renew)400— Bad Request (invalid params, validation failed)404— Not Found (records: name not found or expired)405— Method Not Allowed503— Service Unavailable (health check when signer is nil)