diff --git a/v2/perps-v2/ui/package.json b/v2/perps-v2/ui/package.json index f6320b1ee..f6676f988 100644 --- a/v2/perps-v2/ui/package.json +++ b/v2/perps-v2/ui/package.json @@ -22,6 +22,7 @@ "@graphql-typed-document-node/core": "^3.1.2", "@pythnetwork/pyth-evm-js": "^1.1.0", "@snx-v2/formatters": "workspace:*", + "@snx-v2/useGlobalProvidersWithFallback": "workspace:*", "@snx-v2/v3Theme": "workspace:*", "@synthetixio/contracts": "workspace:*", "@synthetixio/wei": "^2.74.4", diff --git a/v2/perps-v2/ui/src/components/AddressInput/AddressInput.tsx b/v2/perps-v2/ui/src/components/AddressInput/AddressInput.tsx index a8b7884fc..db6670ec0 100644 --- a/v2/perps-v2/ui/src/components/AddressInput/AddressInput.tsx +++ b/v2/perps-v2/ui/src/components/AddressInput/AddressInput.tsx @@ -1,25 +1,38 @@ +import { ethers } from 'ethers'; import { SearchIcon } from '@chakra-ui/icons'; import { Button, Flex, Input } from '@chakra-ui/react'; import { useForm } from 'react-hook-form'; import { useNavigate } from 'react-router-dom'; +import { useGlobalProvidersWithFallback } from '@snx-v2/useGlobalProvidersWithFallback'; export const AddressInput = () => { + const { globalProviders } = useGlobalProvidersWithFallback(); + const L1DefaultProvider = globalProviders.mainnet; const navigate = useNavigate(); const { register, getValues } = useForm({ defaultValues: { address: '' }, }); - const onSubmit = () => { - if (getValues('address')) { - navigate(`/${getValues('address')}`); + const onSubmit = async () => { + const address = getValues('address'); + if (address) { + // Try to resolve ENS + if (!ethers.utils.isAddress(address)) { + const ens: string | null = await L1DefaultProvider.resolveName(address); + if (ens) { + navigate(`/${ens}`); + return; + } + } + navigate(`/${address}`); } }; return ( { + const { globalProviders } = useGlobalProvidersWithFallback(); + + const [addressEnsName, setAddressEnsName] = useState(null); + + useEffect(() => { + async function resolve() { + if (!address || !globalProviders.mainnet) { + setAddressEnsName(null); + return; + } + const name = await globalProviders.mainnet.lookupAddress(address); + setAddressEnsName(name); + } + resolve(); + }, [address, globalProviders]); + + return { addressEnsName }; +}; diff --git a/v2/perps-v2/ui/src/hooks/useOwnerBySmartId.ts b/v2/perps-v2/ui/src/hooks/useOwnerBySmartId.ts new file mode 100644 index 000000000..8d96685d9 --- /dev/null +++ b/v2/perps-v2/ui/src/hooks/useOwnerBySmartId.ts @@ -0,0 +1,81 @@ +import { z } from 'zod'; +import { KWENTA_SUBGRAPH_URL, POLYNOMIAL_SUBGRAPH_URL } from '../utils'; +import { ApolloClient, gql, InMemoryCache, useQuery } from '@apollo/client'; + +const kwentaClient = new ApolloClient({ + uri: KWENTA_SUBGRAPH_URL, + cache: new InMemoryCache(), +}); + +const polyClient = new ApolloClient({ + uri: POLYNOMIAL_SUBGRAPH_URL, + cache: new InMemoryCache(), +}); + +const AccountBySmartIdQuery = gql` + query Account($id: String!) { + smartMarginAccount(id: $id) { + owner + } + } +`; + +const AccountQuery = gql` + query SmAccounts($account: String) { + logAccountCreateds(where: { account: $account }) { + owner + account + } + } +`; + +const SmartMarginAccountSchema = z.object({ + owner: z.string(), +}); + +const ResponseSchema = z + .object({ + smartMarginAccount: SmartMarginAccountSchema, + }) + .optional(); + +const OwnerByPolyAccountSchema = z.array( + z.object({ + owner: z.string(), + account: z.string(), + }) +); + +const ResponseSchemaOwnerByPoly = z + .object({ + logAccountCreateds: OwnerByPolyAccountSchema, + }) + .optional(); + +export const useOwnerKwenta = (smartAccountId?: string) => { + const { data, ...rest } = useQuery(AccountBySmartIdQuery, { + client: kwentaClient, + variables: { id: smartAccountId }, + skip: !smartAccountId, + }); + + const parsedResult = ResponseSchema.safeParse(data); + const kwentaOwner = parsedResult.success ? parsedResult.data?.smartMarginAccount?.owner : null; + + return { ...rest, kwentaOwner }; +}; + +export const useOwnerPolynomial = (polynomialAccount?: string) => { + const { data, ...rest } = useQuery(AccountQuery, { + client: polyClient, + variables: { account: polynomialAccount }, + skip: !polynomialAccount, + }); + + const parsedResult = ResponseSchemaOwnerByPoly.safeParse(data); + const polynomialOwner = parsedResult.success + ? parsedResult.data?.logAccountCreateds?.[0]?.owner + : null; + + return { ...rest, polynomialOwner }; +}; diff --git a/v2/perps-v2/ui/src/pages/Account.tsx b/v2/perps-v2/ui/src/pages/Account.tsx index 6ed22e211..ca555a549 100644 --- a/v2/perps-v2/ui/src/pages/Account.tsx +++ b/v2/perps-v2/ui/src/pages/Account.tsx @@ -7,14 +7,24 @@ import { PositionsTable } from '../components/Positions'; import { AccountActionsTable } from '../components/Actions'; import { useKwentaAccount } from '../hooks/useKwentaAccount'; import { usePolynomialAccount } from '../hooks/usePolynomialAccount'; +import { useOwnerKwenta, useOwnerPolynomial } from '../hooks/useOwnerBySmartId'; +import { useEnsName } from '../hooks/useEnsName'; import { SmartWallet } from '../components/Shared'; export const Account: FC = () => { const params = useParams(); const navigate = useNavigate(); + const { data: kwentaAccount } = useKwentaAccount(params?.walletAddress); const { data: polynomialAccount } = usePolynomialAccount(params?.walletAddress); + const { kwentaOwner } = useOwnerKwenta(params?.walletAddress); + const { polynomialOwner } = useOwnerPolynomial(params?.walletAddress); + + const { addressEnsName: addressEnsName } = useEnsName(params?.walletAddress); + const { addressEnsName: kwentaEnsName } = useEnsName(kwentaOwner); + const { addressEnsName: polynomialEnsName } = useEnsName(polynomialOwner); + return ( @@ -36,7 +46,7 @@ export const Account: FC = () => { flexWrap={{ base: 'wrap', md: 'nowrap' }} > - Account: {params?.walletAddress} + Account: {addressEnsName ? addressEnsName : params?.walletAddress} @@ -44,6 +54,18 @@ export const Account: FC = () => { {kwentaAccount && ( )} + {kwentaOwner && ( + + )} + {polynomialOwner && ( + + )} {polynomialAccount && (