diff --git a/ui/address/AddressContract.pw.tsx b/ui/address/AddressContract.pw.tsx index dcdad6c3c4..0229581a08 100644 --- a/ui/address/AddressContract.pw.tsx +++ b/ui/address/AddressContract.pw.tsx @@ -64,12 +64,12 @@ test.describe('ABI functionality', () => { await expect(component.getByRole('button', { name: 'Connect wallet' })).toBeVisible(); await component.getByText('setReserveInterestRateStrategyAddress').click(); - await expect(component.getByLabel('4.').getByRole('button', { name: 'Simulate' })).toBeEnabled(); - await expect(component.getByLabel('4.').getByRole('button', { name: 'Write' })).toBeEnabled(); + await expect(component.getByLabel('9.').getByRole('button', { name: 'Simulate' })).toBeEnabled(); + await expect(component.getByLabel('9.').getByRole('button', { name: 'Write' })).toBeEnabled(); await component.getByText('pause').click(); - await expect(component.getByLabel('7.').getByRole('button', { name: 'Simulate' })).toBeHidden(); - await expect(component.getByLabel('7.').getByRole('button', { name: 'Write' })).toBeEnabled(); + await expect(component.getByLabel('5.').getByRole('button', { name: 'Simulate' })).toBeHidden(); + await expect(component.getByLabel('5.').getByRole('button', { name: 'Write' })).toBeEnabled(); }); test('write, no wallet client', async({ render, createSocket, mockEnvs }) => { @@ -86,11 +86,11 @@ test.describe('ABI functionality', () => { await expect(component.getByRole('button', { name: 'Connect wallet' })).toBeHidden(); await component.getByText('setReserveInterestRateStrategyAddress').click(); - await expect(component.getByLabel('4.').getByRole('button', { name: 'Simulate' })).toBeEnabled(); - await expect(component.getByLabel('4.').getByRole('button', { name: 'Write' })).toBeDisabled(); + await expect(component.getByLabel('9.').getByRole('button', { name: 'Simulate' })).toBeEnabled(); + await expect(component.getByLabel('9.').getByRole('button', { name: 'Write' })).toBeDisabled(); await component.getByText('pause').click(); - await expect(component.getByLabel('7.').getByRole('button', { name: 'Simulate' })).toBeHidden(); - await expect(component.getByLabel('7.').getByRole('button', { name: 'Write' })).toBeDisabled(); + await expect(component.getByLabel('5.').getByRole('button', { name: 'Simulate' })).toBeHidden(); + await expect(component.getByLabel('5.').getByRole('button', { name: 'Write' })).toBeDisabled(); }); }); diff --git a/ui/address/contract/methods/ContractMethodsCustom.tsx b/ui/address/contract/methods/ContractMethodsCustom.tsx index eb9b677513..00203175c7 100644 --- a/ui/address/contract/methods/ContractMethodsCustom.tsx +++ b/ui/address/contract/methods/ContractMethodsCustom.tsx @@ -18,7 +18,7 @@ import ContractCustomAbiAlert from './ContractCustomAbiAlert'; import ContractMethodsContainer from './ContractMethodsContainer'; import ContractMethodsFilters from './ContractMethodsFilters'; import useMethodsFilters from './useMethodsFilters'; -import { enrichWithMethodId, isMethod } from './utils'; +import { formatAbi } from './utils'; interface Props { isLoading?: boolean; @@ -52,12 +52,7 @@ const ContractMethodsCustom = ({ isLoading: isLoadingProp }: Props) => { contract_address_hash: addressHash, } : undefined); - const abi = React.useMemo(() => { - return currentInfo?.abi - .filter(isMethod) - .map(enrichWithMethodId) ?? []; - }, [ currentInfo ]); - + const abi = React.useMemo(() => formatAbi(currentInfo?.abi || []), [ currentInfo ]); const filters = useMethodsFilters({ abi }); const updateButton = React.useMemo(() => { diff --git a/ui/address/contract/methods/ContractMethodsMudSystem.tsx b/ui/address/contract/methods/ContractMethodsMudSystem.tsx index e695c82050..22cf2596c8 100644 --- a/ui/address/contract/methods/ContractMethodsMudSystem.tsx +++ b/ui/address/contract/methods/ContractMethodsMudSystem.tsx @@ -14,7 +14,7 @@ import ContractConnectWallet from './ContractConnectWallet'; import ContractMethodsContainer from './ContractMethodsContainer'; import ContractMethodsFilters from './ContractMethodsFilters'; import useMethodsFilters from './useMethodsFilters'; -import { enrichWithMethodId, isMethod } from './utils'; +import { formatAbi } from './utils'; interface Props { items: Array; @@ -42,10 +42,7 @@ const ContractMethodsMudSystem = ({ items }: Props) => { setSelectedItem(item as SmartContractMudSystemItem); }, []); - const abi = React.useMemo(() => { - return systemInfoQuery.data?.abi?.filter(isMethod).map(enrichWithMethodId) || []; - }, [ systemInfoQuery.data?.abi ]); - + const abi = React.useMemo(() => formatAbi(systemInfoQuery.data?.abi || []), [ systemInfoQuery.data?.abi ]); const filters = useMethodsFilters({ abi }); return ( diff --git a/ui/address/contract/methods/ContractMethodsProxy.tsx b/ui/address/contract/methods/ContractMethodsProxy.tsx index 372668ecfb..99f872e3a8 100644 --- a/ui/address/contract/methods/ContractMethodsProxy.tsx +++ b/ui/address/contract/methods/ContractMethodsProxy.tsx @@ -13,7 +13,7 @@ import ContractConnectWallet from './ContractConnectWallet'; import ContractMethodsContainer from './ContractMethodsContainer'; import ContractMethodsFilters from './ContractMethodsFilters'; import useMethodsFilters from './useMethodsFilters'; -import { enrichWithMethodId, isMethod } from './utils'; +import { formatAbi } from './utils'; interface Props { implementations: Array; @@ -36,10 +36,7 @@ const ContractMethodsProxy = ({ implementations, isLoading: isInitialLoading }: }, }); - const abi = React.useMemo(() => { - return contractQuery.data?.abi?.filter(isMethod).map(enrichWithMethodId) || []; - }, [ contractQuery.data?.abi ]); - + const abi = React.useMemo(() => formatAbi(contractQuery.data?.abi || []), [ contractQuery.data?.abi ]); const filters = useMethodsFilters({ abi }); return ( diff --git a/ui/address/contract/methods/ContractMethodsRegular.tsx b/ui/address/contract/methods/ContractMethodsRegular.tsx index 5b9956a5de..0d2d129799 100644 --- a/ui/address/contract/methods/ContractMethodsRegular.tsx +++ b/ui/address/contract/methods/ContractMethodsRegular.tsx @@ -1,8 +1,7 @@ import { Flex } from '@chakra-ui/react'; import { useRouter } from 'next/router'; import React from 'react'; - -import type { SmartContractMethod } from './types'; +import type { Abi } from 'viem'; import getQueryParamString from 'lib/router/getQueryParamString'; @@ -11,9 +10,10 @@ import ContractConnectWallet from './ContractConnectWallet'; import ContractMethodsContainer from './ContractMethodsContainer'; import ContractMethodsFilters from './ContractMethodsFilters'; import useMethodsFilters from './useMethodsFilters'; +import { formatAbi } from './utils'; interface Props { - abi: Array; + abi: Abi; isLoading?: boolean; } @@ -24,7 +24,8 @@ const ContractMethodsRegular = ({ abi, isLoading }: Props) => { const tab = getQueryParamString(router.query.tab); const addressHash = getQueryParamString(router.query.hash); - const filters = useMethodsFilters({ abi }); + const formattedAbi = React.useMemo(() => formatAbi(abi), [ abi ]); + const filters = useMethodsFilters({ abi: formattedAbi }); return ( @@ -35,8 +36,8 @@ const ContractMethodsRegular = ({ abi, isLoading }: Props) => { onChange={ filters.onChange } isLoading={ isLoading } /> - - + + ); diff --git a/ui/address/contract/methods/__screenshots__/ContractMethodsCustom.pw.tsx_default_with-data-1.png b/ui/address/contract/methods/__screenshots__/ContractMethodsCustom.pw.tsx_default_with-data-1.png index 760b7f0b9c..904736d673 100644 Binary files a/ui/address/contract/methods/__screenshots__/ContractMethodsCustom.pw.tsx_default_with-data-1.png and b/ui/address/contract/methods/__screenshots__/ContractMethodsCustom.pw.tsx_default_with-data-1.png differ diff --git a/ui/address/contract/methods/__screenshots__/ContractMethodsRegular.pw.tsx_dark-color-mode_all-methods-dark-mode-mobile-1.png b/ui/address/contract/methods/__screenshots__/ContractMethodsRegular.pw.tsx_dark-color-mode_all-methods-dark-mode-mobile-1.png index eeec224eba..246459acef 100644 Binary files a/ui/address/contract/methods/__screenshots__/ContractMethodsRegular.pw.tsx_dark-color-mode_all-methods-dark-mode-mobile-1.png and b/ui/address/contract/methods/__screenshots__/ContractMethodsRegular.pw.tsx_dark-color-mode_all-methods-dark-mode-mobile-1.png differ diff --git a/ui/address/contract/methods/__screenshots__/ContractMethodsRegular.pw.tsx_default_all-methods-dark-mode-mobile-1.png b/ui/address/contract/methods/__screenshots__/ContractMethodsRegular.pw.tsx_default_all-methods-dark-mode-mobile-1.png index 34a7666b31..4094008562 100644 Binary files a/ui/address/contract/methods/__screenshots__/ContractMethodsRegular.pw.tsx_default_all-methods-dark-mode-mobile-1.png and b/ui/address/contract/methods/__screenshots__/ContractMethodsRegular.pw.tsx_default_all-methods-dark-mode-mobile-1.png differ diff --git a/ui/address/contract/methods/__screenshots__/ContractMethodsRegular.pw.tsx_mobile_all-methods-dark-mode-mobile-1.png b/ui/address/contract/methods/__screenshots__/ContractMethodsRegular.pw.tsx_mobile_all-methods-dark-mode-mobile-1.png index 217f7a6ef2..3bd21e7530 100644 Binary files a/ui/address/contract/methods/__screenshots__/ContractMethodsRegular.pw.tsx_mobile_all-methods-dark-mode-mobile-1.png and b/ui/address/contract/methods/__screenshots__/ContractMethodsRegular.pw.tsx_mobile_all-methods-dark-mode-mobile-1.png differ diff --git a/ui/address/contract/methods/utils.ts b/ui/address/contract/methods/utils.ts index bdd96ef5ed..fe63631784 100644 --- a/ui/address/contract/methods/utils.ts +++ b/ui/address/contract/methods/utils.ts @@ -42,6 +42,25 @@ export const enrichWithMethodId = (method: AbiFunction | AbiFallback | AbiReceiv } }; +const getNameForSorting = (method: SmartContractMethod | AbiFallback | AbiReceive) => { + if ('name' in method) { + return method.name; + } + + return method.type === 'fallback' ? 'fallback' : 'receive'; +}; + +export const formatAbi = (abi: Abi) => { + return abi + .filter(isMethod) + .map(enrichWithMethodId) + .sort((a, b) => { + const aName = getNameForSorting(a); + const bName = getNameForSorting(b); + return aName.localeCompare(bName); + }); +}; + export const TYPE_FILTER_OPTIONS: Array<{ value: MethodType; title: string }> = [ { value: 'all', title: 'All' }, { value: 'read', title: 'Read' }, diff --git a/ui/address/contract/useContractTabs.tsx b/ui/address/contract/useContractTabs.tsx index c9beb57a25..d9b9f3e0c0 100644 --- a/ui/address/contract/useContractTabs.tsx +++ b/ui/address/contract/useContractTabs.tsx @@ -13,7 +13,6 @@ import ContractMethodsCustom from 'ui/address/contract/methods/ContractMethodsCu import ContractMethodsMudSystem from 'ui/address/contract/methods/ContractMethodsMudSystem'; import ContractMethodsProxy from 'ui/address/contract/methods/ContractMethodsProxy'; import ContractMethodsRegular from 'ui/address/contract/methods/ContractMethodsRegular'; -import { enrichWithMethodId, isMethod } from 'ui/address/contract/methods/utils'; import ContentLoader from 'ui/shared/ContentLoader'; import type { CONTRACT_MAIN_TAB_IDS } from './utils'; @@ -68,8 +67,6 @@ export default function useContractTabs(data: Address | undefined, isPlaceholder onSocketError: enableQuery, }); - const methods = React.useMemo(() => contractQuery.data?.abi?.filter(isMethod).map(enrichWithMethodId) ?? [], [ contractQuery.data?.abi ]); - const verifiedImplementations = React.useMemo(() => { return data?.implementations?.filter(({ name, address }) => name && address && address !== data?.hash) || []; }, [ data?.hash, data?.implementations ]); @@ -83,10 +80,10 @@ export default function useContractTabs(data: Address | undefined, isPlaceholder component: , subTabs: CONTRACT_DETAILS_TAB_IDS as unknown as Array, }, - methods.length > 0 && { + contractQuery.data?.abi && { id: [ 'read_write_contract' as const, 'read_contract' as const, 'write_contract' as const ], title: 'Read/Write contract', - component: , + component: , }, verifiedImplementations.length > 0 && { id: [ 'read_write_proxy' as const, 'read_proxy' as const, 'write_proxy' as const ], @@ -112,7 +109,6 @@ export default function useContractTabs(data: Address | undefined, isPlaceholder data?.hash, contractQuery, channel, - methods, verifiedImplementations, hasMudTab, mudSystemsQuery.isPlaceholderData,