diff --git a/src/components/FormPopover.tsx b/src/components/FormPopover.tsx
new file mode 100644
index 0000000..a27148a
--- /dev/null
+++ b/src/components/FormPopover.tsx
@@ -0,0 +1,68 @@
+import * as Popover from '@radix-ui/react-popover'
+import { type ReactNode, useState } from 'react'
+
+import { Box, Button, Stack } from '~/design-system'
+import * as Form from './form'
+
+export function FormPopover({
+ children,
+ disabled,
+ onSubmit,
+}: {
+ children: ReactNode
+ disabled: boolean
+ onSubmit: (e: React.FormEvent) => void
+}) {
+ const [open, setOpen] = useState(false)
+
+ return (
+
+
+
+ setOpen(true)}
+ symbol="square.and.pencil"
+ variant="ghost primary"
+ />
+
+
+
+ setOpen(false)}
+ onPointerDownOutside={() => setOpen(false)}
+ style={{ zIndex: 1 }}
+ >
+
+ {
+ onSubmit(e)
+ setOpen(false)
+ }}
+ >
+
+ {children}
+
+
+
+
+
+
+
+ )
+}
diff --git a/src/components/index.ts b/src/components/index.ts
index eebadff..91b9463 100644
--- a/src/components/index.ts
+++ b/src/components/index.ts
@@ -9,6 +9,7 @@ export { DecodedLogs } from './logs'
export { TabsContent, TabsList } from './tabs'
export { Container } from './Container'
+export { FormPopover } from './FormPopover'
export { Header } from './Header'
export { LabelledContent } from './LabelledContent'
export { LoadMore } from './LoadMore'
diff --git a/src/screens/contract-details.tsx b/src/screens/contract-details.tsx
index 5af7640..ccc1377 100644
--- a/src/screens/contract-details.tsx
+++ b/src/screens/contract-details.tsx
@@ -2,15 +2,18 @@ import * as Tabs from '@radix-ui/react-tabs'
import { useMutation } from '@tanstack/react-query'
import type { Abi, AbiFunction } from 'abitype'
import { useMemo } from 'react'
+import { useForm } from 'react-hook-form'
import { useParams } from 'react-router'
import {
AbiFunctionsAccordion,
Container,
+ FormPopover,
LabelledContent,
TabsContent,
TabsList,
} from '~/components'
+import * as Form from '~/components/form'
import { Bleed, Box, Stack, Text } from '~/design-system'
import { useAutoloadAbi } from '~/hooks/useAutoloadAbi'
import { useContracts } from '~/hooks/useContracts'
@@ -20,11 +23,19 @@ export default function ContractDetails() {
const { contracts, updateContract } = useContracts({ enabled: false })
const contract = contracts.find((c) => c.address === contractAddress)
- const {
- data: autoloadAbi,
- isLoading,
- isFetched,
- } = useAutoloadAbi({
+ ////////////////////////////////////////////////////////////////////////
+
+ const { formState, register, handleSubmit } = useForm({
+ defaultValues: { name: contract?.name },
+ })
+
+ const update = handleSubmit(({ name }) => {
+ updateContract({ address: contract?.address!, name })
+ })
+
+ ////////////////////////////////////////////////////////////////////////
+
+ const { data: autoloadAbi, isLoading } = useAutoloadAbi({
address: contract?.address,
enabled: Boolean(!contract?.abi && contract?.address),
})
@@ -66,18 +77,40 @@ export default function ContractDetails() {
return [read as {} as AbiFunction[], write as {} as AbiFunction[]]
}, [abiFunctions, hasStateMutability])
+ ////////////////////////////////////////////////////////////////////////
+
return (
-
-
- {contract?.address}
-
-
+
+
+
+ }
+ >
+ {contract?.name ?? 'Unnamed Contract'}
+
+
+ {contract?.address}
+
{(isGuessedAbi || contract?.abi) && (
- updateContract({ abi, address: contract?.address! })
+ onUpload={({ abi, file }) =>
+ updateContract({
+ abi,
+ address: contract?.address!,
+ name: file.name.replace('.json', ''),
+ })
}
/>
)}
@@ -98,7 +131,7 @@ export default function ContractDetails() {
Loading...
)}
- {isFetched && (
+ {abi && (
{hasStateMutability ? (
@@ -150,7 +183,9 @@ export default function ContractDetails() {
)
}
-function UploadAbi({ onUpload }: { onUpload: (abi: { abi: Abi }) => void }) {
+function UploadAbi({
+ onUpload,
+}: { onUpload: (abi: { abi: Abi; file: File }) => void }) {
const { error, mutateAsync: uploadAbi } = useMutation({
async mutationFn(files: FileList | null) {
if (!files) return
@@ -166,7 +201,7 @@ function UploadAbi({ onUpload }: { onUpload: (abi: { abi: Abi }) => void }) {
if (!isAbi)
throw new Error('ABI is not valid. Please upload a valid ABI.')
- onUpload({ abi })
+ onUpload({ abi, file })
},
})
diff --git a/src/screens/index.tsx b/src/screens/index.tsx
index 3992c1c..20c8118 100644
--- a/src/screens/index.tsx
+++ b/src/screens/index.tsx
@@ -833,7 +833,7 @@ function Contracts() {
(_, index) =>
({
index,
- size: 32,
+ size: 40,
type: 'item',
}) as const,
),
@@ -908,19 +908,39 @@ function Contracts() {
backgroundColor={{ hover: 'surface/fill/quarternary' }}
paddingHorizontal="8px"
paddingVertical="8px"
- height="full"
+ style={{ minHeight: '40px' }}
>
-
+
- {contract.address !== '0x' ? (
-
- {contract.address}
-
- ) : (
-
- Deploying...
-
- )}
+
+ {contract.address !== '0x' ? (
+ <>
+
+ {contract.name || 'Unnamed Contract'}
+
+
+ {contract.address}
+
+ >
+ ) : (
+
+ Deploying...
+
+ )}
+
+
-
+
}
>
@@ -277,10 +274,7 @@ function SendTransactionRequest(args: {
+
-
+
}
>
@@ -304,10 +298,7 @@ function SendTransactionRequest(args: {
+
-
+
}
>
@@ -340,10 +331,7 @@ function SendTransactionRequest(args: {
+
-
+
}
>
@@ -367,10 +355,7 @@ function SendTransactionRequest(args: {
+
-
+
}
>
@@ -406,10 +391,7 @@ function SendTransactionRequest(args: {
+
-
+
}
>
@@ -447,10 +429,7 @@ function SendTransactionRequest(args: {
+
-
+
}
>
{nonce}
@@ -487,7 +466,7 @@ function SendTransactionRequest(args: {
address={to}
data={data || '0x'}
labelRight={
-
@@ -502,7 +481,7 @@ function SendTransactionRequest(args: {
})}
style={{ width: '360px' }}
/>
-
+
}
/>
@@ -518,69 +497,6 @@ function SendTransactionRequest(args: {
)
}
-function UpdateFieldPopover({
- children,
- disabled,
- onSubmit,
-}: {
- children: ReactNode
- disabled: boolean
- onSubmit: (e: React.FormEvent) => void
-}) {
- const [open, setOpen] = useState(false)
-
- return (
-
-
-
- setOpen(true)}
- symbol="square.and.pencil"
- variant="ghost primary"
- />
-
-
-
- setOpen(false)}
- onPointerDownOutside={() => setOpen(false)}
- style={{ zIndex: 1 }}
- >
-
- {
- onSubmit(e)
- setOpen(false)
- }}
- >
-
- {children}
-
-
-
-
-
-
-
- )
-}
-
////////////////////////////////////////////////////////////////////////
// Request Accounts View
diff --git a/src/zustand/contracts.ts b/src/zustand/contracts.ts
index 77324e2..d5ca6af 100644
--- a/src/zustand/contracts.ts
+++ b/src/zustand/contracts.ts
@@ -1,3 +1,4 @@
+import { selectorsFromBytecode } from '@shazow/whatsabi'
import type { Abi, Address } from 'abitype'
import type { Hex, TransactionReceipt } from 'viem'
import { useStore } from 'zustand'
@@ -9,6 +10,7 @@ type Contract = {
abi?: Abi
address: Address
bytecode?: Hex | null
+ name?: string
key: string
receipt?: TransactionReceipt
state: 'loaded' | 'loading'
@@ -72,7 +74,14 @@ export const contractsStore = createStore(
set((state) => {
const contracts = { ...state.contracts }
+ const { abi, name } = getContractAbi({
+ contracts,
+ contract,
+ })
+
const contract_ = {
+ abi,
+ name,
key: `${contract.address}-${contract.bytecode}`,
state: 'loaded',
visible: true,
@@ -162,9 +171,15 @@ export const contractsStore = createStore(
const exists = (contracts[serializedKey] || []).some(
(x) => x.address === contract.address,
)
- if (!exists)
+ if (!exists) {
+ const { abi, name } = getContractAbi({
+ contracts,
+ contract,
+ })
contracts[serializedKey] = [
{
+ abi,
+ name,
...contract,
key: contract.address,
state: 'loaded',
@@ -172,6 +187,7 @@ export const contractsStore = createStore(
},
...(contracts[serializedKey] || []),
]
+ }
}
contracts[serializedKey] = contracts[serializedKey]?.filter(
@@ -194,3 +210,27 @@ export const contractsStore = createStore(
)
export const useContractsStore = () => useStore(contractsStore)
+
+///////////////////////////////////////////////////////////////
+
+function getContractAbi({
+ contracts,
+ contract,
+}: { contract: Partial; contracts: ContractsState['contracts'] }) {
+ if (contract.abi) return contract
+
+ const allContracts = Object.values(contracts).flat()
+ const contracts_ = allContracts.filter(
+ (c) =>
+ c?.abi &&
+ c.bytecode &&
+ contract.bytecode &&
+ selectorsFromBytecode(c.bytecode).join() ===
+ selectorsFromBytecode(contract.bytecode).join(),
+ )
+ const contract_ = contracts_?.[0]
+ return {
+ abi: contract_?.abi,
+ name: contract_?.name,
+ }
+}