# Elyname integration kit for Elyscan

Elyname is the ENS-style name service for the **.elysium** TLD on Elysium. This kit shows how an explorer can display `name.elysium` instead of raw addresses and let users search by name.

> **Status: TESTNET ONLY.** Mainnet chain ID and contract addresses will be published at Elysium mainnet launch. Keep them in config (see `config.json`) so switching is a one-line change.

## Network and contract (testnet)

| Item | Value |
|---|---|
| Chain | Elysium Testnet |
| Chain ID | `99801` |
| RPC | `https://testnet-rpc.elysium.kinetiq.xyz` |
| Elyname contract (registrar + resolver + reverse) | `0xF3A2c7e68A6fBF1e95B9473eF35123321Ac237F0` |
| Deploy block | `19589057` |
| Deploy tx | `0xa3788658df134ba23dc352b37801a9fbbc0e14b11a49cb34e46b9500be4b4b59` |
| NFT | ERC-721, name `Elyname`, symbol `ELYNAME`, tokenId = `uint256(keccak256(label))` |

A single contract holds everything, so there is only one address to integrate.

## View functions

All functions take the **label without `.elysium`** (e.g. `"kinetiq"` for `kinetiq.elysium`). Labels are lowercase `a-z`, `0-9`, `-`, 1 to 63 characters.

### Pricing and limits (contract v2)

| Label length | Price | Limit |
|---|---|---|
| 1-3 characters | 1 USD per year, paid in HYPE (`rentPrice` = `weiPerUsd` x years) | Unlimited per wallet |
| 4+ characters | Free | A wallet can hold at most 5 active free names |

- `weiPerUsd` is set by the contract owner from a market HYPE/USD price (there is no on-chain price oracle on Elysium yet). Always read `rentPrice(label, duration)` before paying.
- Free limit: `register` reverts with `FreeLimitReached()` if the receiving wallet (`owner`) already holds 5 active (unexpired) free names. Paid names do not count. Names transferred in count; names transferred out or expired stop counting. Helpers: `activeFreeCount(address)`, `canRegisterFree(address)`, `isPaid(label)`.
- Free names can be registered or renewed for at most 365 days ahead of the current time (`FreeDurationTooLong()`); renewing a free name costs nothing. Paid names can be registered or renewed for any duration (minimum 28 days).
- 90-day grace period after expiry for all names.

| Purpose | Function | Returns |
|---|---|---|
| Name → address | `addr(string label)` | `address` (zero if unset or expired) |
| Subdomain → address | `subdomainAddr(string parentLabel, string sub)` | `address` for `sub.parent.elysium` |
| Address → primary name | `primaryNameOf(address account)` | `string` label, or `""` |
| Text record | `text(string label, string key)` | keys: `avatar`, `url`, `com.twitter`, `description` |
| Owner of name | `ownerOf(tokenIdOf(label))` | reverts if expired |
| Expiry | `expiries(tokenIdOf(label))` | unix seconds; 90-day grace period after it |
| Availability | `available(string label)` | `bool` |

**Forward check:** `primaryNameOf` already verifies on-chain that the name is active and that `addr(name) == account`. If the name expires, is transferred, or points elsewhere, it returns `""`. You can display the result directly.

Events for indexing: `NameRegistered(string label, uint256 indexed tokenId, address indexed owner, uint256 cost, uint256 expires)`, `AddrChanged(bytes32 indexed node, address addr)`, `PrimaryNameSet(address indexed account, uint256 indexed tokenId)`, `TextChanged(bytes32 indexed node, string key, string value)`.

## Files

- `config.json`: chain, RPC, contract, API base URL (edit `apiBaseUrl` when the domain changes)
- `elyname-abi.min.json`: minimal ABI for read-only integration
- `elyname-abi.full.json`: full contract ABI
- `viem-example.mjs`, `ethers-example.mjs`: runnable examples
- `logos/`: `elyname-logo-final.jpg`, `x-profile-400.png` (400×400), `favicon-64.png`

## viem

```js
import { createPublicClient, http } from 'viem';
import abi from './elyname-abi.min.json' with { type: 'json' };
const client = createPublicClient({ transport: http('https://testnet-rpc.elysium.kinetiq.xyz') });
const C = { address: '0xF3A2c7e68A6fBF1e95B9473eF35123321Ac237F0', abi };

const address = await client.readContract({ ...C, functionName: 'addr', args: ['kinetiq'] });
const label = await client.readContract({ ...C, functionName: 'primaryNameOf', args: ['0x6d838f479d9327E91741A003A00aF9b41249dEba'] });
// label === 'kinetiq'  -> display 'kinetiq.elysium'
```

## ethers v6

```js
import { JsonRpcProvider, Contract } from 'ethers';
import abi from './elyname-abi.min.json' with { type: 'json' };
const ely = new Contract('0xF3A2c7e68A6fBF1e95B9473eF35123321Ac237F0', abi,
  new JsonRpcProvider('https://testnet-rpc.elysium.kinetiq.xyz'));

const address = await ely.addr('kinetiq');
const label = await ely.primaryNameOf('0x6d838f479d9327E91741A003A00aF9b41249dEba');
```

Batch many `primaryNameOf` calls with Multicall3 (if deployed) or JSON-RPC batching when rendering address lists.

## Public REST API

Base URL: `https://elyname.xyz/` (the server is path-agnostic, so the same paths work at a domain root). It will move to an Elyname domain; read it from `config.json`. CORS is open (`*`), responses are cached for about 10 seconds.

### `GET /api/resolve/<name>`

```
curl https://elyname.xyz/api/resolve/kinetiq.elysium
```
```json
{ "name": "kinetiq.elysium", "address": "0x6d838f479d9327E91741A003A00aF9b41249dEba", "chainId": 99801, "contract": "0xF3A2c7e68A6fBF1e95B9473eF35123321Ac237F0" }
```
`.elysium` is optional. Subdomains work too (`/api/resolve/pay.kinetiq.elysium`). Returns `404` with `"address": null` if unregistered, unset or expired; `400` for invalid names.

### `GET /api/reverse/<address>`

```
curl https://elyname.xyz/api/reverse/0x6d838f479d9327E91741A003A00aF9b41249dEba
```
```json
{ "address": "0x6d838f479d9327E91741A003A00aF9b41249dEba", "name": "kinetiq.elysium", "forwardChecked": true, "chainId": 99801, "contract": "0xF3A2c7e68A6fBF1e95B9473eF35123321Ac237F0" }
```
Returns `404` with `"name": null` if the address has no valid primary name.

### Extra

- `GET /api/name/<name>`: full profile (owner, address, expiry, text records, availability, `paid`/`free`, yearly price in wei)
- `GET /api/pricing`: current pricing rules and `weiPerUsd`
- `GET /api/free/<address>`: active free names held and whether the wallet can register another
- `GET /api/names/<address>`: names owned by an address

## Display guidelines

- Show `name.elysium` next to (not instead of) the shortened address on address pages, and link to `https://elyname.xyz/testnet/#/name/<label>`.
- Accept `*.elysium` in the search box and resolve it with `addr` before routing to the address page.
- Use only on-chain results; never cache a primary name longer than a few minutes.

## Mainnet

Mainnet chain ID, RPC and contract addresses are **not deployed yet** and will be added here at Elysium mainnet launch. Do not hard-code the testnet address.

## NFT metadata and image

- `GET https://elyname.xyz/api/metadata/<tokenId>`: ERC-721 style JSON (name, description, image, attributes: Length, Expiration Date, Registration Date)
- `GET https://elyname.xyz/api/image/kinetiq.elysium.svg`: the name card image (SVG)

`tokenURI(tokenId)` returns `https://elyname.xyz/api/metadata/<tokenId>` (owner-settable `baseURI`). The contract emits ERC-4906 `BatchMetadataUpdate` when the base URI changes, so indexers should refresh metadata on that event.

## Where to get this kit

- Kit page: https://elyname.xyz/zip/
- Download: https://elyname.xyz/zip/elyscan-kit.zip
