diff --git a/package.json b/package.json index 3ea978b..16eed46 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,7 @@ "connectkit": "^1.8.2", "connectkit-next-siwe": "^0.3.0", "iron-session": "^8.0.3", + "namestone-sdk": "^0.2.8", "next": "14.2.6", "next-safe-action": "^7.7.1", "pinata-web3": "^0.4.1", diff --git a/src/actions/create.ts b/src/actions/create.ts index d53eecd..bcbe40b 100644 --- a/src/actions/create.ts +++ b/src/actions/create.ts @@ -7,7 +7,8 @@ import { zfd } from 'zod-form-data' import { actionClient } from '@/actions/client' import { isAllowlisted } from '@/lib/allowlist' -import { namestoneFetch } from '@/lib/namestone' +import { namestone } from '@/lib/namestone' +import { extractErrorMessage } from '@/lib/utils' import { NamestoneProfileSchema } from '@/types/namestone' const formSchema = zfd.formData(NamestoneProfileSchema) @@ -47,15 +48,13 @@ export const createName = actionClient return { error: 'Name must be 3 - 12 alphanumeric characters' } } - const res = await namestoneFetch<{ success?: boolean; error?: string }>({ - path: 'claim-name?single_claim=1', - method: 'POST', - body: profile, - }) - - if (res.error) { - return { error: res.error } + try { + await namestone.claimName({ ...profile, single_claim: 1 }) + return { success: true } + } catch (err) { + return { + error: + err instanceof Error ? extractErrorMessage(err) : 'Unknown error', + } } - - return res }) diff --git a/src/actions/update.ts b/src/actions/update.ts index 4e7a0e7..c213e5c 100644 --- a/src/actions/update.ts +++ b/src/actions/update.ts @@ -8,8 +8,9 @@ import { z } from 'zod' import { zfd } from 'zod-form-data' import { actionClient } from '@/actions/client' -import { namestoneFetch, parentDomain } from '@/lib/namestone' -import { NamestoneProfile, NamestoneProfileSchema } from '@/types/namestone' +import { namestone, parentDomain } from '@/lib/namestone' +import { extractErrorMessage } from '@/lib/utils' +import { NamestoneProfileSchema } from '@/types/namestone' const noSpaceString = z.string().refine((value) => !value.includes(' ')) @@ -42,8 +43,9 @@ export const updateName = actionClient } // Check if the name exists - const namesByAddress = await namestoneFetch({ - path: `get-names?domain=${parentDomain}&address=${profile.address}`, + const namesByAddress = await namestone.getNames({ + domain: parentDomain, + address: profile.address, }) const nameExists = namesByAddress.find((name) => name.name === profile.name) @@ -53,25 +55,23 @@ export const updateName = actionClient return { error: 'Name does not exist, or you do not own this name.' } } - // If the name exists, update it - const res = await namestoneFetch<{ success?: boolean; error?: string }>({ - path: 'set-name', - method: 'POST', - body: { + try { + // If the name exists, update it + await namestone.setName({ ...profile, text_records: { avatar: profile.avatar || '', 'com.twitter': profile.twitter || '', 'org.telegram': profile.telegram || '', }, - }, - }) + }) - if (res.error) { - return { error: res.error } + revalidatePath('/') + return { success: true } + } catch (err) { + return { + error: + err instanceof Error ? extractErrorMessage(err) : 'Unknown error', + } } - - revalidatePath('/') - - return res }) diff --git a/src/app/api/names/route.ts b/src/app/api/names/route.ts index 775e265..b38f161 100644 --- a/src/app/api/names/route.ts +++ b/src/app/api/names/route.ts @@ -1,23 +1,16 @@ import { NextRequest, NextResponse } from 'next/server' -import { namestoneFetch, parentDomain } from '@/lib/namestone' +import { namestone, parentDomain } from '@/lib/namestone' export async function GET(req: NextRequest) { const searchParams = req.nextUrl.searchParams const offset = searchParams.get('offset') || '0' const address = searchParams.get('address') - const queryParams = new URLSearchParams({ + const names = await namestone.getNames({ domain: parentDomain, - offset: offset, - }) - - if (address) { - queryParams.append('address', address) - } - - const names = await namestoneFetch({ - path: `get-names?${queryParams.toString()}`, + offset: Number(offset ?? 0), + address: address ?? undefined, }) return NextResponse.json(names) diff --git a/src/app/page.tsx b/src/app/page.tsx index e295e9d..a63cb8d 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -5,13 +5,10 @@ import { XIcon } from '@/components/Icons' import { NameManager } from '@/components/NameManager' import { ProfileCard } from '@/components/ProfileCard' import { Squiggle } from '@/components/Squiggle' -import { namestoneFetch, parentDomain } from '@/lib/namestone' -import { NamestoneProfile } from '@/types/namestone' +import { namestone, parentDomain } from '@/lib/namestone' export default async function Home() { - const profiles = await namestoneFetch({ - path: `get-names?domain=${parentDomain}`, - }) + const profiles = await namestone.getNames({ domain: parentDomain }) return (
diff --git a/src/components/ProfileCard.tsx b/src/components/ProfileCard.tsx index d4a7e71..6d1157d 100644 --- a/src/components/ProfileCard.tsx +++ b/src/components/ProfileCard.tsx @@ -1,16 +1,15 @@ 'use client' +import { NameData } from 'namestone-sdk' import Image from 'next/image' import { useEffect, useState } from 'react' -import { NamestoneProfile } from '@/types/namestone' - import { TelegramIcon, XIcon } from './Icons' const transparentImage = 'data:image/svg+xml,%3Csvg xmlns="http://www.w3.org/2000/svg"%3E%3C/svg%3E' -export function ProfileCard({ profile }: { profile: NamestoneProfile }) { +export function ProfileCard({ profile }: { profile: NameData }) { const [avatarUrl, setAvatarUrl] = useState() const twitter = profile.text_records?.['com.twitter'] const telegram = profile.text_records?.['org.telegram'] diff --git a/src/hooks/useNamestone.ts b/src/hooks/useNamestone.ts index b1708d5..f66f737 100644 --- a/src/hooks/useNamestone.ts +++ b/src/hooks/useNamestone.ts @@ -1,8 +1,7 @@ import { useQuery } from '@tanstack/react-query' +import { NameData } from 'namestone-sdk' import { Address } from 'viem' -import { NamestoneProfile } from '@/types/namestone' - export function useNamestone(address?: Address) { return useQuery({ queryKey: ['namestone', address], @@ -13,7 +12,7 @@ export function useNamestone(address?: Address) { } const res = await fetch(`/api/names?${queryParams.toString()}`) - const json = (await res.json()) as NamestoneProfile[] + const json = (await res.json()) as NameData[] if (address) { return { first: json[0], all: json } } diff --git a/src/lib/namestone.ts b/src/lib/namestone.ts index b2fc810..fd7301c 100644 --- a/src/lib/namestone.ts +++ b/src/lib/namestone.ts @@ -1,40 +1,7 @@ -import { NamestoneProfile } from '@/types/namestone' +import NameStone from 'namestone-sdk' -const NAMESTONE_API_KEY = process.env.NAMESTONE_API_KEY +const NAMESTONE_API_KEY = process.env.NAMESTONE_API_KEY as string export const parentDomain = 'shefi.eth' -export type Props = { - path: string - options?: RequestInit - method?: 'GET' | 'POST' - body?: NamestoneProfile -} - -export async function namestoneFetch({ - path, - options = {}, - method = 'GET', - body, -}: Props) { - if (!NAMESTONE_API_KEY) { - throw new Error('NAMESTONE_API_KEY is not set') - } - - const res = await fetch('https://namestone.xyz/api/public_v1/' + path, { - ...options, - method, - headers: { - ...options.headers, - Authorization: NAMESTONE_API_KEY, - 'Content-Type': 'application/json', - }, - body: JSON.stringify(body), - next: { - revalidate: 0, - }, - }) - - const data = await res.json() - return data as T -} +export const namestone = new NameStone(NAMESTONE_API_KEY) diff --git a/src/lib/utils.ts b/src/lib/utils.ts index 78f4547..a1d4eb8 100644 --- a/src/lib/utils.ts +++ b/src/lib/utils.ts @@ -8,3 +8,23 @@ export function cn(...inputs: ClassValue[]) { export function truncateAddress(address: string) { return `${address.slice(0, 6)}...${address.slice(-4)}` } + +export function extractErrorMessage(error: Error) { + const message = error.message + + // Try to parse the message from the error string + const regex = /message:\s*(.+)/ // Match the message part + const match = regex.exec(message) + + if (match && match[1]) { + // If a match is found, parse the JSON error object + try { + const errorObject = JSON.parse(match[1]) + return errorObject.error // Return just the error message + } catch (e) { + return match[1] // Return the original message if parsing fails + } + } + + return error // Fallback to the original error if no message is found +} diff --git a/src/types/namestone.ts b/src/types/namestone.ts index 05d160a..624d417 100644 --- a/src/types/namestone.ts +++ b/src/types/namestone.ts @@ -7,5 +7,3 @@ export const NamestoneProfileSchema = z.object({ text_records: z.record(z.string(), z.string()).optional(), coin_types: z.record(z.string(), z.string()).optional(), }) - -export type NamestoneProfile = z.infer diff --git a/yarn.lock b/yarn.lock index 8f77eee..652b737 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3083,6 +3083,11 @@ mz@^2.7.0: object-assign "^4.0.1" thenify-all "^1.0.0" +namestone-sdk@^0.2.8: + version "0.2.8" + resolved "https://registry.yarnpkg.com/namestone-sdk/-/namestone-sdk-0.2.8.tgz#21770fffa5c75ebbcc5d1c9ae482a23e040375a1" + integrity sha512-qFKpW4zyUxORu5kBbKoV674+jwm96qYBwH2JTSk50R3w38B6axykzrCNSnH0pgE59+89Vk5epzqkcieUA81ZaQ== + nanoid@^3.3.6, nanoid@^3.3.7: version "3.3.7" resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.7.tgz#d0c301a691bc8d54efa0a2226ccf3fe2fd656bd8"