From 9aa0bb7b2529a87f7a58ee92fda901165853d401 Mon Sep 17 00:00:00 2001 From: Aaron Cook Date: Mon, 10 Jul 2023 18:25:44 +0200 Subject: [PATCH 1/3] fix: Safe creation/batch execution gas estimation (#2232) * fix: underpriced transactions on Ledger devices * refactor: use loading flag from `useGasPrice` * fix: mock --- .../create/steps/ReviewStep/index.tsx | 18 ++++- .../__tests__/useSafeCreation.test.ts | 77 ++++++++++++++++++- .../steps/StatusStep/useSafeCreation.ts | 25 +++++- .../tx/AdvancedParams/useAdvancedParams.ts | 8 +- .../BatchExecuteModal/ReviewBatchExecute.tsx | 19 ++++- src/hooks/__tests__/useGasPrice.test.ts | 50 +++++++++--- src/hooks/useGasPrice.ts | 51 ++++++------ src/services/tx/tx-sender/dispatch.ts | 6 +- 8 files changed, 204 insertions(+), 50 deletions(-) diff --git a/src/components/new-safe/create/steps/ReviewStep/index.tsx b/src/components/new-safe/create/steps/ReviewStep/index.tsx index 54a2bfc71e..c5ebf035f4 100644 --- a/src/components/new-safe/create/steps/ReviewStep/index.tsx +++ b/src/components/new-safe/create/steps/ReviewStep/index.tsx @@ -26,6 +26,7 @@ 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) => { const isWrongChain = useIsWrongChain() @@ -33,7 +34,7 @@ const ReviewStep = ({ data, onSubmit, onBack, setStep }: StepRenderProps Date.now(), []) const [_, setPendingSafe] = useLocalStorage(SAFE_PENDING_CREATION_STORAGE_KEY) const [executionMethod, setExecutionMethod] = useState(ExecutionMethod.RELAY) @@ -55,9 +56,20 @@ const ReviewStep = ({ data, onSubmit, onBack, setStep }: StepRenderProps 0.001' const handleBack = () => { diff --git a/src/components/new-safe/create/steps/StatusStep/__tests__/useSafeCreation.test.ts b/src/components/new-safe/create/steps/StatusStep/__tests__/useSafeCreation.test.ts index a6a342b433..4c3b3e4c58 100644 --- a/src/components/new-safe/create/steps/StatusStep/__tests__/useSafeCreation.test.ts +++ b/src/components/new-safe/create/steps/StatusStep/__tests__/useSafeCreation.test.ts @@ -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', @@ -45,6 +47,7 @@ describe('useSafeCreation', () => { const mockChain = { chainId: '4', + features: [], } as unknown as ChainInfo jest.spyOn(web3, 'useWeb3').mockImplementation(() => mockProvider) @@ -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() }) }) diff --git a/src/components/new-safe/create/steps/StatusStep/useSafeCreation.ts b/src/components/new-safe/create/steps/StatusStep/useSafeCreation.ts index 35a73878a7..ec4823a5fa 100644 --- a/src/components/new-safe/create/steps/StatusStep/useSafeCreation.ts +++ b/src/components/new-safe/create/steps/StatusStep/useSafeCreation.ts @@ -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, @@ -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) => { @@ -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 })) @@ -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) { @@ -106,7 +123,11 @@ export const useSafeCreation = ( chain, createSafeCallback, dispatch, + gasPriceLoading, isCreating, + isEIP1559, + maxFeePerGas, + maxPriorityFeePerGas, pendingSafe, provider, setPendingSafe, diff --git a/src/components/tx/AdvancedParams/useAdvancedParams.ts b/src/components/tx/AdvancedParams/useAdvancedParams.ts index a03773b7b5..1d876384b4 100644 --- a/src/components/tx/AdvancedParams/useAdvancedParams.ts +++ b/src/components/tx/AdvancedParams/useAdvancedParams.ts @@ -9,7 +9,7 @@ export const useAdvancedParams = ({ safeTxGas, }: AdvancedParameters): [AdvancedParameters, (params: AdvancedParameters) => void] => { const [manualParams, setManualParams] = useState() - const { maxFeePerGas, maxPriorityFeePerGas } = useGasPrice() + const [gasPrice] = useGasPrice() const userNonce = useUserNonce() const advancedParams: AdvancedParameters = useMemo( @@ -17,11 +17,11 @@ export const useAdvancedParams = ({ nonce: manualParams?.nonce ?? nonce, 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, safeTxGas: manualParams?.safeTxGas ?? safeTxGas, }), - [manualParams, nonce, userNonce, gasLimit, maxFeePerGas, maxPriorityFeePerGas, safeTxGas], + [manualParams, nonce, userNonce, gasLimit, gasPrice?.maxFeePerGas, gasPrice?.maxPriorityFeePerGas, safeTxGas], ) return [advancedParams, setManualParams] diff --git a/src/components/tx/modals/BatchExecuteModal/ReviewBatchExecute.tsx b/src/components/tx/modals/BatchExecuteModal/ReviewBatchExecute.tsx index b38e0603d9..8c505df00c 100644 --- a/src/components/tx/modals/BatchExecuteModal/ReviewBatchExecute.tsx +++ b/src/components/tx/modals/BatchExecuteModal/ReviewBatchExecute.tsx @@ -1,4 +1,5 @@ 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' @@ -21,6 +22,9 @@ import useOnboard from '@/hooks/wallets/useOnboard' import { WrongChainWarning } from '@/components/tx/WrongChainWarning' import { useWeb3 } from '@/hooks/wallets/web3' import { hasRemainingRelays } from '@/utils/relaying' +import useGasPrice from '@/hooks/useGasPrice' +import { hasFeature } from '@/utils/chains' +import type { PayableOverrides } from 'ethers' const ReviewBatchExecute = ({ data, onSubmit }: { data: BatchExecuteData; onSubmit: (data: null) => void }) => { const [isSubmittable, setIsSubmittable] = useState(true) @@ -29,6 +33,12 @@ const ReviewBatchExecute = ({ data, onSubmit }: { data: BatchExecuteData; onSubm const chain = useCurrentChain() const { safe } = useSafeInfo() const [relays] = useRelaysBySafe() + 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) @@ -58,7 +68,11 @@ const ReviewBatchExecute = ({ data, onSubmit }: { data: BatchExecuteData; onSubm }, [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, @@ -67,6 +81,7 @@ const ReviewBatchExecute = ({ data, onSubmit }: { data: BatchExecuteData; onSubm onboard, safe.chainId, safe.address.value, + overrides, ) onSubmit(null) } @@ -100,7 +115,7 @@ const ReviewBatchExecute = ({ data, onSubmit }: { data: BatchExecuteData; onSubm } } - const submitDisabled = loading || !isSubmittable + const submitDisabled = loading || !isSubmittable || gasPriceLoading return (
diff --git a/src/hooks/__tests__/useGasPrice.test.ts b/src/hooks/__tests__/useGasPrice.test.ts index 987413397c..d3a3d5dd24 100644 --- a/src/hooks/__tests__/useGasPrice.test.ts +++ b/src/hooks/__tests__/useGasPrice.test.ts @@ -74,6 +74,9 @@ describe('useGasPrice', () => { // render the hook const { result } = renderHook(() => useGasPrice()) + // assert the hook is loading + expect(result.current[2]).toBe(true) + // wait for the hook to fetch the gas price await act(async () => { await Promise.resolve() @@ -81,11 +84,14 @@ describe('useGasPrice', () => { expect(fetch).toHaveBeenCalledWith('https://api.etherscan.io/api?module=gastracker&action=gasoracle') + // assert the hook is not loading + expect(result.current[2]).toBe(false) + // assert the gas price is correct - expect(result.current.maxFeePerGas?.toString()).toBe('47000000000') + expect(result.current[0]?.maxFeePerGas?.toString()).toBe('47000000000') // assert the priority fee is correct - expect(result.current.maxPriorityFeePerGas?.toString()).toEqual('4975') + expect(result.current[0]?.maxPriorityFeePerGas?.toString()).toEqual('4975') }) it('should return the fetched gas price from the second oracle if the first one fails', async () => { @@ -110,6 +116,9 @@ describe('useGasPrice', () => { // render the hook const { result } = renderHook(() => useGasPrice()) + // assert the hook is loading + expect(result.current[2]).toBe(true) + // wait for the hook to fetch the gas price await act(async () => { await Promise.resolve() @@ -118,11 +127,14 @@ describe('useGasPrice', () => { expect(fetch).toHaveBeenCalledWith('https://api.etherscan.io/api?module=gastracker&action=gasoracle') expect(fetch).toHaveBeenCalledWith('https://ethgasstation.info/json/ethgasAPI.json') + // assert the hook is not loading + expect(result.current[2]).toBe(false) + // assert the gas price is correct - expect(result.current.maxFeePerGas?.toString()).toBe('60000000000') + expect(result.current[0]?.maxFeePerGas?.toString()).toBe('60000000000') // assert the priority fee is correct - expect(result.current.maxPriorityFeePerGas?.toString()).toEqual('4975') + expect(result.current[0]?.maxPriorityFeePerGas?.toString()).toEqual('4975') }) it('should fallback to a fixed gas price if the oracles fail', async () => { @@ -137,6 +149,9 @@ describe('useGasPrice', () => { // render the hook const { result } = renderHook(() => useGasPrice()) + // assert the hook is loading + expect(result.current[2]).toBe(true) + // wait for the hook to fetch the gas price await act(async () => { await Promise.resolve() @@ -145,11 +160,14 @@ describe('useGasPrice', () => { expect(fetch).toHaveBeenCalledWith('https://api.etherscan.io/api?module=gastracker&action=gasoracle') expect(fetch).toHaveBeenCalledWith('https://ethgasstation.info/json/ethgasAPI.json') + // assert the hook is not loading + expect(result.current[2]).toBe(false) + // assert the gas price is correct - expect(result.current.maxFeePerGas?.toString()).toBe('24000000000') + expect(result.current[0]?.maxFeePerGas?.toString()).toBe('24000000000') // assert the priority fee is correct - expect(result.current.maxPriorityFeePerGas?.toString()).toEqual('4975') + expect(result.current[0]?.maxPriorityFeePerGas?.toString()).toEqual('4975') }) it('should keep the previous gas price if the hook re-renders', async () => { @@ -185,25 +203,37 @@ describe('useGasPrice', () => { // render the hook const { result } = renderHook(() => useGasPrice()) - expect(result.current.maxFeePerGas).toBe(undefined) + // assert the hook is loading + expect(result.current[2]).toBe(true) + + expect(result.current[0]?.maxFeePerGas).toBe(undefined) // wait for the hook to fetch the gas price await act(async () => { await Promise.resolve() }) - expect(result.current.maxFeePerGas?.toString()).toBe('21000000000') + // assert the hook is not loading + expect(result.current[2]).toBe(false) + + expect(result.current[0]?.maxFeePerGas?.toString()).toBe('21000000000') // render the hook again const { result: result2 } = renderHook(() => useGasPrice()) - expect(result.current.maxFeePerGas?.toString()).toBe('21000000000') + // assert the hook is not loading (as a value exists) + expect(result.current[2]).toBe(false) + + expect(result.current[0]?.maxFeePerGas?.toString()).toBe('21000000000') // wait for the hook to fetch the gas price await act(async () => { await Promise.resolve() }) - expect(result2.current.maxFeePerGas?.toString()).toBe('22000000000') + // assert the hook is not loading + expect(result.current[2]).toBe(false) + + expect(result2.current[0]?.maxFeePerGas?.toString()).toBe('22000000000') }) }) diff --git a/src/hooks/useGasPrice.ts b/src/hooks/useGasPrice.ts index 056f78bc3f..2f7127e974 100644 --- a/src/hooks/useGasPrice.ts +++ b/src/hooks/useGasPrice.ts @@ -1,9 +1,7 @@ -import { useMemo } from 'react' import { BigNumber } from 'ethers' -import type { FeeData } from '@ethersproject/providers' import type { GasPrice, GasPriceOracle } from '@safe-global/safe-gateway-typescript-sdk' import { GAS_PRICE_TYPE } from '@safe-global/safe-gateway-typescript-sdk' -import useAsync from '@/hooks/useAsync' +import useAsync, { type AsyncResult } from '@/hooks/useAsync' import { useCurrentChain } from './useChains' import useIntervalCounter from './useIntervalCounter' import { useWeb3ReadOnly } from '../hooks/wallets/web3' @@ -54,41 +52,42 @@ const getGasPrice = async (gasPriceConfigs: GasPrice): Promise { +const useGasPrice = (): AsyncResult<{ + maxFeePerGas: BigNumber | undefined + maxPriorityFeePerGas: BigNumber | undefined +}> => { const chain = useCurrentChain() const gasPriceConfigs = chain?.gasPrice const [counter] = useIntervalCounter(REFRESH_DELAY) const provider = useWeb3ReadOnly() const isEIP1559 = !!chain && hasFeature(chain, FEATURES.EIP1559) - // Fetch gas price from oracles or get a fixed value - const [gasPrice] = useAsync( - () => { - if (gasPriceConfigs) { - return getGasPrice(gasPriceConfigs) + const [gasPrice, gasPriceError, gasPriceLoading] = useAsync( + async () => { + const [gasPrice, feeData] = await Promise.all([ + // Fetch gas price from oracles or get a fixed value + gasPriceConfigs ? getGasPrice(gasPriceConfigs) : undefined, + + // Fetch the gas fees from the blockchain itself + provider?.getFeeData(), + ]) + + // Prepare the return values + const maxFee = gasPrice || (isEIP1559 ? feeData?.maxFeePerGas : feeData?.gasPrice) || undefined + const maxPrioFee = (isEIP1559 && feeData?.maxPriorityFeePerGas) || undefined + + return { + maxFeePerGas: maxFee, + maxPriorityFeePerGas: maxPrioFee, } }, - [gasPriceConfigs, counter], + [gasPriceConfigs, provider, counter], false, ) - // Fetch the gas fees from the blockchain itself - const [feeData] = useAsync(() => provider?.getFeeData(), [provider, counter], false) + const isLoading = gasPriceLoading || (!gasPrice && !gasPriceError) - // Prepare the return values - const maxFee = gasPrice || (isEIP1559 ? feeData?.maxFeePerGas : feeData?.gasPrice) || undefined - const maxPrioFee = (isEIP1559 && feeData?.maxPriorityFeePerGas) || undefined - - return useMemo( - () => ({ - maxFeePerGas: maxFee, - maxPriorityFeePerGas: maxPrioFee, - }), - [maxFee, maxPrioFee], - ) + return [gasPrice, gasPriceError, isLoading] } export default useGasPrice diff --git a/src/services/tx/tx-sender/dispatch.ts b/src/services/tx/tx-sender/dispatch.ts index ae390acf7c..0a05153bda 100644 --- a/src/services/tx/tx-sender/dispatch.ts +++ b/src/services/tx/tx-sender/dispatch.ts @@ -5,7 +5,7 @@ import { didReprice, didRevert } from '@/utils/ethers-utils' import type MultiSendCallOnlyEthersContract from '@safe-global/safe-ethers-lib/dist/src/contracts/MultiSendCallOnly/MultiSendCallOnlyEthersContract' import type { SpendingLimitTxParams } from '@/components/tx/modals/TokenTransferModal/ReviewSpendingLimitTx' import { getSpendingLimitContract } from '@/services/contracts/spendingLimitContracts' -import type { ContractTransaction } from 'ethers' +import type { ContractTransaction, PayableOverrides } from 'ethers' import type { RequestId } from '@safe-global/safe-apps-sdk' import proposeTx from '../proposeTransaction' import { txDispatch, TxEvent } from '../txEvents' @@ -174,6 +174,7 @@ export const dispatchBatchExecution = async ( onboard: OnboardAPI, chainId: SafeInfo['chainId'], safeAddress: string, + overrides?: PayableOverrides, ) => { const groupKey = multiSendTxData @@ -183,7 +184,8 @@ export const dispatchBatchExecution = async ( const wallet = await assertWalletChain(onboard, chainId) const provider = createWeb3(wallet.provider) - result = await multiSendContract.contract.connect(provider.getSigner()).multiSend(multiSendTxData) + result = await multiSendContract.contract.connect(provider.getSigner()).multiSend(multiSendTxData, overrides) + txs.forEach(({ txId }) => { txDispatch(TxEvent.EXECUTING, { txId, groupKey }) }) From ad2e5744db632a88a0163e699c1ba53d75f2f5f4 Mon Sep 17 00:00:00 2001 From: Manuel Gellfart Date: Tue, 11 Jul 2023 10:43:29 +0200 Subject: [PATCH 2/3] fix: resolve third party dependency (#2255) --- package.json | 3 +++ yarn.lock | 21 ++++++++++----------- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/package.json b/package.json index 58bc75420e..94b7f4cc41 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/yarn.lock b/yarn.lock index 6b25b8542b..7c8ab02687 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4093,11 +4093,6 @@ resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.191.tgz#09511e7f7cba275acd8b419ddac8da9a6a79e2fa" integrity sha512-BdZ5BCCvho3EIXw6wUCXHe7rS53AIDPLE+JzwgT+OsJk53oBfbSmZZ7CX4VaRoN78N+TJpFi9QPlfIVNmJYWxQ== -"@types/long@^4.0.1": - version "4.0.2" - resolved "https://registry.yarnpkg.com/@types/long/-/long-4.0.2.tgz#b74129719fc8d11c01868010082d483b7545591a" - integrity sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA== - "@types/minimatch@*": version "5.1.2" resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-5.1.2.tgz#07508b45797cb81ec3f273011b054cd0755eddca" @@ -10470,6 +10465,11 @@ long@^4.0.0: resolved "https://registry.yarnpkg.com/long/-/long-4.0.0.tgz#9a7b71cfb7d361a194ea555241c92f7468d5bf28" integrity sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA== +long@^5.0.0: + version "5.2.3" + resolved "https://registry.yarnpkg.com/long/-/long-5.2.3.tgz#a3ba97f3877cf1d778eccbcb048525ebb77499e1" + integrity sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q== + long@~3: version "3.2.0" resolved "https://registry.yarnpkg.com/long/-/long-3.2.0.tgz#d821b7138ca1cb581c172990ef14db200b5c474b" @@ -11639,10 +11639,10 @@ prop-types@^15.6.0, prop-types@^15.6.2, prop-types@^15.7.2, prop-types@^15.8.1: object-assign "^4.1.1" react-is "^16.13.1" -protobufjs@6.11.3: - version "6.11.3" - resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-6.11.3.tgz#637a527205a35caa4f3e2a9a4a13ddffe0e7af74" - integrity sha512-xL96WDdCZYdU7Slin569tFX712BxsxslWwAfAhCYjQKGTq7dAU91Lomy6nLLhh/dyGhk/YH4TwTSRxTzhuHyZg== +protobufjs@6.11.3, protobufjs@^7.2.4: + version "7.2.4" + resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-7.2.4.tgz#3fc1ec0cdc89dd91aef9ba6037ba07408485c3ae" + integrity sha512-AT+RJgD2sH8phPmCf7OUZR8xGdcJRga4+1cOaXJ64hvcSkVhNcRHOwIxUatPH15+nj59WAGTDv3LSGZPEQbJaQ== dependencies: "@protobufjs/aspromise" "^1.1.2" "@protobufjs/base64" "^1.1.2" @@ -11654,9 +11654,8 @@ protobufjs@6.11.3: "@protobufjs/path" "^1.1.2" "@protobufjs/pool" "^1.1.0" "@protobufjs/utf8" "^1.1.0" - "@types/long" "^4.0.1" "@types/node" ">=13.7.0" - long "^4.0.0" + long "^5.0.0" proxy-addr@~2.0.7: version "2.0.7" From f5e7f5db8c86b863659d7443211664472b3e446d Mon Sep 17 00:00:00 2001 From: Aaron Cook Date: Tue, 11 Jul 2023 14:38:40 +0200 Subject: [PATCH 3/3] chore: fix network name selection in E2E (#2267) * chore: fix network name selection in E2E * fix: rename token id --- cypress/e2e/smoke/create_safe_simple.cy.js | 6 ++++-- cypress/e2e/smoke/nfts.cy.js | 10 ++++++---- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/cypress/e2e/smoke/create_safe_simple.cy.js b/cypress/e2e/smoke/create_safe_simple.cy.js index 36f349792a..30b2540be7 100644 --- a/cypress/e2e/smoke/create_safe_simple.cy.js +++ b/cypress/e2e/smoke/create_safe_simple.cy.js @@ -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 @ Görli') + cy.contains(/E2E Wallet @ G(ö|oe)rli/) cy.contains('Create new Account').click() }) @@ -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', 'Görli').click() + + // Prevent Base Mainnet Goerli from being selected + cy.contains('li span', /^G(ö|oe)rli$/).click() cy.contains('button', 'Next').click() }) diff --git a/cypress/e2e/smoke/nfts.cy.js b/cypress/e2e/smoke/nfts.cy.js index deea89191b..76b6d7fce9 100644 --- a/cypress/e2e/smoke/nfts.cy.js +++ b/cypress/e2e/smoke/nfts.cy.js @@ -6,7 +6,7 @@ describe('Assets > NFTs', () => { cy.visit(`/balances/nfts?safe=${TEST_SAFE}`) cy.contains('button', 'Accept selection').click() - cy.contains('E2E Wallet @ Görli') + cy.contains(/E2E Wallet @ G(ö|oe)rli/) }) describe('should have NFTs', () => { @@ -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"]', @@ -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('Görli') + 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',