diff --git a/src/components/new-safe/create/logic/index.test.ts b/src/components/new-safe/create/logic/index.test.ts index 368507bbb9..652004ec79 100644 --- a/src/components/new-safe/create/logic/index.test.ts +++ b/src/components/new-safe/create/logic/index.test.ts @@ -7,6 +7,7 @@ import { relaySafeCreation, handleSafeCreationError, } from '@/components/new-safe/create/logic/index' + import { type ErrorCode } from 'ethers' import { EthersTxReplacedReason } from '@/utils/ethers-utils' import { SafeCreationStatus } from '@/components/new-safe/create/steps/StatusStep/useSafeCreation' @@ -57,235 +58,237 @@ jest.mock('@safe-global/protocol-kit', () => { } }) -describe('checkSafeCreationTx', () => { - let waitForTxSpy = jest.spyOn(provider, 'waitForTransaction') +describe('create logic', () => { + describe('checkSafeCreationTx', () => { + let waitForTxSpy = jest.spyOn(provider, 'waitForTransaction') - beforeEach(() => { - jest.resetAllMocks() + beforeEach(() => { + jest.resetAllMocks() - jest.spyOn(web3, 'getWeb3ReadOnly').mockImplementation(() => provider) + jest.spyOn(web3, 'getWeb3ReadOnly').mockImplementation(() => provider) - waitForTxSpy = jest.spyOn(provider, 'waitForTransaction') - jest.spyOn(provider, 'getBlockNumber').mockReturnValue(Promise.resolve(4)) - jest.spyOn(provider, 'getTransaction').mockReturnValue(Promise.resolve(mockTransaction as TransactionResponse)) - }) + waitForTxSpy = jest.spyOn(provider, 'waitForTransaction') + jest.spyOn(provider, 'getBlockNumber').mockReturnValue(Promise.resolve(4)) + jest.spyOn(provider, 'getTransaction').mockReturnValue(Promise.resolve(mockTransaction as TransactionResponse)) + }) - it('returns SUCCESS if promise was resolved', async () => { - const receipt = { - status: 1, - } as TransactionReceipt + it('returns SUCCESS if promise was resolved', async () => { + const receipt = { + status: 1, + } as TransactionReceipt - waitForTxSpy.mockImplementationOnce(() => Promise.resolve(receipt)) + waitForTxSpy.mockImplementationOnce(() => Promise.resolve(receipt)) - const result = await checkSafeCreationTx(provider, mockPendingTx, '0x0', jest.fn()) + const result = await checkSafeCreationTx(provider, mockPendingTx, '0x0', jest.fn()) - expect(result).toBe(SafeCreationStatus.SUCCESS) - }) + expect(result).toBe(SafeCreationStatus.SUCCESS) + }) - it('returns REVERTED if transaction was reverted', async () => { - const receipt = { - status: 0, - } as TransactionReceipt + it('returns REVERTED if transaction was reverted', async () => { + const receipt = { + status: 0, + } as TransactionReceipt - waitForTxSpy.mockImplementationOnce(() => Promise.resolve(receipt)) + waitForTxSpy.mockImplementationOnce(() => Promise.resolve(receipt)) - const result = await checkSafeCreationTx(provider, mockPendingTx, '0x0', jest.fn()) + const result = await checkSafeCreationTx(provider, mockPendingTx, '0x0', jest.fn()) - expect(result).toBe(SafeCreationStatus.REVERTED) - }) + expect(result).toBe(SafeCreationStatus.REVERTED) + }) - it('returns TIMEOUT if transaction could not be found within the timeout limit', async () => { - const mockEthersError = { - ...new Error(), - code: 'TIMEOUT' as ErrorCode, - } + it('returns TIMEOUT if transaction could not be found within the timeout limit', async () => { + const mockEthersError = { + ...new Error(), + code: 'TIMEOUT' as ErrorCode, + } - waitForTxSpy.mockImplementationOnce(() => Promise.reject(mockEthersError)) + waitForTxSpy.mockImplementationOnce(() => Promise.reject(mockEthersError)) - const result = await checkSafeCreationTx(provider, mockPendingTx, '0x0', jest.fn()) + const result = await checkSafeCreationTx(provider, mockPendingTx, '0x0', jest.fn()) - expect(result).toBe(SafeCreationStatus.TIMEOUT) - }) + expect(result).toBe(SafeCreationStatus.TIMEOUT) + }) - it('returns SUCCESS if transaction was replaced', async () => { - const mockEthersError = { - ...new Error(), - code: 'TRANSACTION_REPLACED', - reason: 'repriced', - } - waitForTxSpy.mockImplementationOnce(() => Promise.reject(mockEthersError)) + it('returns SUCCESS if transaction was replaced', async () => { + const mockEthersError = { + ...new Error(), + code: 'TRANSACTION_REPLACED', + reason: 'repriced', + } + waitForTxSpy.mockImplementationOnce(() => Promise.reject(mockEthersError)) - const result = await checkSafeCreationTx(provider, mockPendingTx, '0x0', jest.fn()) + const result = await checkSafeCreationTx(provider, mockPendingTx, '0x0', jest.fn()) - expect(result).toBe(SafeCreationStatus.SUCCESS) - }) + expect(result).toBe(SafeCreationStatus.SUCCESS) + }) - it('returns ERROR if transaction was cancelled', async () => { - const mockEthersError = { - ...new Error(), - code: 'TRANSACTION_REPLACED', - reason: 'cancelled', - } - waitForTxSpy.mockImplementationOnce(() => Promise.reject(mockEthersError)) + it('returns ERROR if transaction was cancelled', async () => { + const mockEthersError = { + ...new Error(), + code: 'TRANSACTION_REPLACED', + reason: 'cancelled', + } + waitForTxSpy.mockImplementationOnce(() => Promise.reject(mockEthersError)) - const result = await checkSafeCreationTx(provider, mockPendingTx, '0x0', jest.fn()) + const result = await checkSafeCreationTx(provider, mockPendingTx, '0x0', jest.fn()) - expect(result).toBe(SafeCreationStatus.ERROR) + expect(result).toBe(SafeCreationStatus.ERROR) + }) }) -}) -describe('handleSafeCreationError', () => { - it('returns WALLET_REJECTED if the tx was rejected in the wallet', () => { - const mockEthersError = { - ...new Error(), - code: 'ACTION_REJECTED' as ErrorCode, - reason: '' as EthersTxReplacedReason, - receipt: {} as TransactionReceipt, - } + describe('handleSafeCreationError', () => { + it('returns WALLET_REJECTED if the tx was rejected in the wallet', () => { + const mockEthersError = { + ...new Error(), + code: 'ACTION_REJECTED' as ErrorCode, + reason: '' as EthersTxReplacedReason, + receipt: {} as TransactionReceipt, + } - const result = handleSafeCreationError(mockEthersError) + const result = handleSafeCreationError(mockEthersError) - expect(result).toEqual(SafeCreationStatus.WALLET_REJECTED) - }) + expect(result).toEqual(SafeCreationStatus.WALLET_REJECTED) + }) - it('returns WALLET_REJECTED if the tx was rejected via WC', () => { - const mockEthersError = { - ...new Error(), - code: 'UNKNOWN_ERROR' as ErrorCode, - reason: '' as EthersTxReplacedReason, - receipt: {} as TransactionReceipt, - message: 'rejected', - } + it('returns WALLET_REJECTED if the tx was rejected via WC', () => { + const mockEthersError = { + ...new Error(), + code: 'UNKNOWN_ERROR' as ErrorCode, + reason: '' as EthersTxReplacedReason, + receipt: {} as TransactionReceipt, + message: 'rejected', + } - const result = handleSafeCreationError(mockEthersError) + const result = handleSafeCreationError(mockEthersError) - expect(result).toEqual(SafeCreationStatus.WALLET_REJECTED) - }) + expect(result).toEqual(SafeCreationStatus.WALLET_REJECTED) + }) - it('returns ERROR if the tx was cancelled', () => { - const mockEthersError = { - ...new Error(), - code: 'TRANSACTION_REPLACED' as ErrorCode, - reason: EthersTxReplacedReason.cancelled, - receipt: {} as TransactionReceipt, - } + it('returns ERROR if the tx was cancelled', () => { + const mockEthersError = { + ...new Error(), + code: 'TRANSACTION_REPLACED' as ErrorCode, + reason: EthersTxReplacedReason.cancelled, + receipt: {} as TransactionReceipt, + } - const result = handleSafeCreationError(mockEthersError) + const result = handleSafeCreationError(mockEthersError) - expect(result).toEqual(SafeCreationStatus.ERROR) - }) + expect(result).toEqual(SafeCreationStatus.ERROR) + }) - it('returns SUCCESS if the tx was replaced', () => { - const mockEthersError = { - ...new Error(), - code: 'TRANSACTION_REPLACED' as ErrorCode, - reason: EthersTxReplacedReason.replaced, - receipt: {} as TransactionReceipt, - } + it('returns SUCCESS if the tx was replaced', () => { + const mockEthersError = { + ...new Error(), + code: 'TRANSACTION_REPLACED' as ErrorCode, + reason: EthersTxReplacedReason.replaced, + receipt: {} as TransactionReceipt, + } - const result = handleSafeCreationError(mockEthersError) + const result = handleSafeCreationError(mockEthersError) - expect(result).toEqual(SafeCreationStatus.SUCCESS) - }) + expect(result).toEqual(SafeCreationStatus.SUCCESS) + }) - it('returns SUCCESS if the tx was repriced', () => { - const mockEthersError = { - ...new Error(), - code: 'TRANSACTION_REPLACED' as ErrorCode, - reason: EthersTxReplacedReason.repriced, - receipt: {} as TransactionReceipt, - } + it('returns SUCCESS if the tx was repriced', () => { + const mockEthersError = { + ...new Error(), + code: 'TRANSACTION_REPLACED' as ErrorCode, + reason: EthersTxReplacedReason.repriced, + receipt: {} as TransactionReceipt, + } - const result = handleSafeCreationError(mockEthersError) + const result = handleSafeCreationError(mockEthersError) - expect(result).toEqual(SafeCreationStatus.SUCCESS) - }) + expect(result).toEqual(SafeCreationStatus.SUCCESS) + }) - it('returns ERROR if the tx was not rejected, cancelled or replaced', () => { - const mockEthersError = { - ...new Error(), - code: 'UNKNOWN_ERROR' as ErrorCode, - reason: '' as EthersTxReplacedReason, - receipt: {} as TransactionReceipt, - } + it('returns ERROR if the tx was not rejected, cancelled or replaced', () => { + const mockEthersError = { + ...new Error(), + code: 'UNKNOWN_ERROR' as ErrorCode, + reason: '' as EthersTxReplacedReason, + receipt: {} as TransactionReceipt, + } - const result = handleSafeCreationError(mockEthersError) + const result = handleSafeCreationError(mockEthersError) - expect(result).toEqual(SafeCreationStatus.ERROR) - }) + expect(result).toEqual(SafeCreationStatus.ERROR) + }) - it('returns REVERTED if the tx failed', () => { - const mockEthersError = { - ...new Error(), - code: 'UNKNOWN_ERROR' as ErrorCode, - reason: '' as EthersTxReplacedReason, - receipt: { - status: 0, - } as TransactionReceipt, - } + it('returns REVERTED if the tx failed', () => { + const mockEthersError = { + ...new Error(), + code: 'UNKNOWN_ERROR' as ErrorCode, + reason: '' as EthersTxReplacedReason, + receipt: { + status: 0, + } as TransactionReceipt, + } - const result = handleSafeCreationError(mockEthersError) + const result = handleSafeCreationError(mockEthersError) - expect(result).toEqual(SafeCreationStatus.REVERTED) + expect(result).toEqual(SafeCreationStatus.REVERTED) + }) }) -}) -describe('createNewSafeViaRelayer', () => { - const owner1 = toBeHex('0x1', 20) - const owner2 = toBeHex('0x2', 20) + describe('createNewSafeViaRelayer', () => { + const owner1 = toBeHex('0x1', 20) + const owner2 = toBeHex('0x2', 20) - const mockChainInfo = { - chainId: '5', - l2: false, - } as ChainInfo + const mockChainInfo = { + chainId: '5', + l2: false, + } as ChainInfo - beforeAll(() => { - jest.resetAllMocks() - jest.spyOn(web3, 'getWeb3ReadOnly').mockImplementation(() => provider) - }) + beforeAll(() => { + jest.resetAllMocks() + jest.spyOn(web3, 'getWeb3ReadOnly').mockImplementation(() => provider) + }) - it('returns taskId if create Safe successfully relayed', async () => { - const sponsoredCallSpy = jest.spyOn(relaying, 'sponsoredCall').mockResolvedValue({ taskId: '0x123' }) - - const expectedSaltNonce = 69 - const expectedThreshold = 1 - const proxyFactoryAddress = await (await getReadOnlyProxyFactoryContract('5', LATEST_SAFE_VERSION)).getAddress() - const readOnlyFallbackHandlerContract = await getReadOnlyFallbackHandlerContract('5', LATEST_SAFE_VERSION) - const safeContractAddress = await (await getReadOnlyGnosisSafeContract(mockChainInfo)).getAddress() - - const expectedInitializer = Gnosis_safe__factory.createInterface().encodeFunctionData('setup', [ - [owner1, owner2], - expectedThreshold, - ZERO_ADDRESS, - EMPTY_DATA, - await readOnlyFallbackHandlerContract.getAddress(), - ZERO_ADDRESS, - 0, - ZERO_ADDRESS, - ]) - - const expectedCallData = Proxy_factory__factory.createInterface().encodeFunctionData('createProxyWithNonce', [ - safeContractAddress, - expectedInitializer, - expectedSaltNonce, - ]) - - const taskId = await relaySafeCreation(mockChainInfo, [owner1, owner2], expectedThreshold, expectedSaltNonce) - - expect(taskId).toEqual('0x123') - expect(sponsoredCallSpy).toHaveBeenCalledTimes(1) - expect(sponsoredCallSpy).toHaveBeenCalledWith({ - chainId: '5', - to: proxyFactoryAddress, - data: expectedCallData, + it('returns taskId if create Safe successfully relayed', async () => { + const sponsoredCallSpy = jest.spyOn(relaying, 'sponsoredCall').mockResolvedValue({ taskId: '0x123' }) + + const expectedSaltNonce = 69 + const expectedThreshold = 1 + const proxyFactoryAddress = await (await getReadOnlyProxyFactoryContract('5', LATEST_SAFE_VERSION)).getAddress() + const readOnlyFallbackHandlerContract = await getReadOnlyFallbackHandlerContract('5', LATEST_SAFE_VERSION) + const safeContractAddress = await (await getReadOnlyGnosisSafeContract(mockChainInfo)).getAddress() + + const expectedInitializer = Gnosis_safe__factory.createInterface().encodeFunctionData('setup', [ + [owner1, owner2], + expectedThreshold, + ZERO_ADDRESS, + EMPTY_DATA, + await readOnlyFallbackHandlerContract.getAddress(), + ZERO_ADDRESS, + 0, + ZERO_ADDRESS, + ]) + + const expectedCallData = Proxy_factory__factory.createInterface().encodeFunctionData('createProxyWithNonce', [ + safeContractAddress, + expectedInitializer, + expectedSaltNonce, + ]) + + const taskId = await relaySafeCreation(mockChainInfo, [owner1, owner2], expectedThreshold, expectedSaltNonce) + + expect(taskId).toEqual('0x123') + expect(sponsoredCallSpy).toHaveBeenCalledTimes(1) + expect(sponsoredCallSpy).toHaveBeenCalledWith({ + chainId: '5', + to: proxyFactoryAddress, + data: expectedCallData, + }) }) - }) - it('should throw an error if relaying fails', () => { - const relayFailedError = new Error('Relay failed') + it('should throw an error if relaying fails', () => { + const relayFailedError = new Error('Relay failed') - jest.spyOn(relaying, 'sponsoredCall').mockRejectedValue(relayFailedError) + jest.spyOn(relaying, 'sponsoredCall').mockRejectedValue(relayFailedError) - expect(relaySafeCreation(mockChainInfo, [owner1, owner2], 1, 69)).rejects.toEqual(relayFailedError) + expect(relaySafeCreation(mockChainInfo, [owner1, owner2], 1, 69)).rejects.toEqual(relayFailedError) + }) }) }) diff --git a/src/components/new-safe/create/logic/utils.test.ts b/src/components/new-safe/create/logic/utils.test.ts new file mode 100644 index 0000000000..f7076c06a8 --- /dev/null +++ b/src/components/new-safe/create/logic/utils.test.ts @@ -0,0 +1,44 @@ +import * as creationUtils from '@/components/new-safe/create/logic/index' +import { getAvailableSaltNonce } from '@/components/new-safe/create/logic/utils' +import * as web3Utils from '@/hooks/wallets/web3' +import { faker } from '@faker-js/faker' +import { BrowserProvider, type Eip1193Provider } from 'ethers' + +describe('getAvailableSaltNonce', () => { + jest.spyOn(creationUtils, 'computeNewSafeAddress').mockReturnValue(Promise.resolve(faker.finance.ethereumAddress())) + + const mockProvider = new BrowserProvider(jest.fn() as unknown as Eip1193Provider) + const mockDeployProps = { + safeAccountConfig: { + threshold: 1, + owners: [faker.finance.ethereumAddress()], + fallbackHandler: faker.finance.ethereumAddress(), + }, + } + + beforeEach(() => { + jest.clearAllMocks() + }) + + it('should return initial nonce if no contract is deployed to the computed address', async () => { + jest.spyOn(web3Utils, 'isSmartContract').mockReturnValue(Promise.resolve(false)) + const initialNonce = faker.string.numeric() + + const result = await getAvailableSaltNonce(mockProvider, { ...mockDeployProps, saltNonce: initialNonce }) + + expect(result).toEqual(initialNonce) + }) + + it('should return an increased nonce if a contract is deployed to the computed address', async () => { + jest.spyOn(web3Utils, 'isSmartContract').mockReturnValueOnce(Promise.resolve(true)) + const initialNonce = faker.string.numeric() + + const result = await getAvailableSaltNonce(mockProvider, { ...mockDeployProps, saltNonce: initialNonce }) + + jest.spyOn(web3Utils, 'isSmartContract').mockReturnValueOnce(Promise.resolve(false)) + + const increasedNonce = (Number(initialNonce) + 1).toString() + + expect(result).toEqual(increasedNonce) + }) +}) diff --git a/src/components/new-safe/create/logic/utils.ts b/src/components/new-safe/create/logic/utils.ts new file mode 100644 index 0000000000..e31e01bfd1 --- /dev/null +++ b/src/components/new-safe/create/logic/utils.ts @@ -0,0 +1,17 @@ +import { computeNewSafeAddress } from '@/components/new-safe/create/logic/index' +import { isSmartContract } from '@/hooks/wallets/web3' +import type { DeploySafeProps } from '@safe-global/protocol-kit' +import type { BrowserProvider } from 'ethers' + +export const getAvailableSaltNonce = async (provider: BrowserProvider, props: DeploySafeProps): Promise => { + const safeAddress = await computeNewSafeAddress(provider, props) + const isContractDeployed = await isSmartContract(provider, safeAddress) + + // Safe is already deployed so we try the next saltNonce + if (isContractDeployed) { + return getAvailableSaltNonce(provider, { ...props, saltNonce: (Number(props.saltNonce) + 1).toString() }) + } + + // We know that there will be a saltNonce but the type has it as optional + return props.saltNonce! +} diff --git a/src/components/new-safe/create/steps/ReviewStep/index.tsx b/src/components/new-safe/create/steps/ReviewStep/index.tsx index 615a4bba85..b29705e583 100644 --- a/src/components/new-safe/create/steps/ReviewStep/index.tsx +++ b/src/components/new-safe/create/steps/ReviewStep/index.tsx @@ -1,13 +1,14 @@ +import { getAvailableSaltNonce } from '@/components/new-safe/create/logic/utils' import ErrorMessage from '@/components/tx/ErrorMessage' import { AppRoutes } from '@/config/routes' import { addUndeployedSafe } from '@/features/counterfactual/store/undeployedSafeSlice' -import { getCounterfactualNonce } from '@/features/counterfactual/utils' import useWalletCanPay from '@/hooks/useWalletCanPay' import { useAppDispatch } from '@/store' import { addOrUpdateSafe } from '@/store/addedSafesSlice' import { upsertAddressBookEntry } from '@/store/addressBookSlice' import { defaultSafeInfo } from '@/store/safeInfoSlice' import { FEATURES } from '@/utils/chains' +import type { SafeVersion } from '@safe-global/safe-core-sdk-types' import { useRouter } from 'next/router' import { useMemo, useState } from 'react' import { Button, Grid, Typography, Divider, Box, Alert } from '@mui/material' @@ -113,7 +114,6 @@ const ReviewStep = ({ data, onSubmit, onBack, setStep }: StepRenderProps Date.now(), []) const [_, setPendingSafe] = usePendingSafe() const [executionMethod, setExecutionMethod] = useState(ExecutionMethod.RELAY) const isCounterfactualEnabled = useHasFeature(FEATURES.COUNTERFACTUAL) @@ -129,9 +129,9 @@ const ReviewStep = ({ data, onSubmit, onBack, setStep }: StepRenderProps owner.address), threshold: data.threshold, - saltNonce, + saltNonce: Date.now(), // Doesn't matter for the gas estimation } - }, [data.owners, data.threshold, saltNonce]) + }, [data.owners, data.threshold]) const { gasLimit } = useEstimateSafeCreationGas(safeParams) @@ -163,20 +163,20 @@ const ReviewStep = ({ data, onSubmit, onBack, setStep }: StepRenderProps owner.address), fallbackHandler: await readOnlyFallbackHandlerContract.getAddress(), }, - saltNonce: saltNonce.toString(), } - if (isCounterfactual) { - const counterfactualNonce = await getCounterfactualNonce(provider, { ...props, saltNonce: '0' }) - const safeAddress = await computeNewSafeAddress(provider, { ...props, saltNonce: counterfactualNonce }) + const saltNonce = await getAvailableSaltNonce(provider, { ...props, saltNonce: '0' }) + const safeAddress = await computeNewSafeAddress(provider, { ...props, saltNonce }) + if (isCounterfactual) { const undeployedSafe = { chainId: chain.chainId, address: safeAddress, safeProps: { safeAccountConfig: props.safeAccountConfig, safeDeploymentConfig: { - saltNonce: counterfactualNonce, + saltNonce, + safeVersion: LATEST_SAFE_VERSION as SafeVersion, }, }, } @@ -201,11 +201,9 @@ const ReviewStep = ({ data, onSubmit, onBack, setStep }: StepRenderProps { - describe('getCounterfactualNonce', () => { - jest.spyOn(creationUtils, 'computeNewSafeAddress').mockReturnValue(Promise.resolve(faker.finance.ethereumAddress())) - - const mockProvider = new BrowserProvider(jest.fn() as unknown as Eip1193Provider) - const mockDeployProps = { - safeAccountConfig: { - threshold: 1, - owners: [faker.finance.ethereumAddress()], - fallbackHandler: faker.finance.ethereumAddress(), - }, - } - - beforeEach(() => { - jest.clearAllMocks() - }) - - it('should return initial nonce if no contract is deployed to the computed address', async () => { - jest.spyOn(web3Utils, 'isSmartContract').mockReturnValue(Promise.resolve(false)) - const initialNonce = faker.string.numeric() - - const result = await getCounterfactualNonce(mockProvider, { ...mockDeployProps, saltNonce: initialNonce }) - - expect(result).toEqual(initialNonce) - }) - - it('should return an increased nonce if a contract is deployed to the computed address', async () => { - jest.spyOn(web3Utils, 'isSmartContract').mockReturnValueOnce(Promise.resolve(true)) - const initialNonce = faker.string.numeric() - - const result = await getCounterfactualNonce(mockProvider, { ...mockDeployProps, saltNonce: initialNonce }) - - jest.spyOn(web3Utils, 'isSmartContract').mockReturnValueOnce(Promise.resolve(false)) - - const increasedNonce = (Number(initialNonce) + 1).toString() - - expect(result).toEqual(increasedNonce) - }) - }) - describe('getUndeployedSafeInfo', () => { it('should return undeployed safe info', async () => { const undeployedSafe: PredictedSafeProps = { diff --git a/src/features/counterfactual/utils.ts b/src/features/counterfactual/utils.ts index 8459864f81..bec1b65a44 100644 --- a/src/features/counterfactual/utils.ts +++ b/src/features/counterfactual/utils.ts @@ -1,25 +1,7 @@ -import { computeNewSafeAddress } from '@/components/new-safe/create/logic' import { LATEST_SAFE_VERSION } from '@/config/constants' -import { isSmartContract } from '@/hooks/wallets/web3' import { defaultSafeInfo } from '@/store/safeInfoSlice' -import type { DeploySafeProps, PredictedSafeProps } from '@safe-global/protocol-kit' +import type { PredictedSafeProps } from '@safe-global/protocol-kit' import { ImplementationVersionState } from '@safe-global/safe-gateway-typescript-sdk' -import type { BrowserProvider } from 'ethers' - -export const getCounterfactualNonce = async ( - provider: BrowserProvider, - props: DeploySafeProps, -): Promise => { - const safeAddress = await computeNewSafeAddress(provider, props) - const isContractDeployed = await isSmartContract(provider, safeAddress) - - // Safe is already deployed so we try the next saltNonce - if (isContractDeployed) { - return getCounterfactualNonce(provider, { ...props, saltNonce: (Number(props.saltNonce) + 1).toString() }) - } - - return props.saltNonce -} export const getUndeployedSafeInfo = (undeployedSafe: PredictedSafeProps, address: string, chainId: string) => { return Promise.resolve({