diff --git a/src/@components/Address/AddressAvatarWithChainName.tsx b/src/@components/Address/AddressAvatarWithChainName.tsx index 69ed63a66..27d63cd43 100644 --- a/src/@components/Address/AddressAvatarWithChainName.tsx +++ b/src/@components/Address/AddressAvatarWithChainName.tsx @@ -161,13 +161,11 @@ export const AddressAvatarWithChainName = memo(({ const chainNameClass = variant === 'feed' ? [ 'chain-name text-[14px] md:text-sm font-bold', - 'bg-gradient-to-r from-[var(--neon-teal)] via-[var(--neon-teal)] to-teal-300', - 'bg-clip-text text-transparent', + 'text-[var(--standard-font-color)]', ].join(' ') : [ 'chain-name text-[14px] md:text-[15px] font-bold', - 'bg-gradient-to-r from-[var(--neon-teal)] via-[var(--neon-teal)] to-teal-300', - 'bg-clip-text text-transparent', + 'text-[var(--standard-font-color)]', ].join(' '); return (
@@ -179,8 +177,7 @@ export const AddressAvatarWithChainName = memo(({ @@ -192,7 +189,7 @@ export const AddressAvatarWithChainName = memo(({ return ( {preferredName} - + diff --git a/src/api/backend.ts b/src/api/backend.ts index 5dedb3378..62ad86d20 100644 --- a/src/api/backend.ts +++ b/src/api/backend.ts @@ -68,6 +68,40 @@ export type XAttestationResponse = { signature_base64: string; }; +export type ChainNameChallengeResponse = { + nonce: string; + expires_at: string | number; + message: string; +}; + +export type ChainNameClaimRequest = { + address: string; + name: string; + challenge_nonce: string; + challenge_expires_at: string; + signature_hex: string; +}; + +export type ChainNameClaimResponse = { + status: string; + message?: string | null; +}; + +export type ChainNameClaimStatusResponse = { + status: string; + name?: string | null; + error?: string | null; + preclaim_tx_hash?: string | null; + claim_tx_hash?: string | null; + update_tx_hash?: string | null; + transfer_tx_hash?: string | null; + expires_at?: string | number | null; + approximate_expire_time?: string | number | null; + approximateExpireTime?: string | number | null; + expire_time?: string | number | null; + expireTime?: string | number | null; +}; + // Superhero API client export const SuperheroApi = { async fetchJson(path: string, init?: RequestInit) { @@ -376,6 +410,29 @@ export const SuperheroApi = { }), }) as Promise; }, + createChainNameChallenge(address: string) { + return this.fetchJson('/api/profile/chain-name/challenge', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + address, + }), + }) as Promise; + }, + claimChainName(payload: ChainNameClaimRequest) { + return this.fetchJson('/api/profile/chain-name/claim', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(payload), + }) as Promise; + }, + getChainNameClaimStatus(address: string) { + return this.fetchJson(`/api/profile/${encodeURIComponent(address)}/chain-name-claim`) as Promise; + }, /** Exchange OAuth code (from X redirect) for attestation; backend exchanges code for token and creates attestation. */ createXAttestationFromCode( address: string, diff --git a/src/components/Trendminer/TokenChat.tsx b/src/components/Trendminer/TokenChat.tsx index 5fe574c36..fbc086789 100644 --- a/src/components/Trendminer/TokenChat.tsx +++ b/src/components/Trendminer/TokenChat.tsx @@ -131,12 +131,12 @@ const AddCommentCTA = ({ token }: { token: { name: string; address: string } }) href={qualiPublicUrl} target="_blank" rel="noopener noreferrer" - className="group no-underline rounded-xl border border-white/15 bg-white/[0.05] p-3 text-left transition-all duration-200 hover:-translate-y-0.5 hover:bg-white/[0.09] focus:outline-none focus:ring-2 focus:ring-white/30 no-gradient-text" + className="group no-underline rounded-xl border border-white/15 bg-white/[0.05] p-3 text-left transition-all duration-200 hover:-translate-y-0.5 hover:bg-white/[0.09] focus:outline-none focus:ring-2 focus:ring-white/30" title="Open the public chat on Quali.chat" >
- 🌐 + 🌐
@@ -152,12 +152,12 @@ const AddCommentCTA = ({ token }: { token: { name: string; address: string } }) href={qualiPrivateUrl} target="_blank" rel="noopener noreferrer" - className="group no-underline rounded-xl border border-white/15 bg-white/[0.05] p-3 text-left transition-all duration-200 hover:-translate-y-0.5 hover:bg-white/[0.09] focus:outline-none focus:ring-2 focus:ring-white/30 no-gradient-text" + className="group no-underline rounded-xl border border-white/15 bg-white/[0.05] p-3 text-left transition-all duration-200 hover:-translate-y-0.5 hover:bg-white/[0.09] focus:outline-none focus:ring-2 focus:ring-white/30" title="Open the private chat on Quali.chat (holders only)" >
- 🔒 + 🔒
diff --git a/src/components/Truncate.tsx b/src/components/Truncate.tsx index 9700d0782..d4ea0efbd 100644 --- a/src/components/Truncate.tsx +++ b/src/components/Truncate.tsx @@ -87,7 +87,7 @@ export const Truncate = ({ '--animation-delay': '1s !important', } as React.CSSProperties : undefined} > -
{nameComponent}
+
{nameComponent}
{nameComponent !== str && ( diff --git a/src/components/dex/core/LiquiditySuccessNotification.tsx b/src/components/dex/core/LiquiditySuccessNotification.tsx index a5dbd3bbf..fe303c159 100644 --- a/src/components/dex/core/LiquiditySuccessNotification.tsx +++ b/src/components/dex/core/LiquiditySuccessNotification.tsx @@ -72,7 +72,7 @@ export default function LiquiditySuccessNotification({
{/* Title */} - + Liquidity Added Successfully! diff --git a/src/components/dex/core/SwapConfirmation.tsx b/src/components/dex/core/SwapConfirmation.tsx index af4a9b393..521489250 100644 --- a/src/components/dex/core/SwapConfirmation.tsx +++ b/src/components/dex/core/SwapConfirmation.tsx @@ -69,7 +69,7 @@ export default function SwapConfirmation({ {/* Header */}
- + Confirm Swap diff --git a/src/components/dex/core/SwapForm.tsx b/src/components/dex/core/SwapForm.tsx index 8fe1d1712..74c55ef9b 100644 --- a/src/components/dex/core/SwapForm.tsx +++ b/src/components/dex/core/SwapForm.tsx @@ -356,7 +356,7 @@ export default function SwapForm({ onPairSelected, onFromTokenSelected }: SwapFo
{/* Header */}
-

+

{t('swap.title')}

diff --git a/src/components/dex/core/TokenSelector.tsx b/src/components/dex/core/TokenSelector.tsx index 12b75eb57..d4a455d2f 100644 --- a/src/components/dex/core/TokenSelector.tsx +++ b/src/components/dex/core/TokenSelector.tsx @@ -138,7 +138,7 @@ export default function TokenSelector({ {/* Header */}
- + Select a token diff --git a/src/components/hero-banner/banner.styles.css b/src/components/hero-banner/banner.styles.css index d08d6b6b4..723b4b85e 100644 --- a/src/components/hero-banner/banner.styles.css +++ b/src/components/hero-banner/banner.styles.css @@ -151,10 +151,6 @@ html.ios-webkit .hero-banner--ios-safe .banner-dismiss { color: #ffffff !important; text-shadow: 0 2px 16px rgba(0, 0, 0, 0.35); animation: fadeUp 0.9s ease both 0.05s; - background: none !important; - -webkit-background-clip: unset !important; - background-clip: unset !important; - -webkit-text-fill-color: #ffffff !important; } .mobile-break { @@ -260,10 +256,6 @@ html.ios-webkit .hero-banner--ios-safe .banner-dismiss { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; border: none; - /* Override global link styles */ - -webkit-background-clip: unset !important; - background-clip: unset !important; - -webkit-text-fill-color: unset !important; filter: none !important; } diff --git a/src/components/layout/app-header/MobileAppHeader.tsx b/src/components/layout/app-header/MobileAppHeader.tsx index 97b68ee13..1c6f476a7 100644 --- a/src/components/layout/app-header/MobileAppHeader.tsx +++ b/src/components/layout/app-header/MobileAppHeader.tsx @@ -203,7 +203,7 @@ const MobileAppHeader = () => { ) : ( <> - +
@@ -268,9 +268,6 @@ const MobileAppHeader = () => { style={{ color: 'rgba(255,255,255,0.8)', background: 'transparent', - backgroundImage: 'none', - WebkitTextFillColor: 'rgba(255,255,255,0.8)', - WebkitBackgroundClip: 'initial' as any, }} > {t('labels.menu')} @@ -343,7 +340,7 @@ const MobileAppHeader = () => { target="_blank" rel="noreferrer" className={`${commonClasses} bg-transparent`} - style={{ WebkitTextFillColor: 'white', WebkitBackgroundClip: 'initial' as any, background: 'none' }} + style={{ background: 'none' }} onClick={handleNavigationClick} > {item.label} @@ -355,7 +352,7 @@ const MobileAppHeader = () => { to={item.path} onClick={handleNavigationClick} className={`${commonClasses} bg-transparent`} - style={{ WebkitTextFillColor: 'white', WebkitBackgroundClip: 'initial' as any, background: 'none' }} + style={{ background: 'none' }} > {item.label} @@ -377,7 +374,7 @@ const MobileAppHeader = () => { to="/defi/buy-ae-with-eth" onClick={handleNavigationClick} className="w-full no-underline font-semibold transition-all duration-200 h-[56px] sm:h-[52px] rounded-xl text-white text-base flex items-center justify-center px-5 bg-transparent" - style={{ WebkitTextFillColor: 'white', WebkitBackgroundClip: 'initial' as any, background: 'none' }} + style={{ background: 'none' }} > {t('labels.buyAe')} diff --git a/src/components/layout/app-header/WebAppHeader.tsx b/src/components/layout/app-header/WebAppHeader.tsx index a5094380f..3a979a1c5 100644 --- a/src/components/layout/app-header/WebAppHeader.tsx +++ b/src/components/layout/app-header/WebAppHeader.tsx @@ -59,7 +59,7 @@ const WebAppHeader = () => {
@@ -88,7 +88,7 @@ const WebAppHeader = () => { +
+ {isTooShort && ( +

+ {normalizedValueLength} + /13 characters before `.chain` +

+ )} +
+ {error && ( +
+

{error}

+
+ )} +
+ + + ); +}; + +export default ClaimChainNameModal; diff --git a/src/components/modals/ConnectWalletModal.tsx b/src/components/modals/ConnectWalletModal.tsx index fb25bc825..94bf24016 100644 --- a/src/components/modals/ConnectWalletModal.tsx +++ b/src/components/modals/ConnectWalletModal.tsx @@ -75,7 +75,7 @@ const ConnectWalletModal = ({ onClose }: Props) => { {' '} Terms of Use @@ -84,7 +84,7 @@ const ConnectWalletModal = ({ onClose }: Props) => { {' '} Privacy Policy @@ -102,7 +102,7 @@ const ConnectWalletModal = ({ onClose }: Props) => { {' '} Terms of Use @@ -111,7 +111,7 @@ const ConnectWalletModal = ({ onClose }: Props) => { {' '} Privacy Policy diff --git a/src/components/modals/__tests__/ClaimChainNameModal.test.tsx b/src/components/modals/__tests__/ClaimChainNameModal.test.tsx new file mode 100644 index 000000000..15ea1159e --- /dev/null +++ b/src/components/modals/__tests__/ClaimChainNameModal.test.tsx @@ -0,0 +1,93 @@ +import { describe, expect, it } from 'vitest'; +import { resolveClaimErrorMessage } from '../ClaimChainNameModal'; + +const messages: Record = { + 'messages.tooManyRequests': 'Too many requests. Please wait and try again.', + 'messages.connectWalletToClaimChainName': 'Connect your wallet to claim a .chain name', + 'messages.chainNameClaimTimedOut': 'The claim is still processing. Please check back in a moment.', + 'messages.chainNameClaimWalletUnavailable': 'Wallet message signing is not available right now. Please try again.', + 'messages.chainNameClaimNameTaken': 'That .chain name is already taken. Try another one.', + 'messages.chainNameClaimNameInProgress': 'That .chain name is currently being claimed. Try another one.', + 'messages.chainNameClaimAddressInProgress': 'You already have a sponsored .chain claim in progress.', + 'messages.chainNameClaimAddressClaimed': 'This wallet already has a sponsored .chain name.', + 'messages.chainNameClaimChallengeExpired': 'Your claim session expired. Please try again.', + 'messages.chainNameClaimVerificationFailed': 'We could not verify your wallet signature. Please try again.', + 'messages.chainNameClaimUnavailable': 'Sponsored .chain claiming is temporarily unavailable. Please try again later.', + 'messages.chainNameClaimTooShort': 'Sponsored claims require more than 12 characters before .chain.', + 'messages.chainNameClaimRetry': 'We could not start the .chain claim. Please try again.', + 'messages.chainNameClaimFailed': 'Failed to claim .chain name.', +}; + +const t = (key: string) => messages[key] || key; + +describe('resolveClaimErrorMessage', () => { + it('maps name availability conflicts to friendly copy', () => { + expect(resolveClaimErrorMessage( + new Error('Superhero API error (400): This name is already taken on-chain'), + t, + )).toBe(messages['messages.chainNameClaimNameTaken']); + + expect(resolveClaimErrorMessage( + new Error('Superhero API error (409): This name is already being claimed by another address'), + t, + )).toBe(messages['messages.chainNameClaimNameInProgress']); + }); + + it('maps address-specific conflicts to friendly copy', () => { + expect(resolveClaimErrorMessage( + new Error('Superhero API error (409): Address already has an in-progress chain name claim: abc.chain'), + t, + )).toBe(messages['messages.chainNameClaimAddressInProgress']); + + expect(resolveClaimErrorMessage( + new Error('Superhero API error (409): Address already has a claimed chain name: abc.chain'), + t, + )).toBe(messages['messages.chainNameClaimAddressClaimed']); + }); + + it('maps challenge and signature issues to retryable copy', () => { + expect(resolveClaimErrorMessage( + new Error('Superhero API error (400): Challenge has expired'), + t, + )).toBe(messages['messages.chainNameClaimChallengeExpired']); + + expect(resolveClaimErrorMessage( + new Error('Superhero API error (400): Invalid challenge signature'), + t, + )).toBe(messages['messages.chainNameClaimVerificationFailed']); + + expect(resolveClaimErrorMessage( + new Error('Superhero API error (400): name shorter than 13'), + t, + )).toBe(messages['messages.chainNameClaimTooShort']); + }); + + it('maps temporary backend availability issues to generic unavailable copy', () => { + expect(resolveClaimErrorMessage( + new Error('Superhero API error (503): Chain name claiming is temporarily unavailable due to insufficient sponsor funds'), + t, + )).toBe(messages['messages.chainNameClaimUnavailable']); + + expect(resolveClaimErrorMessage( + new Error('Superhero API error (503): Unable to verify chain name availability right now'), + t, + )).toBe(messages['messages.chainNameClaimUnavailable']); + }); + + it('maps rate limits and unknown backend errors safely', () => { + expect(resolveClaimErrorMessage( + new Error('Superhero API error (429): Too many requests'), + t, + )).toBe(messages['messages.tooManyRequests']); + + expect(resolveClaimErrorMessage( + new Error('Superhero API error (400): Invalid address'), + t, + )).toBe(messages['messages.chainNameClaimRetry']); + + expect(resolveClaimErrorMessage( + new Error('Superhero API error (500): unexpected internal stack trace'), + t, + )).toBe(messages['messages.chainNameClaimFailed']); + }); +}); diff --git a/src/components/social/PostHashtagLink.tsx b/src/components/social/PostHashtagLink.tsx index f0d0fd7d0..070ee0009 100644 --- a/src/components/social/PostHashtagLink.tsx +++ b/src/components/social/PostHashtagLink.tsx @@ -107,13 +107,7 @@ const PostHashtagLink = ({ : 'inline-flex items-baseline gap-1 text-[var(--neon-blue)] underline-offset-2 hover:underline text-[13px] font-medium', 'no-underline outline-none focus:outline-none focus-visible:outline-none focus-visible:ring-0 break-words', )} - style={{ - outline: 'none', - background: 'none', - WebkitTextFillColor: 'currentColor', - WebkitBackgroundClip: 'initial', - backgroundClip: 'initial', - }} + style={{ outline: 'none' }} onClick={(e) => e.stopPropagation()} > {label} diff --git a/src/config.ts b/src/config.ts index a4c8b8bbe..629c5c048 100644 --- a/src/config.ts +++ b/src/config.ts @@ -90,12 +90,12 @@ export const NETWORKS: Record = { ae_mainnet: { name: 'Mainnet', NETWORK: 'ae_mainnet', - BACKEND_URL: 'https://api.superhero.com', - SUPERHERO_API_URL: 'https://api.superhero.com', + BACKEND_URL: 'https://api.dev.tokensale.org', + SUPERHERO_API_URL: 'https://api.dev.tokensale.org', NODE_URL: 'https://mdw.wordcraft.fun', MIDDLEWARE_URL: 'https://mdw.wordcraft.fun/mdw', EXPLORER_URL: 'https://aescan.io', - websocketUrl: 'https://api.superhero.com', + websocketUrl: 'https://api.dev.tokensale.org', compilerUrl: 'https://v7.compiler.aepps.com', superheroBackendUrl: 'https://superhero-backend-mainnet.prd.service.aepps.com', DEX_BACKEND_URL: 'https://dex-backend-mainnet.prd.service.aepps.com', diff --git a/src/features/ae-eth-bridge/components/AeEthBridge.tsx b/src/features/ae-eth-bridge/components/AeEthBridge.tsx index 80faf0093..97f807525 100644 --- a/src/features/ae-eth-bridge/components/AeEthBridge.tsx +++ b/src/features/ae-eth-bridge/components/AeEthBridge.tsx @@ -925,7 +925,7 @@ export const AeEthBridge = () => {
{/* Header */}
-

+

{t('bridge.title')}

diff --git a/src/features/ae-eth-bridge/components/BridgeTokenSelector.tsx b/src/features/ae-eth-bridge/components/BridgeTokenSelector.tsx index c93647b45..fad350468 100644 --- a/src/features/ae-eth-bridge/components/BridgeTokenSelector.tsx +++ b/src/features/ae-eth-bridge/components/BridgeTokenSelector.tsx @@ -118,7 +118,7 @@ const BridgeTokenSelector = ({ {/* Header */}
- + {t('bridge.selectAToken')} diff --git a/src/features/ae-eth-buy/components/BuyAeWidget.tsx b/src/features/ae-eth-buy/components/BuyAeWidget.tsx index e011ee16d..542ca0574 100644 --- a/src/features/ae-eth-buy/components/BuyAeWidget.tsx +++ b/src/features/ae-eth-buy/components/BuyAeWidget.tsx @@ -411,7 +411,7 @@ const BuyAeWidgetContent = ({ Buy AE with ETH ) : ( -

+

Buy AE with ETH

)} diff --git a/src/features/dex/WrapUnwrapWidget.tsx b/src/features/dex/WrapUnwrapWidget.tsx index 3b6847596..7e251c118 100644 --- a/src/features/dex/WrapUnwrapWidget.tsx +++ b/src/features/dex/WrapUnwrapWidget.tsx @@ -137,7 +137,7 @@ export const WrapUnwrapWidget = ({ className }: WrapUnwrapWidgetProps) => { > {/* Header */}
-

+

(Un)Wrap AE

diff --git a/src/features/dex/components/AddLiquidityForm.tsx b/src/features/dex/components/AddLiquidityForm.tsx index e1800a922..e7f275c3e 100644 --- a/src/features/dex/components/AddLiquidityForm.tsx +++ b/src/features/dex/components/AddLiquidityForm.tsx @@ -437,7 +437,7 @@ const AddLiquidityForm = () => { {/* Header */}
-

{tDex('addLiquidityForm.title')}

+

{tDex('addLiquidityForm.title')}

diff --git a/src/features/dex/components/LiquidityConfirmation.tsx b/src/features/dex/components/LiquidityConfirmation.tsx index 867bcf835..02208b657 100644 --- a/src/features/dex/components/LiquidityConfirmation.tsx +++ b/src/features/dex/components/LiquidityConfirmation.tsx @@ -44,7 +44,7 @@ const LiquidityConfirmation = ({ {/* Header */}
- + Confirm Add Liquidity diff --git a/src/features/dex/components/RemoveLiquidityForm.tsx b/src/features/dex/components/RemoveLiquidityForm.tsx index 98fea4968..ef4c3aede 100644 --- a/src/features/dex/components/RemoveLiquidityForm.tsx +++ b/src/features/dex/components/RemoveLiquidityForm.tsx @@ -148,7 +148,7 @@ const RemoveLiquidityForm = () => { 💧
-

+

Confirm Removal

@@ -257,7 +257,7 @@ const RemoveLiquidityForm = () => { 💧

-

+

Remove Liquidity

diff --git a/src/features/dex/views/DexExplorePools.tsx b/src/features/dex/views/DexExplorePools.tsx index 1076a0a41..7dfed6ff8 100644 --- a/src/features/dex/views/DexExplorePools.tsx +++ b/src/features/dex/views/DexExplorePools.tsx @@ -70,7 +70,7 @@ const DexExplorePools = () => {

{/* Header */}
-

+

Explore Pools

diff --git a/src/features/dex/views/DexExploreTokens.tsx b/src/features/dex/views/DexExploreTokens.tsx index a4efbebeb..c77895125 100644 --- a/src/features/dex/views/DexExploreTokens.tsx +++ b/src/features/dex/views/DexExploreTokens.tsx @@ -54,7 +54,7 @@ const DexExploreTokens = () => {

{/* Header Card */}
-

+

Explore Tokens

diff --git a/src/features/dex/views/DexExploreTransactions.tsx b/src/features/dex/views/DexExploreTransactions.tsx index 19ac0e7c6..841c41c1d 100644 --- a/src/features/dex/views/DexExploreTransactions.tsx +++ b/src/features/dex/views/DexExploreTransactions.tsx @@ -68,7 +68,7 @@ const DexExploreTransactions = () => {

{/* Header */}
-

+

Advanced Transaction Explorer

diff --git a/src/features/dex/views/Pool.tsx b/src/features/dex/views/Pool.tsx index 4bf67a1da..e225f239e 100644 --- a/src/features/dex/views/Pool.tsx +++ b/src/features/dex/views/Pool.tsx @@ -42,7 +42,7 @@ const PoolContent = () => {

{/* Header */}
-

+

Your Liquidity Positions

diff --git a/src/features/social/components/BlockchainInfoPopover.tsx b/src/features/social/components/BlockchainInfoPopover.tsx index 8db372f8e..2c439b8a9 100644 --- a/src/features/social/components/BlockchainInfoPopover.tsx +++ b/src/features/social/components/BlockchainInfoPopover.tsx @@ -147,7 +147,7 @@ export const BlockchainInfoPopover = ({

Tx hash
- e.stopPropagation()}>{shortHash} + e.stopPropagation()}>{shortHash} + )} +
{effectiveAddress}
@@ -403,19 +423,6 @@ export default function UserProfile({ {/* Action buttons */}
- {(canEdit && false) ? ( - { - setEditInitialSection('profile'); - setEditOpen(true); - }} - > - {t('buttons.editProfile')} - - ) : null} {!canEdit ? ( openModal({ name: 'tip', props: { toAddress: effectiveAddress } })} @@ -445,19 +452,6 @@ export default function UserProfile({
- {(canEdit && !isXVerified && false) && ( - - )} - {/* Portfolio Chart and Stats - Side by side on md+ */}
{/* Portfolio Chart - Smaller on md+ */} @@ -600,6 +594,11 @@ export default function UserProfile({ initialBio={bioText} initialSection={editInitialSection} /> + setClaimChainNameOpen(false)} + address={effectiveAddress} + /> ) : ( <> @@ -630,6 +629,11 @@ export default function UserProfile({ initialBio={bioText} initialSection={editInitialSection} /> + setClaimChainNameOpen(false)} + address={effectiveAddress} + /> ); }