/** * ROB Domains — TypeScript API Client * * A fully-typed, zero-dependency client for the ROB Domains Agent API. * No AI SDK required — direct REST API integration. * * Works with Node.js 18+, Bun, Deno, and any modern JS runtime with fetch. * Copy this file into your project and import as needed. * * Base URL: https://robdn.com */ const BASE_URL = 'https://robdn.com'; // ─── Shared Types ───────────────────────────────────────────────────────────── export interface ResponseMeta { chain: string; chainId: number; timestamp: string; } export interface ApiSuccess { success: true; data: T; meta: ResponseMeta; } export interface ApiError { success: false; code: number; message: string; details?: string; } export type ApiResponse = ApiSuccess | ApiError; export interface SocialLink { platform: | 'twitter' | 'instagram' | 'github' | 'linkedin' | 'youtube' | 'facebook' | 'twitch' | 'discord' | 'telegram' | 'tiktok'; url: string; icon?: string; } export interface CustomLink { title: string; url: string; } export interface ProfileSummary { bio: string; profilePicture: string; coverImage: string; background: string; socialLinks: SocialLink[]; customLinks: CustomLink[]; } export interface TransactionPayload { to: string; data: string; value: string; chainId: number; contractName: string; method: string; description: string; } export interface WorkflowStep { step: number; action: string; description: string; transaction: TransactionPayload | TransactionPayload[]; } // ─── Response Data Types ────────────────────────────────────────────────────── export interface AgentStatusData { status: string; version: string; chain: { name: string; chainId: number; rpcUrl: string; explorerUrl: string; symbol: string; }; contracts: { domainNFT: string; marketplace: string; resolver: string; }; endpoints: string[]; auth: string; docs: string; openapi: string; manifest: string; } export interface DomainSearchData { domain: string; fullDomain: string; available: boolean; owner: string | null; tokenId: string | null; primaryOwner: string | null; profile: ProfileSummary | null; } export interface DomainProfileData { domain: string; fullDomain: string; owner: string; tokenId: string; profile: ProfileSummary; } export interface ReverseData { wallet: string; tokenId: string; domainName: string; fullDomain: string; resolved: boolean; } export interface PrimaryData extends ReverseData { source: string; } export interface UpdateProfileData { tokenId: string; transactionCount: number; note: string | null; transactions: TransactionPayload[]; } export interface ExecutionPlanData { domain: string; fullDomain: string; wallet: string; totalSteps: number; steps: WorkflowStep[]; warning?: string; } // ─── Request Types ──────────────────────────────────────────────────────────── export interface MintRequest { domain: string; } export interface SetPrimaryRequest { tokenId: string; wallet: string; } export interface UpdateProfileRequest { tokenId: string; bio?: string; profilePicture?: string; coverImage?: string; background?: string; socialLinks?: SocialLink[]; customLinks?: CustomLink[]; } export interface RegisterDomainRequest { domain: string; wallet: string; setPrimary?: boolean; profile?: Partial> & { socialLinks?: SocialLink[]; customLinks?: CustomLink[]; }; } // ─── HTTP Helpers ───────────────────────────────────────────────────────────── async function get(path: string): Promise> { const res = await fetch(`${BASE_URL}${path}`); return res.json() as Promise>; } async function post(path: string, body: unknown): Promise> { const res = await fetch(`${BASE_URL}${path}`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body), }); return res.json() as Promise>; } // ─── Read Endpoints ─────────────────────────────────────────────────────────── /** Returns platform status, chain info, and contract addresses. */ export async function getStatus(): Promise> { return get('/agent/status'); } /** * Search for a .rob domain by name. * Accepts the domain with or without the ".rob" suffix. */ export async function searchDomain(domain: string): Promise> { return get(`/agent/search/${encodeURIComponent(domain)}`); } /** Returns the full on-chain profile for a .rob domain. */ export async function getProfile(domain: string): Promise> { return get(`/agent/profile/${encodeURIComponent(domain)}`); } /** Reverse-resolves an Ethereum wallet to its primary .rob domain. */ export async function reverseResolve(wallet: string): Promise> { return get(`/agent/reverse/${encodeURIComponent(wallet)}`); } /** Returns the primary .rob domain set for a wallet via the on-chain resolver. */ export async function getPrimaryDomain(wallet: string): Promise> { return get(`/agent/primary/${encodeURIComponent(wallet)}`); } // ─── Transaction Endpoints ──────────────────────────────────────────────────── /** * Generates an unsigned transaction payload for minting a .rob domain NFT. * Pass the returned payload to the user's wallet for signing and broadcasting. */ export async function buildMintTransaction( request: MintRequest ): Promise> { return post('/agent/tx/mint', request); } /** * Generates an unsigned transaction payload for setting a .rob domain as primary. * Verifies token ownership before returning the payload. */ export async function buildSetPrimaryTransaction( request: SetPrimaryRequest ): Promise> { return post('/agent/tx/set-primary', request); } /** * Generates one or more unsigned transaction payloads for updating a .rob profile. * When socialLinks or customLinks are provided, multiple payloads are returned. * Sign and send them in order. */ export async function buildUpdateProfileTransaction( request: UpdateProfileRequest ): Promise> { return post('/agent/tx/update-profile', request); } // ─── Workflow Endpoint ──────────────────────────────────────────────────────── /** * Returns a complete multi-step execution plan for registering a .rob domain. * Steps: Mint → Set Primary (optional) → Update Profile (optional). * All transaction payloads are included — sign and send each step in order. */ export async function registerDomainWorkflow( request: RegisterDomainRequest ): Promise> { return post('/agent/workflow/register-domain', request); } // ─── Demo ───────────────────────────────────────────────────────────────────── async function main() { // 1. Platform status console.log('─── Status ───'); const status = await getStatus(); if (status.success) { console.log(`Platform: ${status.data.status} | Chain ID: ${status.data.chain.chainId}`); } // 2. Search a domain console.log('\n─── Search ───'); const search = await searchDomain('alice'); if (search.success) { console.log(`"alice.rob" is ${search.data.available ? 'available ✓' : 'taken ✗'}`); if (!search.data.available) { console.log(` Owner: ${search.data.owner}`); console.log(` Token ID: ${search.data.tokenId}`); } } // 3. Reverse resolve console.log('\n─── Reverse Resolve ───'); const reverse = await reverseResolve('0xAbCd1234567890AbCd1234567890AbCd12345678'); if (reverse.success) { console.log( reverse.data.resolved ? `Primary domain: ${reverse.data.fullDomain}` : 'No primary domain set' ); } // 4. Mint transaction console.log('\n─── Mint Transaction ───'); const mintResult = await buildMintTransaction({ domain: 'alice' }); if (mintResult.success) { const tx = mintResult.data; console.log('Unsigned mint payload:'); console.log(JSON.stringify({ to: tx.to, data: tx.data, value: tx.value, chainId: tx.chainId }, null, 2)); console.log('→ Pass this to your wallet for signing and broadcasting.'); } // 5. Full registration workflow console.log('\n─── Full Registration Workflow ───'); const plan = await registerDomainWorkflow({ domain: 'alice', wallet: '0xAbCd1234567890AbCd1234567890AbCd12345678', setPrimary: true, profile: { bio: 'Web3 builder', socialLinks: [{ platform: 'twitter', url: 'https://twitter.com/alice' }], }, }); if (plan.success) { console.log(`Execution plan: ${plan.data.totalSteps} step(s)`); for (const step of plan.data.steps) { console.log(` Step ${step.step}: ${step.action} — ${step.description}`); } } } main().catch(console.error);