Skip to content

Commit

Permalink
Merge branch 'dev' into epic-tx-flow
Browse files Browse the repository at this point in the history
  • Loading branch information
iamacook committed Jul 11, 2023
2 parents 9792587 + f5e7f5d commit cbf8119
Show file tree
Hide file tree
Showing 12 changed files with 232 additions and 73 deletions.
6 changes: 4 additions & 2 deletions cypress/e2e/smoke/create_safe_simple.cy.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ describe('Create Safe form', () => {
cy.contains('button', 'Accept selection').click()

// Ensure wallet is connected to correct chain via header
cy.contains('E2E Wallet @ Goerli')
cy.contains(/E2E Wallet @ G(ö|oe)rli/)

cy.contains('Create new Account').click()
})
Expand All @@ -33,7 +33,9 @@ describe('Create Safe form', () => {

// Switch back to Görli
cy.get('[data-cy="create-safe-select-network"]').click()
cy.contains('li span', 'Goerli').click()

// Prevent Base Mainnet Goerli from being selected
cy.contains('li span', /^G(ö|oe)rli$/).click()

cy.contains('button', 'Next').click()
})
Expand Down
10 changes: 6 additions & 4 deletions cypress/e2e/smoke/nfts.cy.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ describe('Assets > NFTs', () => {

cy.visit(`/balances/nfts?safe=${TEST_SAFE}`)
cy.contains('button', 'Accept selection').click()
cy.contains('E2E Wallet @ Goerli')
cy.contains(/E2E Wallet @ G(ö|oe)rli/)
})

describe('should have NFTs', () => {
Expand All @@ -18,7 +18,7 @@ describe('Assets > NFTs', () => {
cy.get('tbody tr:first-child').contains('td:first-child', 'BillyNFT721')
cy.get('tbody tr:first-child').contains('td:first-child', '0x0000...816D')

cy.get('tbody tr:first-child').contains('td:nth-child(2)', 'Kitaro #261')
cy.get('tbody tr:first-child').contains('td:nth-child(2)', 'Kitaro World #261')

cy.get(
'tbody tr:first-child td:nth-child(3) a[href="https://testnets.opensea.io/assets/0x000000000faE8c6069596c9C805A1975C657816D/443"]',
Expand All @@ -30,8 +30,10 @@ describe('Assets > NFTs', () => {
cy.get('tbody tr:first-child td:nth-child(2)').click()

// Modal
cy.get('div[role="dialog"]').contains('Kitaro #261')
cy.get('div[role="dialog"]').contains('Goerli')
cy.get('div[role="dialog"]').contains('Kitaro World #261')

// Prevent Base Mainnet Goerli from being selected
cy.get('div[role="dialog"]').contains(/^G(ö|oe)rli$/)
cy.get('div[role="dialog"]').contains(
'a[href="https://testnets.opensea.io/assets/0x000000000faE8c6069596c9C805A1975C657816D/443"]',
'View on OpenSea',
Expand Down
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@
"pre-commit": [
"lint"
],
"resolutions": {
"@web3-onboard/trezor/**/protobufjs": "^7.2.4"
},
"dependencies": {
"@date-io/date-fns": "^2.15.0",
"@emotion/cache": "^11.10.1",
Expand Down
18 changes: 15 additions & 3 deletions src/components/new-safe/create/steps/ReviewStep/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,15 @@ import { ExecutionMethodSelector, ExecutionMethod } from '@/components/tx/Execut
import { useLeastRemainingRelays } from '@/hooks/useRemainingRelays'
import classnames from 'classnames'
import { hasRemainingRelays } from '@/utils/relaying'
import { BigNumber } from 'ethers'

const ReviewStep = ({ data, onSubmit, onBack, setStep }: StepRenderProps<NewSafeFormData>) => {
const isWrongChain = useIsWrongChain()
useSyncSafeCreationStep(setStep)
const chain = useCurrentChain()
const wallet = useWallet()
const provider = useWeb3()
const { maxFeePerGas, maxPriorityFeePerGas } = useGasPrice()
const [gasPrice] = useGasPrice()
const saltNonce = useMemo(() => Date.now(), [])
const [_, setPendingSafe] = useLocalStorage<PendingSafeData | undefined>(SAFE_PENDING_CREATION_STORAGE_KEY)
const [executionMethod, setExecutionMethod] = useState(ExecutionMethod.RELAY)
Expand All @@ -55,9 +56,20 @@ const ReviewStep = ({ data, onSubmit, onBack, setStep }: StepRenderProps<NewSafe

const { gasLimit } = useEstimateSafeCreationGas(safeParams)

const maxFeePerGas = gasPrice?.maxFeePerGas
const maxPriorityFeePerGas = gasPrice?.maxPriorityFeePerGas

const totalFee =
gasLimit && maxFeePerGas && maxPriorityFeePerGas
? formatVisualAmount(maxFeePerGas.add(maxPriorityFeePerGas).mul(gasLimit), chain?.nativeCurrency.decimals)
gasLimit && maxFeePerGas
? formatVisualAmount(
maxFeePerGas
.add(
// maxPriorityFeePerGas is undefined if EIP-1559 disabled
maxPriorityFeePerGas || BigNumber.from(0),
)
.mul(gasLimit),
chain?.nativeCurrency.decimals,
)
: '> 0.001'

const handleBack = () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ import { waitFor } from '@testing-library/react'
import type Safe from '@safe-global/safe-core-sdk'
import { hexZeroPad } from 'ethers/lib/utils'
import type CompatibilityFallbackHandlerEthersContract from '@safe-global/safe-ethers-lib/dist/src/contracts/CompatibilityFallbackHandler/CompatibilityFallbackHandlerEthersContract'
import { FEATURES } from '@/utils/chains'
import * as gasPrice from '@/hooks/useGasPrice'

const mockSafeInfo = {
data: '0x',
Expand Down Expand Up @@ -45,6 +47,7 @@ describe('useSafeCreation', () => {

const mockChain = {
chainId: '4',
features: [],
} as unknown as ChainInfo

jest.spyOn(web3, 'useWeb3').mockImplementation(() => mockProvider)
Expand All @@ -56,15 +59,87 @@ describe('useSafeCreation', () => {
jest
.spyOn(contracts, 'getReadOnlyFallbackHandlerContract')
.mockReturnValue({ getAddress: () => hexZeroPad('0x123', 20) } as CompatibilityFallbackHandlerEthersContract)
jest
.spyOn(gasPrice, 'default')
.mockReturnValue([{ maxFeePerGas: BigNumber.from(123), maxPriorityFeePerGas: undefined }, undefined, false])
})

it('should create a safe if there is no txHash and status is AWAITING', async () => {
it('should create a safe with gas params if there is no txHash and status is AWAITING', async () => {
const createSafeSpy = jest.spyOn(logic, 'createNewSafe').mockReturnValue(Promise.resolve({} as Safe))

renderHook(() => useSafeCreation(mockPendingSafe, mockSetPendingSafe, mockStatus, mockSetStatus, false))

await waitFor(() => {
expect(createSafeSpy).toHaveBeenCalled()

const { gasPrice, maxFeePerGas, maxPriorityFeePerGas } = createSafeSpy.mock.calls[0][1].options || {}

expect(gasPrice).toBe('123')

expect(maxFeePerGas).toBeUndefined()
expect(maxPriorityFeePerGas).toBeUndefined()
})
})

it('should create a safe with EIP-1559 gas params if there is no txHash and status is AWAITING', async () => {
jest
.spyOn(gasPrice, 'default')
.mockReturnValue([
{ maxFeePerGas: BigNumber.from(123), maxPriorityFeePerGas: BigNumber.from(456) },
undefined,
false,
])

jest.spyOn(chain, 'useCurrentChain').mockImplementation(
() =>
({
chainId: '4',
features: [FEATURES.EIP1559],
} as unknown as ChainInfo),
)
const createSafeSpy = jest.spyOn(logic, 'createNewSafe').mockReturnValue(Promise.resolve({} as Safe))

renderHook(() => useSafeCreation(mockPendingSafe, mockSetPendingSafe, mockStatus, mockSetStatus, false))

await waitFor(() => {
expect(createSafeSpy).toHaveBeenCalled()

const { gasPrice, maxFeePerGas, maxPriorityFeePerGas } = createSafeSpy.mock.calls[0][1].options || {}

expect(maxFeePerGas).toBe('123')
expect(maxPriorityFeePerGas).toBe('456')

expect(gasPrice).toBeUndefined()
})
})

it('should create a safe with no gas params if the gas estimation threw, there is no txHash and status is AWAITING', async () => {
jest.spyOn(gasPrice, 'default').mockReturnValue([undefined, Error('Error for testing'), false])

const createSafeSpy = jest.spyOn(logic, 'createNewSafe').mockReturnValue(Promise.resolve({} as Safe))

renderHook(() => useSafeCreation(mockPendingSafe, mockSetPendingSafe, mockStatus, mockSetStatus, false))

await waitFor(() => {
expect(createSafeSpy).toHaveBeenCalled()

const { gasPrice, maxFeePerGas, maxPriorityFeePerGas } = createSafeSpy.mock.calls[0][1].options || {}

expect(gasPrice).toBeUndefined()
expect(maxFeePerGas).toBeUndefined()
expect(maxPriorityFeePerGas).toBeUndefined()
})
})

it('should not create a safe if there is no txHash, status is AWAITING but gas is loading', async () => {
jest.spyOn(gasPrice, 'default').mockReturnValue([undefined, undefined, true])

const createSafeSpy = jest.spyOn(logic, 'createNewSafe').mockReturnValue(Promise.resolve({} as Safe))

renderHook(() => useSafeCreation(mockPendingSafe, mockSetPendingSafe, mockStatus, mockSetStatus, false))

await waitFor(() => {
expect(createSafeSpy).not.toHaveBeenCalled()
})
})

Expand Down
25 changes: 23 additions & 2 deletions src/components/new-safe/create/steps/StatusStep/useSafeCreation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ import { useAppDispatch } from '@/store'
import { closeByGroupKey } from '@/store/notificationsSlice'
import { CREATE_SAFE_EVENTS, trackEvent } from '@/services/analytics'
import { waitForCreateSafeTx } from '@/services/tx/txMonitor'
import useGasPrice from '@/hooks/useGasPrice'
import { hasFeature } from '@/utils/chains'
import { FEATURES } from '@safe-global/safe-gateway-typescript-sdk'
import type { DeploySafeProps } from '@safe-global/safe-core-sdk'

export enum SafeCreationStatus {
AWAITING,
Expand Down Expand Up @@ -48,6 +52,12 @@ export const useSafeCreation = (
const provider = useWeb3()
const web3ReadOnly = useWeb3ReadOnly()
const chain = useCurrentChain()
const [gasPrice, , gasPriceLoading] = useGasPrice()

const maxFeePerGas = gasPrice?.maxFeePerGas
const maxPriorityFeePerGas = gasPrice?.maxPriorityFeePerGas

const isEIP1559 = chain && hasFeature(chain, FEATURES.EIP1559)

const createSafeCallback = useCallback(
async (txHash: string, tx: PendingSafeTx) => {
Expand All @@ -59,7 +69,7 @@ export const useSafeCreation = (
)

const handleCreateSafe = useCallback(async () => {
if (!pendingSafe || !provider || !chain || !wallet || isCreating) return
if (!pendingSafe || !provider || !chain || !wallet || isCreating || gasPriceLoading) return

setIsCreating(true)
dispatch(closeByGroupKey({ groupKey: SAFE_CREATION_ERROR_KEY }))
Expand Down Expand Up @@ -87,7 +97,14 @@ export const useSafeCreation = (
chain.chainId,
)

await createNewSafe(provider, safeParams)
const options: DeploySafeProps['options'] = isEIP1559
? { maxFeePerGas: maxFeePerGas?.toString(), maxPriorityFeePerGas: maxPriorityFeePerGas?.toString() }
: { gasPrice: maxFeePerGas?.toString() }

await createNewSafe(provider, {
...safeParams,
options,
})
setStatus(SafeCreationStatus.SUCCESS)
}
} catch (err) {
Expand All @@ -106,7 +123,11 @@ export const useSafeCreation = (
chain,
createSafeCallback,
dispatch,
gasPriceLoading,
isCreating,
isEIP1559,
maxFeePerGas,
maxPriorityFeePerGas,
pendingSafe,
provider,
setPendingSafe,
Expand Down
30 changes: 22 additions & 8 deletions src/components/tx-flow/flows/ExecuteBatch/ReviewBatch.tsx
Original file line number Diff line number Diff line change
@@ -1,26 +1,26 @@
import { Typography, Button, CardActions, Divider, Alert } from '@mui/material'
import useAsync from '@/hooks/useAsync'
import { FEATURES } from '@safe-global/safe-gateway-typescript-sdk'
import type { TransactionDetails } from '@safe-global/safe-gateway-typescript-sdk'
import { getMultiSendCallOnlyContract } from '@/services/contracts/safeContracts'
import { useCurrentChain } from '@/hooks/useChains'
import useSafeInfo from '@/hooks/useSafeInfo'
import { encodeMultiSendData } from '@safe-global/safe-core-sdk/dist/src/utils/transactions/utils'
import { useState, useMemo, useContext } from 'react'
import type { SyntheticEvent } from 'react'
import type { TransactionDetails } from '@safe-global/safe-gateway-typescript-sdk'

import { generateDataRowValue } from '@/components/transactions/TxDetails/Summary/TxDataRow'
import ErrorMessage from '@/components/tx/ErrorMessage'
import { ExecutionMethod, ExecutionMethodSelector } from '@/components/tx/ExecutionMethodSelector'
import DecodedTxs from '@/components/tx-flow/flows/ExecuteBatch/DecodedTxs'
import { TxSimulation } from '@/components/tx/security/tenderly'
import { WrongChainWarning } from '@/components/tx/WrongChainWarning'
import useAsync from '@/hooks/useAsync'
import { useCurrentChain } from '@/hooks/useChains'
import { useRelaysBySafe } from '@/hooks/useRemainingRelays'
import useSafeInfo from '@/hooks/useSafeInfo'
import useOnboard from '@/hooks/wallets/useOnboard'
import { useWeb3 } from '@/hooks/wallets/web3'
import { logError, Errors } from '@/services/exceptions'
import { dispatchBatchExecution, dispatchBatchExecutionRelay } from '@/services/tx/tx-sender'
import { hasRemainingRelays } from '@/utils/relaying'
import { getTxsWithDetails, getMultiSendTxs } from '@/utils/transactions'
import { getMultiSendCallOnlyContract } from '@/services/contracts/safeContracts'
import TxCard from '../../common/TxCard'
import CheckWallet from '@/components/common/CheckWallet'
import type { ExecuteBatchFlowProps } from '.'
Expand All @@ -29,6 +29,9 @@ import SendToBlock from '@/components/tx-flow/flows/TokenTransfer/SendToBlock'
import ConfirmationTitle, { ConfirmationTitleTypes } from '@/components/tx/SignOrExecuteForm/ConfirmationTitle'
import commonCss from '@/components/tx-flow/common/styles.module.css'
import { TxModalContext } from '@/components/tx-flow'
import useGasPrice from '@/hooks/useGasPrice'
import { hasFeature } from '@/utils/chains'
import type { PayableOverrides } from 'ethers'

export const ReviewBatch = ({ params }: { params: ExecuteBatchFlowProps }) => {
const [isSubmittable, setIsSubmittable] = useState<boolean>(true)
Expand All @@ -38,6 +41,12 @@ export const ReviewBatch = ({ params }: { params: ExecuteBatchFlowProps }) => {
const { safe } = useSafeInfo()
const [relays] = useRelaysBySafe()
const { setTxFlow } = useContext(TxModalContext)
const [gasPrice, , gasPriceLoading] = useGasPrice()

const maxFeePerGas = gasPrice?.maxFeePerGas
const maxPriorityFeePerGas = gasPrice?.maxPriorityFeePerGas

const isEIP1559 = chain && hasFeature(chain, FEATURES.EIP1559)

// Chain has relaying feature and available relays
const canRelay = hasRemainingRelays(relays)
Expand Down Expand Up @@ -66,7 +75,11 @@ export const ReviewBatch = ({ params }: { params: ExecuteBatchFlowProps }) => {
}, [txsWithDetails, multiSendTxs])

const onExecute = async () => {
if (!onboard || !multiSendTxData || !multiSendContract || !txsWithDetails) return
if (!onboard || !multiSendTxData || !multiSendContract || !txsWithDetails || gasPriceLoading) return

const overrides: PayableOverrides = isEIP1559
? { maxFeePerGas: maxFeePerGas?.toString(), maxPriorityFeePerGas: maxPriorityFeePerGas?.toString() }
: { gasPrice: maxFeePerGas?.toString() }

await dispatchBatchExecution(
txsWithDetails,
Expand All @@ -75,6 +88,7 @@ export const ReviewBatch = ({ params }: { params: ExecuteBatchFlowProps }) => {
onboard,
safe.chainId,
safe.address.value,
overrides,
)
}

Expand Down Expand Up @@ -107,7 +121,7 @@ export const ReviewBatch = ({ params }: { params: ExecuteBatchFlowProps }) => {
}
}

const submitDisabled = loading || !isSubmittable
const submitDisabled = loading || !isSubmittable || gasPriceLoading

return (
<>
Expand Down
8 changes: 4 additions & 4 deletions src/components/tx/AdvancedParams/useAdvancedParams.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,17 @@ export const useAdvancedParams = (
gasLimit?: AdvancedParameters['gasLimit'],
): [AdvancedParameters, (params: AdvancedParameters) => void] => {
const [manualParams, setManualParams] = useState<AdvancedParameters>()
const { maxFeePerGas, maxPriorityFeePerGas } = useGasPrice()
const [gasPrice] = useGasPrice()
const userNonce = useUserNonce()

const advancedParams: AdvancedParameters = useMemo(
() => ({
userNonce: manualParams?.userNonce ?? userNonce,
gasLimit: manualParams?.gasLimit ?? gasLimit,
maxFeePerGas: manualParams?.maxFeePerGas ?? maxFeePerGas,
maxPriorityFeePerGas: manualParams?.maxPriorityFeePerGas ?? maxPriorityFeePerGas,
maxFeePerGas: manualParams?.maxFeePerGas ?? gasPrice?.maxFeePerGas,
maxPriorityFeePerGas: manualParams?.maxPriorityFeePerGas ?? gasPrice?.maxPriorityFeePerGas,
}),
[manualParams, userNonce, gasLimit, maxFeePerGas, maxPriorityFeePerGas],
[manualParams, userNonce, gasLimit, gasPrice?.maxFeePerGas, gasPrice?.maxPriorityFeePerGas],
)

return [advancedParams, setManualParams]
Expand Down
Loading

0 comments on commit cbf8119

Please sign in to comment.