A live demonstration of CIP-???? on-chain handle resolution.
No provider API involved β handles resolve directly from the Cardano blockchain
via Koios, with zero trust in any provider infrastructure.
π
On-chain resolution only. This demo queries the Cardano blockchain directly via Koios. The provider's API server is never contacted. The NFT holder address IS the resolved address β enforced by Cardano itself.
STEP 1
Load Registry
registry.json
STEP 2
Detect Namespace
string match
STEP 3
Query Blockchain
Koios RPC
STEP 4
Show Result
address + policy
π Step 1 β Registry
The wallet loads registry.json once. All providers and their Policy IDs discovered automatically. No API keys needed β Policy IDs are public.
Enter a handle minted on GetMyID. The algorithm detects the namespace, constructs the Cardano asset ID, and queries the blockchain directly for the NFT holder.
// On-chain resolution trace will appear here
// Type a handle above and click Resolve On-Chain
π» Complete Implementation
The full on-chain resolver in ~50 lines. This is all a wallet needs β no provider API, no trust in any server, pure blockchain query.
// Step 1: Load registry once at startupconst registry = await fetch('registry.json').then(r => r.json());
// Step 2: Namespace detection (pure string ops, no network)function detectProviders(input, registry) {
const EXCLUDED = /[?&%"'<>()\[\]{},\^`\s]/;
const norm = input.trim().toLowerCase();
const candidates = [];
// Check prefix providersfor (const p of registry.filter(r => r.namespace_type === 'prefix'))
if (norm.startsWith(p.namespace)) candidates.push(p);
// Find suffix from last non-excluded delimiterconst stripped = candidates[0]
? norm.slice(candidates[0].namespace.length) : norm;
let lastDelim = -1;
for (let i = stripped.length-1; i >= 0; i--) {
if (!stripped[i].match(/[a-z0-9]/) && !EXCLUDED.test(stripped[i]))
{ lastDelim = i; break; }
}
if (lastDelim > -1) {
const suffix = stripped.slice(lastDelim);
for (const p of registry.filter(r => r.namespace_type === 'suffix' && r.namespace === suffix))
candidates.push(p);
}
// Always add bare namespace providers as fallback
candidates.push(...registry.filter(r => r.namespace_type === 'bare'));
return [...new Set(candidates)];
}
// Step 3: On-chain resolution via Koios (no API key needed)async function resolveOnChain(handle, policyId, network) {
const assetHex = [...handle].map(c =>
c.charCodeAt(0).toString(16).padStart(2,'0')).join('');
const base = network === 'mainnet'
? 'https://api.koios.rest/api/v1'
: 'https://preprod.koios.rest/api/v1';
const resp = await fetch(
`${base}/asset_addresses?_asset_policy=${policyId}&_asset_name=${assetHex}`,
{ headers: { Accept: 'application/json' } }
);
const data = await resp.json();
return data?.find(e => parseInt(e.quantity) > 0)?.payment_address || null;
}
// Step 4: Display address + provider to user
showResult(address, provider.provider, provider.policy_ids[network]);