diff --git a/packages/app/package.json b/packages/app/package.json
index fa8072dd3..aee691ade 100644
--- a/packages/app/package.json
+++ b/packages/app/package.json
@@ -19,6 +19,7 @@
"xstate:typegen": "xstate typegen 'src/**/*.ts?(x)'"
},
"dependencies": {
+ "@bako-id/sdk": "0.0.16",
"@fontsource/source-code-pro": "5.0.13",
"@fuel-ui/css": "0.23.3",
"@fuel-ui/icons": "0.23.3",
@@ -33,7 +34,7 @@
"@sentry/react": "8.33.1",
"@storybook/addon-viewport": "7.4.6",
"@storybook/jest": "0.2.3",
- "@tanstack/react-query": "5.28.4",
+ "@tanstack/react-query": "5.61.0",
"@xstate/react": "3.2.2",
"compare-versions": "6.1.0",
"cross-fetch": "4.0.0",
@@ -83,7 +84,7 @@
"@storybook/react-webpack5": "7.4.6",
"@storybook/testing-library": "0.2.2",
"@storybook/theming": "7.4.6",
- "@tanstack/react-query-devtools": "5.28.4",
+ "@tanstack/react-query-devtools": "5.61.0",
"@testing-library/react": "14.0.0",
"@types/chrome": "0.0.246",
"@types/lodash.debounce": "4.0.7",
diff --git a/packages/app/playwright/e2e/SendTransaction.test.ts b/packages/app/playwright/e2e/SendTransaction.test.ts
index d7d1fe14b..94d38360b 100644
--- a/packages/app/playwright/e2e/SendTransaction.test.ts
+++ b/packages/app/playwright/e2e/SendTransaction.test.ts
@@ -8,6 +8,7 @@ import {
getButtonByText,
getByAriaLabel,
getInputByName,
+ getInputByValue,
hasText,
visit,
} from '../commons';
@@ -306,6 +307,43 @@ test.describe('SendTransaction', () => {
await hasText(page, 'success');
});
+ test('Send transaction to a name', async () => {
+ await visit(page, '/send');
+
+ // Check submit button is disable by default
+ await page.waitForSelector('[aria-disabled="true"]');
+
+ // Select asset
+ await getButtonByText(page, 'Select one asset').click();
+ await page.getByText('Ethereum').click();
+ await page.waitForTimeout(2000);
+
+ // Fill address
+ await getInputByName(page, 'address').fill('@bakoid');
+ await new Promise((resolve) => setTimeout(resolve, 3000));
+
+ await hasText(page, '0x89297d82...6bbe387D2D6975C7D3');
+
+ await getInputByName(page, 'amount').focus();
+
+ // Fill amount
+ await getInputByName(page, 'amount').fill('0.001');
+
+ // Submit transaction
+ const btnLocator = getButtonByText(page, 'Review');
+
+ await expectButtonToBeEnabled(btnLocator);
+ await page.waitForTimeout(5000);
+ await expectButtonToBeEnabled(btnLocator);
+ await btnLocator.click();
+
+ await getButtonByText(page, 'Approve').click();
+ await hasText(page, '0.001 ETH');
+
+ // Wait for transaction to be confirmed
+ await hasText(page, 'success');
+ });
+
test('Send max amount transaction', async () => {
const receiverWallet = Wallet.generate({
provider,
diff --git a/packages/app/src/systems/Core/components/Providers/Providers.tsx b/packages/app/src/systems/Core/components/Providers/Providers.tsx
index 57f41ef47..fe9c7e497 100644
--- a/packages/app/src/systems/Core/components/Providers/Providers.tsx
+++ b/packages/app/src/systems/Core/components/Providers/Providers.tsx
@@ -11,6 +11,7 @@ import { StoreProvider } from '~/store';
import icons from '/icons/sprite.svg';
+import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { ErrorBoundary } from '~/systems/Error';
type ProvidersProps = {
@@ -50,13 +51,23 @@ setFuelThemes({
},
});
+const queryClient = new QueryClient({
+ defaultOptions: {
+ queries: {
+ refetchOnMount: false,
+ },
+ },
+});
+
export function Providers({ children }: ProvidersProps) {
return (
- {globalCss(customStyles)()}
- {children}
+
+ {globalCss(customStyles)()}
+ {children}
+
diff --git a/packages/app/src/systems/Core/utils/address.tsx b/packages/app/src/systems/Core/utils/address.tsx
index 0f9ac5f86..f737d3b7b 100644
--- a/packages/app/src/systems/Core/utils/address.tsx
+++ b/packages/app/src/systems/Core/utils/address.tsx
@@ -1,8 +1,14 @@
import { isB256 } from 'fuels';
-export function shortAddress(address = '') {
+export function shortAddress(
+ address = '',
+ options: { left: number; right: number } = {
+ left: 6,
+ right: 4,
+ }
+) {
return address.length > 10
- ? `${address.slice(0, 6)}...${address.slice(-4)}`
+ ? `${address.slice(0, options.left)}...${address.slice(-options.right)}`
: address;
}
diff --git a/packages/app/src/systems/Core/utils/adress.test.ts b/packages/app/src/systems/Core/utils/adress.test.ts
index f4007aeae..33220bd9d 100644
--- a/packages/app/src/systems/Core/utils/adress.test.ts
+++ b/packages/app/src/systems/Core/utils/adress.test.ts
@@ -5,4 +5,12 @@ describe('shortAddress()', () => {
const addr = 'fuel0x2c8e117bcfba11c76d7db2d43464b1d2093474ef';
expect(shortAddress(addr)).toBe('fuel0x...74ef');
});
+
+ it('should show a small version of a full address with custom length', () => {
+ const addr =
+ '0xf2Bc792F42A19fC64646787626E4451576b95d123cc525C1583e0462c9a62a23';
+ expect(shortAddress(addr, { left: 10, right: 30 })).toBe(
+ '0xf2Bc792F...b95d123cc525C1583e0462c9a62a23'
+ );
+ });
});
diff --git a/packages/app/src/systems/NameSystem/__mocks__/index.tsx b/packages/app/src/systems/NameSystem/__mocks__/index.tsx
new file mode 100644
index 000000000..93c13987e
--- /dev/null
+++ b/packages/app/src/systems/NameSystem/__mocks__/index.tsx
@@ -0,0 +1 @@
+export * from './names';
diff --git a/packages/app/src/systems/NameSystem/__mocks__/names.tsx b/packages/app/src/systems/NameSystem/__mocks__/names.tsx
new file mode 100644
index 000000000..ca5a1261e
--- /dev/null
+++ b/packages/app/src/systems/NameSystem/__mocks__/names.tsx
@@ -0,0 +1,8 @@
+export const MOCK_NAMES = [
+ {
+ name: '@bakoid',
+ link: 'https://app.bako.id/profile/bakoid',
+ resolver:
+ '0xec8ff99af54e7f4c9dd81f32dffade6b41f3b980436ee2eabf47a069f998cd73',
+ },
+];
diff --git a/packages/app/src/systems/NameSystem/components/NameSystemBox.stories.tsx b/packages/app/src/systems/NameSystem/components/NameSystemBox.stories.tsx
new file mode 100644
index 000000000..06676d491
--- /dev/null
+++ b/packages/app/src/systems/NameSystem/components/NameSystemBox.stories.tsx
@@ -0,0 +1,53 @@
+import { Box, Button } from '@fuel-ui/react';
+
+import { useState } from 'react';
+import {
+ NameSystemBox,
+ type NameSystemBoxProps,
+ NameSystemWrapper,
+} from '~/systems/NameSystem';
+import { MOCK_NAMES } from '~/systems/NameSystem/__mocks__';
+
+export default {
+ component: NameSystemBox,
+ title: 'NameSystem/Components/NameSystemBox',
+};
+
+export const Usage = (args: NameSystemBoxProps) => {
+ const { name: addressName, resolver, link } = MOCK_NAMES[0];
+ return (
+
+ console.log('Clear')}
+ />
+
+ );
+};
+
+export const Wrapper = (args: NameSystemBoxProps) => {
+ const { name: addressName, resolver, link } = MOCK_NAMES[0];
+
+ const [isVisible, setIsVisible] = useState(false);
+
+ return (
+
+ setIsVisible(false)}
+ />
+ }
+ input={}
+ />
+
+ );
+};
diff --git a/packages/app/src/systems/NameSystem/components/NameSystemBox.tsx b/packages/app/src/systems/NameSystem/components/NameSystemBox.tsx
new file mode 100644
index 000000000..44f61f965
--- /dev/null
+++ b/packages/app/src/systems/NameSystem/components/NameSystemBox.tsx
@@ -0,0 +1,93 @@
+import { cssObj } from '@fuel-ui/css';
+import {
+ Avatar,
+ Box,
+ Icon,
+ IconButton,
+ Input,
+ Link,
+ Text,
+} from '@fuel-ui/react';
+import { AnimatePresence, motion } from 'framer-motion';
+import { type Maybe, animations, shortAddress } from '~/systems/Core';
+
+const MotionContent = motion(Box);
+
+export type NameSystemWrapperProps = {
+ isVisible?: boolean;
+ element: React.ReactNode;
+ input: React.ReactNode;
+};
+
+export type NameSystemBoxProps = {
+ link: Maybe;
+ name: Maybe;
+ resolver: Maybe;
+ onClear: () => void;
+};
+
+export const NameSystemWrapper = (props: NameSystemWrapperProps) => (
+
+ {props.isVisible ? (
+
+ {props.element}
+
+ ) : (
+
+ {props.input}
+
+ )}
+
+);
+
+export const NameSystemBox = (props: NameSystemBoxProps) => {
+ return (
+
+
+
+
+ {props.name}
+
+
+ {props.resolver &&
+ shortAddress(props.resolver, {
+ left: 10,
+ right: 10,
+ })}
+
+
+
+
+ );
+};
+
+const styles = {
+ addressBox: cssObj({
+ display: 'flex',
+ px: '$3 !important',
+ py: '$1 !important',
+ alignItems: 'center',
+ gap: '$3',
+ flexDirection: 'row',
+ position: 'relative',
+ '.fuel_Link': {
+ fontSize: '$sm',
+ },
+ '.fuel_Button': {
+ position: 'absolute',
+ right: '$2',
+ },
+ '.fuel_Avatar': {
+ width: 30,
+ height: 30,
+ },
+ '.fuel_Icon': {
+ color: '$inputBaseIcon !important',
+ },
+ }),
+};
diff --git a/packages/app/src/systems/NameSystem/components/index.ts b/packages/app/src/systems/NameSystem/components/index.ts
new file mode 100644
index 000000000..22a409141
--- /dev/null
+++ b/packages/app/src/systems/NameSystem/components/index.ts
@@ -0,0 +1 @@
+export * from './NameSystemBox';
diff --git a/packages/app/src/systems/NameSystem/hooks/index.ts b/packages/app/src/systems/NameSystem/hooks/index.ts
new file mode 100644
index 000000000..fb37a8500
--- /dev/null
+++ b/packages/app/src/systems/NameSystem/hooks/index.ts
@@ -0,0 +1,2 @@
+export * from './useNameSystem';
+export * from './useNameResolver';
diff --git a/packages/app/src/systems/NameSystem/hooks/useNameResolver.ts b/packages/app/src/systems/NameSystem/hooks/useNameResolver.ts
new file mode 100644
index 000000000..17b930d26
--- /dev/null
+++ b/packages/app/src/systems/NameSystem/hooks/useNameResolver.ts
@@ -0,0 +1,23 @@
+import { useQuery, useQueryClient } from '@tanstack/react-query';
+import { isB256 } from 'fuels';
+import { NameSystemService } from '~/systems/NameSystem/services';
+import { NS_QUERY_KEYS } from '~/systems/NameSystem/utils/queryKeys';
+import { useProvider } from '~/systems/Network/hooks/useProvider';
+
+export const useNameResolver = (address: string) => {
+ const provider = useProvider();
+ const queryClient = useQueryClient();
+
+ const { data: name, isLoading } = useQuery({
+ queryKey: NS_QUERY_KEYS.name(address, provider?.url),
+ queryFn: async () => {
+ if (!provider) return null;
+ const nameSystem = new NameSystemService(provider, queryClient);
+ const { name } = await nameSystem.name(address);
+ return name;
+ },
+ enabled: !!provider && isB256(address),
+ });
+
+ return { name, isLoading };
+};
diff --git a/packages/app/src/systems/NameSystem/hooks/useNameSystem.ts b/packages/app/src/systems/NameSystem/hooks/useNameSystem.ts
new file mode 100644
index 000000000..4f06655bb
--- /dev/null
+++ b/packages/app/src/systems/NameSystem/hooks/useNameSystem.ts
@@ -0,0 +1,78 @@
+import { isValidDomain } from '@bako-id/sdk';
+import debounce from 'lodash.debounce';
+import { useCallback, useMemo } from 'react';
+
+import { useMutation, useQueryClient } from '@tanstack/react-query';
+import { delay } from '~/systems/Core';
+import { NameSystemService } from '~/systems/NameSystem/services';
+import { useProvider } from '~/systems/Network/hooks/useProvider';
+
+export const useNameSystem = () => {
+ const provider = useProvider();
+ const queryClient = useQueryClient();
+
+ const resolveNameMutation = useMutation({
+ mutationFn: async (name: string) => {
+ if (!provider) return null;
+ const nameSystem = new NameSystemService(provider, queryClient);
+ await delay(1200);
+ return nameSystem.resolver(name);
+ },
+ });
+
+ const resolveAddressMutation = useMutation({
+ mutationFn: async (address: string) => {
+ if (!provider) return null;
+ const nameSystem = new NameSystemService(provider, queryClient);
+ await delay(1200);
+ return nameSystem.name(address);
+ },
+ });
+
+ const clear = () => {
+ resolveNameMutation.reset();
+ resolveAddressMutation.reset();
+ };
+
+ const isName = useCallback((name: string) => {
+ return name.startsWith('@') && isValidDomain(name);
+ }, []);
+
+ // biome-ignore lint/correctness/useExhaustiveDependencies:
+ const getResolver = useCallback(debounce(resolveNameMutation.mutate, 500), [
+ resolveNameMutation.mutate,
+ ]);
+
+ const profileURI = useMemo(() => {
+ if (!provider || !resolveNameMutation.data?.name) return null;
+ return NameSystemService.profileURI(
+ provider,
+ resolveNameMutation.data?.name
+ );
+ }, [provider, resolveNameMutation.data?.name]);
+
+ return {
+ handlers: {
+ clear,
+ isName,
+ getResolver,
+ getName: resolveAddressMutation.mutate,
+ },
+ profileURI,
+ isLoading:
+ resolveAddressMutation.isPending || resolveNameMutation.isPending,
+ name: {
+ isLoading: resolveAddressMutation.isPending,
+ isSuccess: resolveAddressMutation.isSuccess,
+ value:
+ resolveAddressMutation.data?.name ?? resolveNameMutation.data?.name,
+ },
+ resolver: {
+ isLoading: resolveNameMutation.isPending,
+ isSuccess: resolveNameMutation.isSuccess,
+ value:
+ resolveAddressMutation.data?.address ??
+ resolveNameMutation.data?.address,
+ },
+ };
+};
diff --git a/packages/app/src/systems/NameSystem/index.ts b/packages/app/src/systems/NameSystem/index.ts
new file mode 100644
index 000000000..9330290d5
--- /dev/null
+++ b/packages/app/src/systems/NameSystem/index.ts
@@ -0,0 +1,2 @@
+export * from './hooks';
+export * from './components';
diff --git a/packages/app/src/systems/NameSystem/services/index.ts b/packages/app/src/systems/NameSystem/services/index.ts
new file mode 100644
index 000000000..aca5e9bbe
--- /dev/null
+++ b/packages/app/src/systems/NameSystem/services/index.ts
@@ -0,0 +1 @@
+export * from './nameSystem';
diff --git a/packages/app/src/systems/NameSystem/services/nameSystem.ts b/packages/app/src/systems/NameSystem/services/nameSystem.ts
new file mode 100644
index 000000000..641e1fb51
--- /dev/null
+++ b/packages/app/src/systems/NameSystem/services/nameSystem.ts
@@ -0,0 +1,56 @@
+import { BakoIDClient } from '@bako-id/sdk';
+import type { QueryClient } from '@tanstack/react-query';
+import type { Provider } from 'fuels';
+import { NS_QUERY_KEYS } from '~/systems/NameSystem/utils/queryKeys';
+
+type QueryData = string | null;
+
+export class NameSystemService {
+ private provider: Provider;
+ private queryClient: QueryClient;
+ private client: BakoIDClient;
+
+ constructor(provider: Provider, queryClient: QueryClient) {
+ this.provider = provider;
+ this.client = new BakoIDClient(provider);
+ this.queryClient = queryClient;
+ }
+
+ static profileURI(provider: Provider, name: string) {
+ const client = new BakoIDClient(provider);
+ return client.profile(name);
+ }
+
+ async name(address: string) {
+ const queryKey = NS_QUERY_KEYS.address(address, this.provider.url);
+ const queryData = this.queryClient.getQueryData(queryKey);
+
+ if (typeof queryData !== 'undefined') {
+ return { name: queryData, address };
+ }
+
+ let name = await this.client.name(address);
+ name = name ? `@${name}` : null;
+ this.queryClient.setQueryData(queryKey, name);
+ return {
+ name,
+ address,
+ };
+ }
+
+ async resolver(name: string) {
+ const queryKey = NS_QUERY_KEYS.name(name, this.provider.url);
+ const queryData = this.queryClient.getQueryData(queryKey);
+
+ if (typeof queryData !== 'undefined') {
+ return { name, address: queryData };
+ }
+
+ const resolver = await this.client.resolver(name);
+ this.queryClient.setQueryData(queryKey, resolver);
+ return {
+ name,
+ address: resolver,
+ };
+ }
+}
diff --git a/packages/app/src/systems/NameSystem/utils/index.ts b/packages/app/src/systems/NameSystem/utils/index.ts
new file mode 100644
index 000000000..e69de29bb
diff --git a/packages/app/src/systems/NameSystem/utils/queryKeys.ts b/packages/app/src/systems/NameSystem/utils/queryKeys.ts
new file mode 100644
index 000000000..85e20fd41
--- /dev/null
+++ b/packages/app/src/systems/NameSystem/utils/queryKeys.ts
@@ -0,0 +1,15 @@
+import type { QueryKey } from '@tanstack/react-query';
+
+export const NS_QUERY_KEYS = {
+ base: ['nameSystem'] as QueryKey,
+ name: (name: string, provider?: string) => {
+ const base = NS_QUERY_KEYS.base.concat(['name', name]);
+ if (typeof provider !== 'undefined') base.push(provider);
+ return base as QueryKey;
+ },
+ address: (address: string, provider?: string) => {
+ const base = NS_QUERY_KEYS.base.concat(['address', address]);
+ if (typeof provider !== 'undefined') base.push(provider);
+ return base as QueryKey;
+ },
+};
diff --git a/packages/app/src/systems/Send/components/SendSelect/SendSelect.tsx b/packages/app/src/systems/Send/components/SendSelect/SendSelect.tsx
index f4e0ff290..7df8df77a 100644
--- a/packages/app/src/systems/Send/components/SendSelect/SendSelect.tsx
+++ b/packages/app/src/systems/Send/components/SendSelect/SendSelect.tsx
@@ -1,8 +1,19 @@
import { cssObj } from '@fuel-ui/css';
-import { Alert, Box, Form, Input, Text } from '@fuel-ui/react';
+import {
+ Alert,
+ Box,
+ Card,
+ Form,
+ Icon,
+ IconButton,
+ Input,
+ Link,
+ Spinner,
+ Text,
+} from '@fuel-ui/react';
import { motion } from 'framer-motion';
import { type BN, bn } from 'fuels';
-import { useEffect, useMemo, useRef, useState } from 'react';
+import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { AssetSelect } from '~/systems/Asset';
import {
ControlledField,
@@ -10,10 +21,12 @@ import {
MotionFlex,
MotionStack,
animations,
+ shortAddress,
} from '~/systems/Core';
import { useController, useWatch } from 'react-hook-form';
import { InputAmount } from '~/systems/Core/components/InputAmount/InputAmount';
+import { NameSystemBox, NameSystemWrapper } from '~/systems/NameSystem';
import { TxFeeOptions } from '~/systems/Transaction/components/TxFeeOptions/TxFeeOptions';
import type { UseSendReturn } from '../../hooks';
@@ -33,6 +46,7 @@ export function SendSelect({
errorMessage,
warningMessage,
provider,
+ nameSystem,
}: SendSelectProps) {
const [watchMax, setWatchMax] = useState(false);
const isAmountFocused = useRef(false);
@@ -90,6 +104,11 @@ export function SendSelect({
...b.asset,
}));
+ const clearAddress = useCallback(() => {
+ form.setValue('address', '');
+ nameSystem.handlers.clear();
+ }, [form, nameSystem.handlers]);
+
return (
@@ -120,26 +139,60 @@ export function SendSelect({
To
- (
-
-
-
+
+ }
+ input={
+ (
+
+
+ {nameSystem.isLoading && (
+ }
+ />
+ )}
+
+ )}
+ />
+ }
/>
+ {warningMessage && !form.formState.errors?.address && (
+
+ {warningMessage}
+
+ )}
@@ -244,6 +297,22 @@ const styles = {
color: '$intentsError9',
},
}),
+ addressBox: cssObj({
+ display: 'flex',
+ justifyContent: 'center',
+ px: '$3 !important',
+ py: '$1 !important',
+ flexDirection: 'column',
+ position: 'relative',
+
+ '.fuel_Link': {
+ fontSize: '$sm',
+ },
+ '.fuel_Button': {
+ position: 'absolute',
+ right: '-$1',
+ },
+ }),
alert: cssObj({
fontSize: '$sm',
lineHeight: '$tight',
diff --git a/packages/app/src/systems/Send/hooks/useSend.tsx b/packages/app/src/systems/Send/hooks/useSend.tsx
index 01f862328..bdd5d429a 100644
--- a/packages/app/src/systems/Send/hooks/useSend.tsx
+++ b/packages/app/src/systems/Send/hooks/useSend.tsx
@@ -12,7 +12,9 @@ import { useTransactionRequest } from '~/systems/DApp';
import { TxRequestStatus } from '~/systems/DApp/machines/transactionRequestMachine';
import type { TxInputs } from '~/systems/Transaction/services';
+import { isValidDomain } from '@bako-id/sdk';
import { AssetsCache } from '~/systems/Asset/cache/AssetsCache';
+import { useNameSystem } from '~/systems/NameSystem';
import { useProvider } from '~/systems/Network/hooks/useProvider';
import { formatGasLimit } from '~/systems/Transaction';
import { sendMachine } from '../machines/sendMachine';
@@ -126,6 +128,16 @@ const schemaFactory = (provider?: Provider) =>
if (!provider) {
return true;
}
+
+ if (value.startsWith('@')) {
+ if (value.length >= 2 && !isValidDomain(value)) {
+ return ctx.createError({
+ message: 'Invalid domain',
+ });
+ }
+ return true;
+ }
+
if (!isB256(value)) {
return ctx.createError({
message: 'Address is not a valid',
@@ -271,6 +283,7 @@ export function useSend() {
const txRequest = useTransactionRequest();
const { account } = useAccounts();
const provider = useProvider();
+ const nameSystem = useNameSystem();
const service = useInterpret(() =>
sendMachine.withConfig({
@@ -288,6 +301,7 @@ export function useSend() {
fastTip,
maxGasLimit,
} = ctx;
+
if (!providerUrl || !transactionRequest || !address) {
throw new Error('Params are required');
}
@@ -295,7 +309,7 @@ export function useSend() {
txRequest.handlers.request({
providerUrl,
transactionRequest,
- address,
+ address: nameSystem.resolver.value ?? address,
fees: {
baseFee,
regularTip,
@@ -389,7 +403,7 @@ export function useSend() {
const { address, asset, amount, fees } = data;
const input: TxInputs['createTransfer'] = {
- to: address,
+ to: nameSystem.resolver.value ?? address,
assetId: asset,
amount,
tip: fees.tip.amount,
@@ -408,6 +422,7 @@ export function useSend() {
form.trigger,
form.handleSubmit,
service.send,
+ nameSystem.resolver,
]);
return {
@@ -421,6 +436,7 @@ export function useSend() {
readyToSend,
account,
txRequest,
+ nameSystem,
assetIdSelected,
balances: account?.balances,
balanceAssetSelected,
diff --git a/packages/app/src/systems/Send/pages/SendPage/SendPage.tsx b/packages/app/src/systems/Send/pages/SendPage/SendPage.tsx
index f56eb1f7f..d52ad3e88 100644
--- a/packages/app/src/systems/Send/pages/SendPage/SendPage.tsx
+++ b/packages/app/src/systems/Send/pages/SendPage/SendPage.tsx
@@ -14,7 +14,8 @@ const CHECKSUM_MESSAGE =
export function SendPage() {
const send = useSend();
- const { handlers, txRequest, status, form, readyToSend } = send;
+ const { handlers, txRequest, status, form, readyToSend, nameSystem } = send;
+ const { handlers: nameSystemHandlers } = nameSystem;
const [warningMessage, setWarningMessage] = useState(
undefined
);
@@ -23,9 +24,18 @@ export function SendPage() {
name: 'address',
});
+ // biome-ignore lint/correctness/useExhaustiveDependencies:
useEffect(() => {
+ nameSystemHandlers.clear();
+
if (address) {
+ if (nameSystemHandlers.isName(address)) {
+ nameSystemHandlers.getResolver(address);
+ return;
+ }
+
if (isB256(address)) {
+ nameSystemHandlers.getName(address);
const isValid = Address.isChecksumValid(address);
setWarningMessage(isValid ? undefined : CHECKSUM_MESSAGE);
return;
@@ -34,6 +44,17 @@ export function SendPage() {
setWarningMessage(undefined);
}, [address]);
+ // biome-ignore lint/correctness/useExhaustiveDependencies:
+ useEffect(() => {
+ const isName = nameSystemHandlers.isName(address);
+ if (isName && nameSystem.resolver.isSuccess && !nameSystem.resolver.value) {
+ form.setError('address', {
+ type: 'manual',
+ message: 'No resolver for domain provided',
+ });
+ }
+ }, [address, nameSystem.resolver.value]);
+
return (