From 4c25628bb5a4a5b23cadcd143c03197ee1274b71 Mon Sep 17 00:00:00 2001 From: rama_seltzer Date: Wed, 12 Nov 2025 16:44:59 -0300 Subject: [PATCH 1/3] add: improved UX & DX documentation --- src/content/docs/guides/index.mdx | 50 +- src/content/docs/guides/quickstart.mdx | 401 ++++++++++++++++ src/content/docs/guides/update_fields.mdx | 431 ++++++++++++++++- src/content/docs/index.mdx | 26 +- src/content/docs/reference/index.mdx | 89 +++- src/content/docs/reference/operations.mdx | 540 +++++++++++++++++++++ src/content/docs/reference/resolve.mdx | 316 +++++++++++++ src/content/docs/reference/types.mdx | 545 ++++++++++++++++++++++ src/content/docs/reference/utils.mdx | 488 +++++++++++++++++++ src/content/docs/reference/widget.mdx | 532 ++++++++++++++++++++- 10 files changed, 3404 insertions(+), 14 deletions(-) create mode 100644 src/content/docs/guides/quickstart.mdx create mode 100644 src/content/docs/reference/operations.mdx create mode 100644 src/content/docs/reference/types.mdx create mode 100644 src/content/docs/reference/utils.mdx diff --git a/src/content/docs/guides/index.mdx b/src/content/docs/guides/index.mdx index c84c3c5..e0a85fa 100644 --- a/src/content/docs/guides/index.mdx +++ b/src/content/docs/guides/index.mdx @@ -1,10 +1,54 @@ --- title: Guides -description: Here are some guides to help you get started with Midnames, the .night domain registry. +description: Step-by-step guides for using Midnames and the .night domain registry sidebar: hidden: true --- -Here are some guides to help you get started with Midnames, the .night domain registry. +import { CardGrid, LinkCard } from "@astrojs/starlight/components"; -Look through the list on the left to find a guide that suits your needs. +Step-by-step guides to help you get started with Midnames, the .night domain registry, and the Midnames SDK. + +## Getting Started + + + + + + +## Managing Your Domain + + + + + + +## Specifications + + + + + +## SDK Documentation + +Looking for API documentation? Check out the [SDK Reference](/reference/) section for complete API docs, React components, utility functions, and TypeScript types. diff --git a/src/content/docs/guides/quickstart.mdx b/src/content/docs/guides/quickstart.mdx new file mode 100644 index 0000000..1e4e1f3 --- /dev/null +++ b/src/content/docs/guides/quickstart.mdx @@ -0,0 +1,401 @@ +--- +title: Quick Start +description: Get started with the Midnames SDK in 5 minutes +sidebar: + label: Quick Start + badge: + text: Start here + variant: success +--- + +import { Aside, Steps, Code } from "@astrojs/starlight/components"; + +Get started with the Midnames SDK in minutes. This guide covers installation, basic usage, and common patterns for both DApp and wallet developers. + +## What is Midnames SDK? + +Midnames SDK is a TypeScript library for integrating the Midnight Name Service (MNS) into your applications. It allows you to: + +- **Resolve** `.night` domains to blockchain addresses +- **Display** rich profile information with pre-built React components +- **Manage** domain fields, ownership, and settings (with wallet integration) + + + +## Installation + + + +1. **Install the SDK** + + ```bash + npm install @midnames/sdk + ``` + + For React components, also install peer dependencies: + + ```bash + npm install react react-dom lucide-react + ``` + +2. **Import and start using** + + ```typescript + import { resolveDomain } from '@midnames/sdk'; + + const result = await resolveDomain('alice.night'); + console.log(result.data); // Address or contract + ``` + + + +That's it! The SDK works out of the box with sensible defaults. + +## Basic Examples + +### Example 1: Resolve a Domain + +Translate a `.night` domain to its target address: + +```typescript +import { resolveDomain } from '@midnames/sdk'; + +async function resolveUserDomain(domain: string) { + const result = await resolveDomain(domain); + + if (result.success) { + console.log('Resolved to:', result.data); + // => "mn_shield-cpk_test1..." or "0200..." + } else { + console.error('Resolution failed:', result.error.message); + } +} + +await resolveUserDomain('alice.night'); +``` + +### Example 2: Get Domain Profile + +Fetch complete profile data including fields and metadata: + +```typescript +import { getDomainProfile } from '@midnames/sdk'; + +async function fetchProfile(domain: string) { + const result = await getDomainProfile(domain); + + if (!result.success) { + console.error('Error:', result.error.message); + return; + } + + const profile = result.data; + + console.log('Domain:', profile.fullDomain); + console.log('Owner:', profile.info?.owner); + console.log('Target:', profile.resolvedTarget); + + // Access custom fields + console.log('Name:', profile.fields.get('name')); + console.log('Bio:', profile.fields.get('bio')); + console.log('Twitter:', profile.fields.get('twitter')); +} + +await fetchProfile('alice.night'); +``` + +### Example 3: Display Profile Widget (React) + +Show a beautiful profile card with zero configuration: + +```tsx +import '@midnames/sdk/styles.css'; +import { DomainProfileWidget } from '@midnames/sdk/react/DomainProfileWidget'; + +function ProfilePage() { + return ( +
+

User Profile

+ +
+ ); +} +``` + +That's all you need! The widget automatically: +- Fetches domain data +- Displays avatar, bio, and social links +- Shows ownership information +- Handles errors gracefully + +## Common Use Cases + +### For DApp Developers + +#### Display user names instead of addresses + +```typescript +import { resolveDomain } from '@midnames/sdk'; + +// In your transaction UI +async function displayRecipient(addressOrDomain: string) { + // If it looks like a domain, resolve it + if (addressOrDomain.endsWith('.night')) { + const result = await resolveDomain(addressOrDomain); + if (result.success) { + return { + displayName: addressOrDomain, + address: result.data, + }; + } + } + + return { + displayName: `${addressOrDomain.slice(0, 10)}...`, + address: addressOrDomain, + }; +} + +// Usage +const recipient = await displayRecipient('alice.night'); +console.log(`Sending to ${recipient.displayName} (${recipient.address})`); +``` + +#### Reverse resolution (address โ†’ domain) + +While the SDK doesn't provide direct reverse resolution, you can implement it by storing mappings in your application or querying all domains. + +### For Wallet Developers + +#### Autocomplete domain search + +```typescript +import { getDomainInfo, normalizeDomain } from '@midnames/sdk'; + +async function isDomainAvailable(input: string): Promise { + const domain = normalizeDomain(input); + const result = await getDomainInfo(domain); + + // Domain is available if info is null + return !result.success || result.data.info === null; +} + +// Usage +const available = await isDomainAvailable('alice'); +console.log(available ? 'Available!' : 'Taken'); +``` + +#### Show domain profile in wallet + +```tsx +import { DomainProfileWidget } from '@midnames/sdk/react/DomainProfileWidget'; + +function WalletContactCard({ domain }: { domain: string }) { + return ( + { + showToast(`Copied ${field}`); + }} + /> + ); +} +``` + +## Error Handling + +All SDK functions return a `Result` type for type-safe error handling: + +```typescript +import { resolveDomain } from '@midnames/sdk'; + +const result = await resolveDomain('alice.night'); + +if (result.success) { + // Type: string (the resolved address) + console.log('Success:', result.data); +} else { + // Type: MidnamesError + console.error('Error:', result.error); + console.error('Code:', result.error.code); + console.error('Message:', result.error.message); +} +``` + +**Common error codes:** + +| Code | Meaning | Action | +|------|---------|--------| +| `DOMAIN_NOT_FOUND` | Domain doesn't exist | Show "Available" UI | +| `INVALID_DOMAIN` | Invalid format | Show validation error | +| `NETWORK_ERROR` | Connection failed | Retry or show offline UI | +| `CONTRACT_NOT_FOUND` | Contract not deployed | Handle as not found | + +## Advanced Configuration + +### Custom Provider + +By default, the SDK connects to Midnight TestNet. You can customize this: + +```typescript +import { createDefaultProvider, resolveDomain } from '@midnames/sdk'; +import { NetworkId } from '@midnight-ntwrk/midnight-js-network-id'; + +// Create custom provider +const provider = createDefaultProvider({ + indexerUrl: 'https://your-indexer.example.com/api/v1/graphql', + indexerWsUrl: 'wss://your-indexer.example.com/api/v1/graphql/ws', + networkId: NetworkId.TestNet, +}); + +// Use it +const result = await resolveDomain('alice.night', { provider }); +``` + +### Set Default Provider Globally + +```typescript +import { setDefaultProvider, createDefaultProvider } from '@midnames/sdk'; + +// Set once at app initialization +const provider = createDefaultProvider(); +setDefaultProvider(provider); + +// Now all SDK functions use this provider by default +``` + +## Next Steps + +Now that you know the basics, explore these topics: + +
+ +**๐Ÿ“– Learn More** +- [Domain Resolution](/reference/resolve/) - Complete guide to reading domain data +- [Profile Widget](/reference/widget/) - Customize the React component +- [Domain Operations](/reference/operations/) - Write operations (requires wallet) + +**๐Ÿ› ๏ธ Guides** +- [Buy a .night domain](/guides/buy_domain/) - Get your own domain +- [Update domain fields](/guides/update_fields/) - Customize your profile +- [Transfer domains](/guides/transfer_domain/) - Transfer ownership + +**๐Ÿ“š Reference** +- [Utility Functions](/reference/utils/) - Helper functions for validation +- [MNS Specification](/guides/valid_domains/) - Domain naming rules +- [Types Reference](/reference/types/) - TypeScript type definitions + +
+ +## Example Projects + +### Minimal DApp Integration + +```typescript +// app.ts +import { getDomainProfile } from '@midnames/sdk'; + +async function main() { + const domain = process.argv[2] || 'alice.night'; + const result = await getDomainProfile(domain); + + if (!result.success) { + console.error('Error:', result.error.message); + process.exit(1); + } + + const { fullDomain, resolvedTarget, info, fields } = result.data; + + console.log(` +=== ${fullDomain} === +Owner: ${info?.owner || 'N/A'} +Target: ${resolvedTarget || 'Not set'} +Name: ${fields.get('name') || 'N/A'} +Bio: ${fields.get('bio') || 'N/A'} +Website: ${fields.get('website') || 'N/A'} + `); +} + +main(); +``` + +```bash +# Run it +npx ts-node app.ts alice.night +``` + +### React App with Widget + +```tsx +// App.tsx +import { useState } from 'react'; +import '@midnames/sdk/styles.css'; +import { DomainProfileWidget } from '@midnames/sdk/react/DomainProfileWidget'; + +export default function App() { + const [domain, setDomain] = useState('alice.night'); + + return ( +
+

Midnames Profile Viewer

+ + setDomain(e.target.value)} + placeholder="Enter a .night domain" + style={{ width: '100%', padding: '0.5rem', marginBottom: '1rem' }} + /> + + { + console.error('Widget error:', error); + }} + /> +
+ ); +} +``` + +## Troubleshooting + +### "Domain not found" for valid domains + +- Check that you're using the correct network (TestNet vs MainNet) +- Verify the domain is actually registered at [ns.midnames.com](https://ns.midnames.com) +- Ensure `.night` TLD is included (use `normalizeDomain()` helper) + +### React widget not styling correctly + +- Make sure you imported the CSS: `import '@midnames/sdk/styles.css'` +- Check for CSS conflicts with your app's global styles +- Try wrapping in a container with `className` to isolate styles + +### TypeScript errors + +- Ensure you have `@midnight-ntwrk` peer dependencies installed +- Check your `tsconfig.json` has `"moduleResolution": "bundler"` or `"node"` +- Import types explicitly if needed: `import type { DomainProfileData } from '@midnames/sdk'` + +### Network connection issues + +- Verify your internet connection +- Check if the indexer is accessible (default: `https://indexer.testnet-02.midnight.network`) +- Try with a custom provider to rule out default configuration issues + +## Community & Support + +- **Documentation**: [https://docs.midnames.com](/) +- **GitHub**: [https://github.com/midnames/sdk](https://github.com/midnames/sdk) +- **Issues**: [Report bugs](https://github.com/midnames/sdk/issues) + +Ready to dive deeper? Check out the [complete API reference](/reference/) or explore specific [guides](/guides/). diff --git a/src/content/docs/guides/update_fields.mdx b/src/content/docs/guides/update_fields.mdx index 58eefed..c2bcb5d 100644 --- a/src/content/docs/guides/update_fields.mdx +++ b/src/content/docs/guides/update_fields.mdx @@ -1,6 +1,431 @@ --- -title: Update fields in a domain -description: Learn how to update the records in a domain in Midnight +title: Update Domain Fields +description: Learn how to update custom fields in your .night domain sidebar: - label: Update fields in a domain + label: Update Domain Fields --- + +import { Aside, Steps } from "@astrojs/starlight/components"; + +Learn how to update custom fields in your `.night` domain using the Midnames SDK. Fields allow you to store profile information, social links, and metadata on-chain. + +## What are Domain Fields? + +Domain fields are key-value pairs stored in your domain's contract. They allow you to: + +- Store profile information (name, bio, avatar, banner) +- Add social media links (Twitter, GitHub, website, etc.) +- Set privacy settings (encryption public key) +- Store custom metadata specific to your application + + + +## Standard Fields + +These fields are recognized across the Midnames ecosystem: + +| Field | Purpose | Example | +| ---------- | -------------------------------- | ------------------------- | +| `name` | Display name | `"Alice Cooper"` | +| `bio` | Short biography | `"Web3 Developer"` | +| `avatar` | Profile picture URL | `"https://..."` | +| `banner` | Banner image URL | `"https://..."` | +| `website` | Personal website | `"https://alice.dev"` | +| `twitter` | Twitter handle | `"@alice"` or `"alice"` | +| `github` | GitHub username | `"alice"` | +| `location` | Location | `"San Francisco"` | +| `epk` | Encryption public key | `"mn_shield-epk_test..."` | +| `email` | Email address | `"alice@example.com"` | +| `discord` | Discord handle | `"alice#1234"` | +| `telegram` | Telegram username | `"@alice"` | + + + +## Prerequisites + +To update fields, you need: + +1. **Lace Wallet** installed and funded ([Get Lace](https://docs.midnight.network/develop/tutorial/using/chrome-ext)) +2. **Domain ownership** - You must own the domain to update its fields +3. **SDK setup** with wallet providers (see [Domain Operations](/reference/operations/#prerequisites)) + +## Update a Single Field + +### Using the SDK + +```typescript +import { insertField } from '@midnames/sdk'; + +async function updateBio(domain: string, providers) { + const result = await insertField( + domain, + 'bio', + 'Web3 Developer | Midnight Enthusiast', + providers + ); + + if (result.success) { + console.log('Field updated!', result.data.transactionId); + } else { + console.error('Update failed:', result.error.message); + } +} + +await updateBio('alice.night', providers); +``` + +### Common Updates + +```typescript +// Update profile name +await insertField('alice.night', 'name', 'Alice Cooper', providers); + +// Set avatar +await insertField('alice.night', 'avatar', 'https://avatars.example.com/alice.png', providers); + +// Add Twitter +await insertField('alice.night', 'twitter', '@alice', providers); + +// Add website +await insertField('alice.night', 'website', 'https://alice.dev', providers); + +// Set encryption key +await insertField('alice.night', 'epk', 'mn_shield-epk_test1...', providers); +``` + +## Update Multiple Fields + +For efficiency, update multiple fields in a single transaction: + +```typescript +import { addMultipleFields } from '@midnames/sdk'; + +async function setupProfile(domain: string, providers) { + const fields: Array<[string, string]> = [ + ['name', 'Alice Cooper'], + ['bio', 'Web3 Developer | Midnight Enthusiast'], + ['avatar', 'https://avatars.example.com/alice.png'], + ['website', 'https://alice.dev'], + ['twitter', '@alice'], + ['github', 'alice'], + ['location', 'San Francisco'], + ]; + + const result = await addMultipleFields(domain, fields, providers); + + if (result.success) { + console.log('Profile setup complete!', result.data.transactionId); + } else { + console.error('Setup failed:', result.error.message); + } +} + +await setupProfile('alice.night', providers); +``` + + + +## Remove Fields + +### Remove a specific field + +```typescript +import { clearField } from '@midnames/sdk'; + +// Remove Twitter link +const result = await clearField('alice.night', 'twitter', providers); + +if (result.success) { + console.log('Field removed:', result.data.transactionId); +} +``` + +### Remove all fields + +```typescript +import { clearAllFields } from '@midnames/sdk'; + +// Clear entire profile +const result = await clearAllFields('alice.night', providers); + +if (result.success) { + console.log('All fields cleared:', result.data.transactionId); +} +``` + + + +## Complete Example: Profile Manager + +Here's a full React component for managing domain fields: + +```tsx +import { useState } from 'react'; +import { insertField, getDomainProfile } from '@midnames/sdk'; +import type { ContractProviders } from '@midnight-ntwrk/midnight-js-contracts'; + +function ProfileEditor({ domain, providers }: { + domain: string; + providers: ContractProviders; +}) { + const [name, setName] = useState(''); + const [bio, setBio] = useState(''); + const [twitter, setTwitter] = useState(''); + const [loading, setLoading] = useState(false); + + const handleSave = async () => { + setLoading(true); + + try { + // Update each field + if (name) { + const result = await insertField(domain, 'name', name, providers); + if (!result.success) throw result.error; + } + + if (bio) { + const result = await insertField(domain, 'bio', bio, providers); + if (!result.success) throw result.error; + } + + if (twitter) { + const result = await insertField(domain, 'twitter', twitter, providers); + if (!result.success) throw result.error; + } + + alert('Profile updated successfully!'); + } catch (error) { + console.error('Update failed:', error); + alert('Update failed. See console for details.'); + } finally { + setLoading(false); + } + }; + + return ( +
+

Edit Profile: {domain}

+ +
+ + setName(e.target.value)} + placeholder="Your display name" + style={{ width: '100%', padding: '0.5rem' }} + /> +
+ +
+ +