Introduction
Sharing an ICP principal looks like this:
"Send it to `e6agv-wen3n-4a4y3-4zpeu-47coa-kso4c-vvow5-p5fbv-2pjg4-bm53l-lqe`"
63 characters. No human can verify it by eye. One typo sends funds to the void, permanently. And even if you copy it correctly — it says nothing about who you are, and it changes every time someone uses a different app.
iiname is a portable, on-chain username system for ICP, built on top of iilink’s unified principal layer. Register once. Use it everywhere in the iilink ecosystem.
I shipped it to mainnet this week and wanted to share it here with the community.
The Problem
Even if you solve the fragmented-principal problem with iilink, there’s still no way for users to refer to each other by name. Leaderboards show gibberish. Transfer recipient fields are error-prone. Developer-built UIs either truncate principals into something even less readable or copy-paste the full string.
The ICP ecosystem needs human-readable names that:
-
Resolve deterministically to an on-chain principal
-
Work across every app without re-registration
-
Are owned by the user, not the app
The Solution: iiname
iiname is an on-chain name service where every registered name resolves to an iilink main principal — the same unified principal that works across all iilink-integrated apps.
Register kayicp → it points to your iilink principal → any app in the ecosystem can resolve it instantly with a single canister call.
Key properties:
-
->Names are lowercase letters, digits, and underscores (1–32 chars)
-
->Registration is annual, with generous bonus months for multi-year packages
-
->Shorter names are priced higher (rarity reflects real scarcity)
-
->Every registered name gets a free public profile page at
/@name— live token balances, ICRC account, Account ID, and a QR code -
->Built on iilink: your name is tied to your unified principal, not an isolated per-app identity
Key Features
-
->Portable — name resolves to the same principal in every iilink-integrated app
-
->Free public profile —
/@yournameshows live balances, addresses, and QR codes with no extra setup -
->On-chain resolution — no server, no DNS, pure canister-based name → principal lookup
-
->Bidirectional — resolve name → principal AND principal → name
-
->Batch-friendly — resolve 100 names or principals per call
-
->Developer-ready — two read-only canister calls handle the full integration
Pricing Structure
Names are priced per character length per year (in TCYCLES, payable in ICP or TCYCLES):
| Length | TCYCLES / year |
|---|---|
| 5 chars | 50 |
| 6 chars | 25 |
| 7 chars | 10 |
| 8 chars | 5 |
| 9 chars | 2 |
| 10–19 chars | 1 |
| 20–32 chars | 0.5 |
Duration packages:
-
->1 year + 1 bonus month
-
->3 years + 4 bonus months
-
->5 years + 8 bonus months
Pricing is anchored to TCYCLES (pegged to compute, not ICP market volatility) for long-term stability.
Developer Integration
The integration surface is intentionally minimal. Two read-only canister calls cover everything.
Principal → Name (batch display)
// accounts = array of { owner: Principal, subaccount: [] }
// Batch up to 100 per call
const names_res = await iiname.ans_names(accounts);
for (let i = 0; i < names_res.results.length; i++) {
const { name, expires_at } = names_res.results[i];
const isValid = name.length > 0
&& nano2date(expires_at) > new Date();
display[i] = isValid
? `@${name}`
: Principal.toText(accounts[i].owner);
}
Name → Principal (resolution)
javascript
// Resolve a name typed by a user in a recipient field
const res = await iiname.ans_accounts(['kayicp']);
const account = res.results[0]?.[0];
if (account == null) {
// Name doesn't exist or has expired — show error
} else {
// account = { owner: Principal, subaccount: [] }
// Pass directly to your transfer or action function
await sendTokens(account, amount);
}
Deep Link: Send Tokens to an iiname
iilink’s transfer flow already resolves iinames natively. You can pre-fill a send UI with a name via URL:
const url = `https://loxja-3yaaa-aaaan-qz3ha-cai.icp0.io/balances/send?to=kayicp&token=${ICP_TOKEN_ID}`;
window.open(url, 'iilink', 'popup,width=480,height=660');
Canister ID: zyfw5-4qaaa-aaaac-qc7za-cai (dashboard)
Read-only calls require no authentication.
Existing Integrations
-
->iilink Transfer — the recipient field accepts iinames natively and resolves to principal before sending
-
->iiprofile — displays iiname instead of raw principal wherever user identity is shown
Current State & Limitations
-
->No transfer/resale mechanism (FOR NOW) — names are bound to a principal, not tradeable like NFTs (YET). This is intentional for now (UNLESS you guys want it?)
-
->No subdomain support — flat namespace only
-
->No renewal reminders — users are responsible for tracking expiry. An on-chain notification mechanism is on the roadmap.
-
->Early stage — I’m the first registrant. The canister is live but the ecosystem around it is nascent.
Feedback on any of these design decisions is very welcome.
Call to Action
If you’re building an app on ICP that shows user identities anywhere — leaderboards, transaction histories, profiles, @mentions — two canister calls gives you human-readable names.
Try it / claim your name: https://z7eqj-riaaa-aaaac-qc7zq-cai.icp0.io/
Check out my iiname profile: https://z7eqj-riaaa-aaaac-qc7zq-cai.icp0.io/@kayicp
Happy to help with any integration questions here or directly.
previous topic (iilink): https://forum.dfinity.org/t/1-iilink-one-unified-internet-identity-principal-across-ii-powered-apps-mainnet/69048/



