Skip to content

Commit

Permalink
fix: select available signer in SafeTxProvider, disable signers that …
Browse files Browse the repository at this point in the history
…already signed
  • Loading branch information
schmanu committed Nov 5, 2024
1 parent 4f5cb58 commit edcf876
Show file tree
Hide file tree
Showing 6 changed files with 74 additions and 24 deletions.
5 changes: 3 additions & 2 deletions src/components/common/WalletProvider/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { useCurrentChain } from '@/hooks/useChains'
import { useRouter } from 'next/router'
import { type Eip1193Provider } from 'ethers'
import { getNestedWallet } from '@/utils/nested-safe-wallet'
import { sameAddress } from '@/utils/addresses'

export type SignerWallet = {
provider: Eip1193Provider | null
Expand Down Expand Up @@ -34,10 +35,10 @@ const WalletProvider = ({ children }: { children: ReactNode }): ReactElement =>
const [signerAddress, setSignerAddress] = useState<string>()

const [nestedSafeInfo] = useAsync(() => {
if (signerAddress && currentChain) {
if (signerAddress && !sameAddress(signerAddress, wallet?.address) && currentChain) {
return getSafeInfo(currentChain.chainId, signerAddress)
}
}, [currentChain, signerAddress])
}, [currentChain, signerAddress, wallet?.address])

useEffect(() => {
if (!onboard) return
Expand Down
5 changes: 4 additions & 1 deletion src/components/tx-flow/SafeTxProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import type { EIP712TypedData } from '@safe-global/safe-gateway-typescript-sdk'
import useSafeInfo from '@/hooks/useSafeInfo'
import { useCurrentChain } from '@/hooks/useChains'
import { prependSafeToL2Migration } from '@/utils/transactions'
import { useSelectAvailableSigner } from '@/hooks/wallets/useAvailableSigner'

export type SafeTxContextParams = {
safeTx?: SafeTransaction
Expand Down Expand Up @@ -84,12 +85,14 @@ const SafeTxProvider = ({ children }: { children: ReactNode }): ReactElement =>

createTx({ ...safeTx.data, safeTxGas: String(finalSafeTxGas) }, finalNonce)
.then((tx) => {
console.log('SafeTxProvider: Updated tx with nonce and safeTxGas', tx)
setSafeTx(tx)
})
.catch(setSafeTxError)
}, [isSigned, finalNonce, finalSafeTxGas, safeTx?.data])

// Update the available signers based on the transaction
useSelectAvailableSigner(safeTx, safe)

// Log errors
useEffect(() => {
safeTxError && logError(Errors._103, safeTxError)
Expand Down
35 changes: 16 additions & 19 deletions src/components/tx/SignOrExecuteForm/SignerForm/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,31 +12,35 @@ import {
import { useNestedSafeOwners } from '@/hooks/useNestedSafeOwners'
import { useWalletContext } from '@/hooks/wallets/useWallet'
import EthHashInfo from '@/components/common/EthHashInfo'
import { useEffect, useMemo } from 'react'
import { useCallback, useContext, useMemo } from 'react'
import useSafeInfo from '@/hooks/useSafeInfo'
import TxCard from '@/components/tx-flow/common/TxCard'
import InfoIcon from '@/public/images/notifications/info.svg'
import SignatureIcon from '@/public/images/transactions/signature.svg'

import css from './styles.module.css'
import { sameAddress } from '@/utils/addresses'
import { SafeTxContext } from '@/components/tx-flow/SafeTxProvider'
import useAvailableSigners from '@/hooks/wallets/useAvailableSigner'

export const SignerForm = () => {
const { signer, setSignerAddress, connectedWallet: wallet } = useWalletContext() ?? {}
const nestedSafeOwners = useNestedSafeOwners()
const signerAddress = signer?.address
const { safe } = useSafeInfo()
const { safeTx } = useContext(SafeTxContext)

const availableSigners = useAvailableSigners(safeTx, safe)

const onChange = (event: SelectChangeEvent<string>) => {
setSignerAddress?.(event.target.value)
}

const isOwner = useMemo(
() => safe.owners.map((owner) => owner.value).includes(wallet?.address ?? ''),
[wallet?.address, safe.owners],
)

const isNotNestedOwner = useMemo(() => nestedSafeOwners && nestedSafeOwners.length === 0, [nestedSafeOwners])
const isOptionEnabled = useCallback(
(address: string) => availableSigners.some((available) => sameAddress(available, address)),
[availableSigners],
)

const options = useMemo(
() =>
Expand All @@ -48,18 +52,6 @@ export const SignerForm = () => {
[nestedSafeOwners, safe.owners, wallet],
)

useEffect(() => {
if (signerAddress) {
return
}
if (!isOwner && nestedSafeOwners && nestedSafeOwners.length > 0) {
setSignerAddress?.(nestedSafeOwners[0])
}
if (isOwner && (!nestedSafeOwners || nestedSafeOwners.length === 0)) {
setSignerAddress?.(undefined)
}
}, [isOwner, nestedSafeOwners, setSignerAddress, signerAddress])

if (!wallet || isNotNestedOwner) {
return null
}
Expand Down Expand Up @@ -90,8 +82,13 @@ export const SignerForm = () => {
value={signerAddress ?? options[0]}
>
{options?.map((owner) => (
<MenuItem key={owner} value={owner}>
<MenuItem key={owner} value={owner} disabled={!isOptionEnabled(owner)}>
<EthHashInfo address={owner} avatarSize={32} onlyName />
{!isOptionEnabled(owner) && (
<Typography variant="caption" component="span" className={css.disabledPill}>
Already signed
</Typography>
)}
</MenuItem>
))}
</Select>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
.signerForm :global .MuiOutlinedInput-notchedOutline {
border: 1px solid var(--color-border-light) !important;
}

.disabledPill {
background-color: var(--color-border-light);
border-radius: 4px;
color: var(--color-text-primary);
padding: 4px 8px;
}
4 changes: 2 additions & 2 deletions src/components/tx/SignOrExecuteForm/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,8 @@ type txDetails = AsyncResult<TransactionDetails>

export const useProposeTx = (safeTx?: SafeTransaction, txId?: string, origin?: string): txDetails => {
const { safe } = useSafeInfo()
const wallet = useWallet()
const sender = wallet?.address || safe.owners?.[0]?.value
const signer = useSigner()
const sender = signer?.address || safe.owners?.[0]?.value

return useAsync(
async () => {
Expand Down
42 changes: 42 additions & 0 deletions src/hooks/wallets/useAvailableSigner.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { useMemo, useEffect } from 'react'
import { useWalletContext } from './useWallet'
import type { SafeInfo } from '@safe-global/safe-gateway-typescript-sdk'
import type { SafeTransaction } from '@safe-global/safe-core-sdk-types'
import { useNestedSafeOwners } from '../useNestedSafeOwners'
import { sameAddress } from '@/utils/addresses'

const useAvailableSigners = (tx: SafeTransaction | undefined, safe: SafeInfo) => {
const { connectedWallet: wallet } = useWalletContext() ?? {}

const nestedSafeOwners = useNestedSafeOwners()

const isDirectOwner = useMemo(
() => typeof wallet?.address === 'string' && safe.owners.map((owner) => owner.value).includes(wallet.address),
[wallet?.address, safe],
)

return useMemo(() => {
const result = nestedSafeOwners?.filter((owner) => !tx?.signatures.has(owner.toLowerCase())) ?? []
if (wallet?.address && isDirectOwner && !tx?.signatures.has(wallet.address.toLowerCase())) {
result?.push(wallet.address)
}
return result
}, [isDirectOwner, nestedSafeOwners, tx?.signatures, wallet?.address])
}

export const useSelectAvailableSigner = (tx: SafeTransaction | undefined, safe: SafeInfo) => {
const { signer, setSignerAddress } = useWalletContext() ?? {}
const availableSigners = useAvailableSigners(tx, safe)

// Sets the first available signer if non is set
useEffect(() => {
// Make sure the current signer can sign the tx
if (availableSigners.some((available) => sameAddress(signer?.address, available))) {
return
}

setSignerAddress?.(availableSigners[0])
}, [availableSigners, safe, setSignerAddress, signer?.address, tx])
}

export default useAvailableSigners

0 comments on commit edcf876

Please sign in to comment.