From b0b95208d9a5921eff1210227b4cb915f5148163 Mon Sep 17 00:00:00 2001 From: iamacook Date: Fri, 9 Dec 2022 12:30:19 +0100 Subject: [PATCH 1/5] fix: handle nullable `SafeInfo['version'] --- package.json | 2 +- src/components/settings/ContractVersion/index.tsx | 2 +- src/components/settings/TransactionGuards/index.tsx | 2 +- src/components/tx/TxSimulation/utils.ts | 2 +- .../tx/modals/BatchExecuteModal/ReviewBatchExecute.tsx | 8 ++++---- src/hooks/coreSDK/safeCoreSDK.ts | 4 ++-- src/hooks/coreSDK/useInitSafeCoreSDK.ts | 4 ++-- src/services/contracts/safeContracts.ts | 2 +- 8 files changed, 13 insertions(+), 13 deletions(-) diff --git a/package.json b/package.json index 2262e42152..80a7a0efd3 100644 --- a/package.json +++ b/package.json @@ -47,7 +47,7 @@ "@reduxjs/toolkit": "^1.8.2", "@safe-global/safe-core-sdk": "^3.2.0", "@safe-global/safe-ethers-lib": "^1.7.0", - "@safe-global/safe-gateway-typescript-sdk": "^3.5.3", + "@safe-global/safe-gateway-typescript-sdk": "^3.5.4", "@sentry/react": "^7.8.1", "@sentry/tracing": "^7.8.1", "@truffle/hdwallet-provider": "^2.0.14", diff --git a/src/components/settings/ContractVersion/index.tsx b/src/components/settings/ContractVersion/index.tsx index 76b428a1b4..e1399b22fd 100644 --- a/src/components/settings/ContractVersion/index.tsx +++ b/src/components/settings/ContractVersion/index.tsx @@ -33,7 +33,7 @@ export const ContractVersion = ({ isGranted }: { isGranted: boolean }) => { Contract version - {safe.version} + {safe?.version} {getSafeVersionUpdate()} diff --git a/src/components/settings/TransactionGuards/index.tsx b/src/components/settings/TransactionGuards/index.tsx index bd3ad9b753..a0b8f8340f 100644 --- a/src/components/settings/TransactionGuards/index.tsx +++ b/src/components/settings/TransactionGuards/index.tsx @@ -32,7 +32,7 @@ const GUARD_SUPPORTED_SAFE_VERSION = '1.3.0' const TransactionGuards = () => { const { safe, safeLoaded } = useSafeInfo() - const isVersionWithGuards = safeLoaded && gte(safe.version, GUARD_SUPPORTED_SAFE_VERSION) + const isVersionWithGuards = safeLoaded && safe?.version && gte(safe.version, GUARD_SUPPORTED_SAFE_VERSION) if (!isVersionWithGuards) { return null diff --git a/src/components/tx/TxSimulation/utils.ts b/src/components/tx/TxSimulation/utils.ts index 0c4974ff5c..81c8e84b69 100644 --- a/src/components/tx/TxSimulation/utils.ts +++ b/src/components/tx/TxSimulation/utils.ts @@ -108,7 +108,7 @@ export const _getMultiSendCallOnlyPayload = ( params: MultiSendTransactionSimulationParams, ): Pick => { const data = encodeMultiSendData(params.transactions) - const instance = getMultiSendCallOnlyContractInstance(params.safe.chainId, params.safe.version) + const instance = getMultiSendCallOnlyContractInstance(params.safe.chainId, params.safe?.version) return { to: instance.getAddress(), diff --git a/src/components/tx/modals/BatchExecuteModal/ReviewBatchExecute.tsx b/src/components/tx/modals/BatchExecuteModal/ReviewBatchExecute.tsx index 54ba7e0fcc..e4405421f1 100644 --- a/src/components/tx/modals/BatchExecuteModal/ReviewBatchExecute.tsx +++ b/src/components/tx/modals/BatchExecuteModal/ReviewBatchExecute.tsx @@ -32,14 +32,14 @@ const ReviewBatchExecute = ({ data, onSubmit }: { data: BatchExecuteData; onSubm }, [data.txs, chain?.chainId]) const multiSendContract = useMemo(() => { - if (!chain?.chainId) return + if (!chain?.chainId || !safe?.version) return return getMultiSendCallOnlyContractInstance(chain.chainId, safe.version) - }, [chain?.chainId, safe.version]) + }, [chain?.chainId, safe?.version]) const multiSendTxs = useMemo(() => { - if (!txsWithDetails || !chain) return + if (!txsWithDetails || !chain || !safe?.version) return return getMultiSendTxs(txsWithDetails, chain, safe.address.value, safe.version) - }, [chain, safe.address.value, safe.version, txsWithDetails]) + }, [chain, safe.address.value, safe?.version, txsWithDetails]) const multiSendTxData = useMemo(() => { if (!txsWithDetails || !multiSendTxs) return diff --git a/src/hooks/coreSDK/safeCoreSDK.ts b/src/hooks/coreSDK/safeCoreSDK.ts index 26bc622003..87d75116aa 100644 --- a/src/hooks/coreSDK/safeCoreSDK.ts +++ b/src/hooks/coreSDK/safeCoreSDK.ts @@ -36,11 +36,11 @@ export const initSafeSDK = async ( provider: EIP1193Provider, chainId: string, safeAddress: string, - safeVersion: string, + safeVersion?: string, ): Promise => { let isL1SafeMasterCopy = chainId === chains.eth // Legacy Safe contracts - if (isLegacyVersion(safeVersion)) { + if (safeVersion && isLegacyVersion(safeVersion)) { isL1SafeMasterCopy = true } diff --git a/src/hooks/coreSDK/useInitSafeCoreSDK.ts b/src/hooks/coreSDK/useInitSafeCoreSDK.ts index 4cda186c11..f68f3f6328 100644 --- a/src/hooks/coreSDK/useInitSafeCoreSDK.ts +++ b/src/hooks/coreSDK/useInitSafeCoreSDK.ts @@ -13,7 +13,7 @@ export const useInitSafeCoreSDK = () => { const dispatch = useAppDispatch() useEffect(() => { - if (!safeLoaded || !wallet?.provider || safe.chainId !== wallet.chainId) { + if (!safeLoaded || !wallet?.provider || safe.chainId !== wallet.chainId || !safe?.version) { // If we don't reset the SDK, a previous Safe could remain in the store setSafeSDK(undefined) return @@ -31,5 +31,5 @@ export const useInitSafeCoreSDK = () => { ) trackError(ErrorCodes._105, (e as Error).message) }) - }, [wallet?.provider, wallet?.chainId, safe.chainId, safe.address.value, safe.version, safeLoaded, dispatch]) + }, [wallet?.provider, wallet?.chainId, safe.chainId, safe.address.value, safe?.version, safeLoaded, dispatch]) } diff --git a/src/services/contracts/safeContracts.ts b/src/services/contracts/safeContracts.ts index 408e656fa5..12de9895d3 100644 --- a/src/services/contracts/safeContracts.ts +++ b/src/services/contracts/safeContracts.ts @@ -50,7 +50,7 @@ export const getSpecificGnosisSafeContractInstance = (safe: SafeInfo) => { return ethAdapter.getSafeContract({ customContractAddress: safe.address.value, - ..._getValidatedGetContractProps(safe.chainId, safe.version), + ..._getValidatedGetContractProps(safe.chainId, safe?.version), }) } From 189ec4a5d2814649a608c113a5de81d6d5c45a3f Mon Sep 17 00:00:00 2001 From: iamacook Date: Fri, 9 Dec 2022 13:12:36 +0100 Subject: [PATCH 2/5] fix: install + fix types w/ new package --- src/hooks/coreSDK/safeCoreSDK.ts | 13 ++++++++++--- src/services/contracts/safeContracts.ts | 13 +++++++------ src/services/tx/safeUpdateParams.ts | 3 +++ src/utils/helpers.ts | 8 ++++++++ yarn.lock | 8 ++++---- 5 files changed, 32 insertions(+), 13 deletions(-) create mode 100644 src/utils/helpers.ts diff --git a/src/hooks/coreSDK/safeCoreSDK.ts b/src/hooks/coreSDK/safeCoreSDK.ts index 87d75116aa..ae8510b2d5 100644 --- a/src/hooks/coreSDK/safeCoreSDK.ts +++ b/src/hooks/coreSDK/safeCoreSDK.ts @@ -1,10 +1,12 @@ import chains from '@/config/chains' import { getWeb3 } from '@/hooks/wallets/web3' import ExternalStore from '@/services/ExternalStore' +import { invariant } from '@/utils/helpers' import { Web3Provider } from '@ethersproject/providers' import Safe from '@safe-global/safe-core-sdk' import type { SafeVersion } from '@safe-global/safe-core-sdk-types' import EthersAdapter from '@safe-global/safe-ethers-lib' +import type { SafeInfo } from '@safe-global/safe-gateway-typescript-sdk' import { type EIP1193Provider } from '@web3-onboard/core' import { ethers } from 'ethers' import semverSatisfies from 'semver/functions/satisfies' @@ -14,11 +16,16 @@ export const isLegacyVersion = (safeVersion: string): boolean => { return semverSatisfies(safeVersion, LEGACY_VERSION) } -export const isValidSafeVersion = (safeVersion?: string): safeVersion is SafeVersion => { +export const isValidSafeVersion = (safeVersion?: SafeInfo['version']): safeVersion is SafeVersion => { const SAFE_VERSIONS: SafeVersion[] = ['1.3.0', '1.2.0', '1.1.1'] return !!safeVersion && SAFE_VERSIONS.some((version) => semverSatisfies(safeVersion, version)) } +// `assert` does not work with arrow functions +export function assertValidSafeVersion(safeVersion?: T): asserts safeVersion { + return invariant(isValidSafeVersion(safeVersion), `${safeVersion} is not a valid Safe version`) +} + export const createEthersAdapter = (provider = getWeb3()) => { if (!provider) { throw new Error('Unable to create `EthersAdapter` without a provider') @@ -36,11 +43,11 @@ export const initSafeSDK = async ( provider: EIP1193Provider, chainId: string, safeAddress: string, - safeVersion?: string, + safeVersion: string, ): Promise => { let isL1SafeMasterCopy = chainId === chains.eth // Legacy Safe contracts - if (safeVersion && isLegacyVersion(safeVersion)) { + if (isLegacyVersion(safeVersion)) { isL1SafeMasterCopy = true } diff --git a/src/services/contracts/safeContracts.ts b/src/services/contracts/safeContracts.ts index 12de9895d3..e577c0c615 100644 --- a/src/services/contracts/safeContracts.ts +++ b/src/services/contracts/safeContracts.ts @@ -16,7 +16,7 @@ import type { SafeInfo } from '@safe-global/safe-gateway-typescript-sdk' import { getMasterCopies, type ChainInfo } from '@safe-global/safe-gateway-typescript-sdk' import type { GetContractProps, SafeVersion } from '@safe-global/safe-core-sdk-types' import { type Sign_message_lib } from '@/types/contracts/Sign_message_lib' -import { createEthersAdapter, isValidSafeVersion } from '@/hooks/coreSDK/safeCoreSDK' +import { assertValidSafeVersion, createEthersAdapter } from '@/hooks/coreSDK/safeCoreSDK' import { sameAddress } from '@/utils/addresses' import type CompatibilityFallbackHandlerEthersContract from '@safe-global/safe-ethers-lib/dist/src/contracts/CompatibilityFallbackHandler/CompatibilityFallbackHandlerEthersContract' @@ -27,11 +27,9 @@ export const isValidMasterCopy = async (chainId: string, address: string): Promi export const _getValidatedGetContractProps = ( chainId: string, - safeVersion: string, + safeVersion: SafeInfo['version'], ): Pick => { - if (!isValidSafeVersion(safeVersion)) { - throw new Error(`${safeVersion} is not a valid Safe version`) - } + assertValidSafeVersion(safeVersion) // SDK request here: https://github.com/safe-global/safe-core-sdk/issues/261 // Remove '+L2'/'+Circles' metadata from version @@ -125,7 +123,10 @@ export const getMultiSendCallOnlyContractAddress = (chainId: string): string | u return deployment?.networkAddresses[chainId] } -export const getMultiSendCallOnlyContractInstance = (chainId: string, safeVersion: string = LATEST_SAFE_VERSION) => { +export const getMultiSendCallOnlyContractInstance = ( + chainId: string, + safeVersion: SafeInfo['version'] = LATEST_SAFE_VERSION, +) => { const ethAdapter = createEthersAdapter() return ethAdapter.getMultiSendCallOnlyContract({ diff --git a/src/services/tx/safeUpdateParams.ts b/src/services/tx/safeUpdateParams.ts index 8a0bb6669c..735680ce59 100644 --- a/src/services/tx/safeUpdateParams.ts +++ b/src/services/tx/safeUpdateParams.ts @@ -3,6 +3,7 @@ import { OperationType } from '@safe-global/safe-core-sdk-types' import type { ChainInfo, SafeInfo } from '@safe-global/safe-gateway-typescript-sdk' import { getFallbackHandlerContractInstance, getGnosisSafeContractInstance } from '@/services/contracts/safeContracts' import { LATEST_SAFE_VERSION } from '@/config/constants' +import { assertValidSafeVersion } from '@/hooks/coreSDK/safeCoreSDK' // TODO: Check if these are still needed export const CHANGE_MASTER_COPY_ABI = 'function changeMasterCopy(address _masterCopy)' @@ -15,6 +16,8 @@ export const CHANGE_FALLBACK_HANDLER_ABI = 'function setFallbackHandler(address * Only works for safes < 1.3.0 as the changeMasterCopy function was removed */ export const createUpdateSafeTxs = (safe: SafeInfo, chain: ChainInfo): MetaTransactionData[] => { + assertValidSafeVersion(safe.version) + const latestMasterCopy = getGnosisSafeContractInstance(chain, LATEST_SAFE_VERSION) const safeContractInstance = getGnosisSafeContractInstance(chain, safe.version) diff --git a/src/utils/helpers.ts b/src/utils/helpers.ts new file mode 100644 index 0000000000..6592417b31 --- /dev/null +++ b/src/utils/helpers.ts @@ -0,0 +1,8 @@ +// `assert` does not work with arrow functions +export function invariant(condition: T, error: string): asserts condition { + if (condition) { + return + } + + throw new Error(error) +} diff --git a/yarn.lock b/yarn.lock index fdd00ecf50..8b6a1c4af2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3275,10 +3275,10 @@ "@safe-global/safe-core-sdk-utils" "^1.5.0" ethers "^5.7.2" -"@safe-global/safe-gateway-typescript-sdk@^3.5.3": - version "3.5.3" - resolved "https://registry.yarnpkg.com/@safe-global/safe-gateway-typescript-sdk/-/safe-gateway-typescript-sdk-3.5.3.tgz#07b5307601ba9d9d0d62f6366fdfc3cdfdfc6f87" - integrity sha512-HzvCmFHw9NEOJK9d3X8nqG3MMuw+/ceccltrgBenHn8OUxNkv8za9jUou0YUmG0HtJ4tKFTW+z8AwpohHTmWAw== +"@safe-global/safe-gateway-typescript-sdk@^3.5.4": + version "3.5.4" + resolved "https://registry.yarnpkg.com/@safe-global/safe-gateway-typescript-sdk/-/safe-gateway-typescript-sdk-3.5.4.tgz#daa13d830eb8b6c8f60bf4026be42a9965abcc35" + integrity sha512-olTC+2lxb45MyG5Rxgs0UO1gd3MKYW8ahTvY88nV64KMEbjQQUl0lbQ8VlVT7BZsWuVgG7HshrPDsKU/wPVTeA== dependencies: cross-fetch "^3.1.5" From 03bc61f5db2b034f146172b98e4be5f1bf3eed34 Mon Sep 17 00:00:00 2001 From: iamacook Date: Fri, 9 Dec 2022 13:16:49 +0100 Subject: [PATCH 3/5] fix: remove unnecessary optional chaining --- src/components/settings/ContractVersion/index.tsx | 2 +- src/components/settings/TransactionGuards/index.tsx | 2 +- src/components/tx/TxSimulation/utils.ts | 2 +- .../tx/modals/BatchExecuteModal/ReviewBatchExecute.tsx | 8 ++++---- src/hooks/coreSDK/useInitSafeCoreSDK.ts | 4 ++-- src/services/contracts/safeContracts.ts | 2 +- 6 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/components/settings/ContractVersion/index.tsx b/src/components/settings/ContractVersion/index.tsx index e1399b22fd..76b428a1b4 100644 --- a/src/components/settings/ContractVersion/index.tsx +++ b/src/components/settings/ContractVersion/index.tsx @@ -33,7 +33,7 @@ export const ContractVersion = ({ isGranted }: { isGranted: boolean }) => { Contract version - {safe?.version} + {safe.version} {getSafeVersionUpdate()} diff --git a/src/components/settings/TransactionGuards/index.tsx b/src/components/settings/TransactionGuards/index.tsx index a0b8f8340f..b477d7ed62 100644 --- a/src/components/settings/TransactionGuards/index.tsx +++ b/src/components/settings/TransactionGuards/index.tsx @@ -32,7 +32,7 @@ const GUARD_SUPPORTED_SAFE_VERSION = '1.3.0' const TransactionGuards = () => { const { safe, safeLoaded } = useSafeInfo() - const isVersionWithGuards = safeLoaded && safe?.version && gte(safe.version, GUARD_SUPPORTED_SAFE_VERSION) + const isVersionWithGuards = safeLoaded && safe.version && gte(safe.version, GUARD_SUPPORTED_SAFE_VERSION) if (!isVersionWithGuards) { return null diff --git a/src/components/tx/TxSimulation/utils.ts b/src/components/tx/TxSimulation/utils.ts index 81c8e84b69..0c4974ff5c 100644 --- a/src/components/tx/TxSimulation/utils.ts +++ b/src/components/tx/TxSimulation/utils.ts @@ -108,7 +108,7 @@ export const _getMultiSendCallOnlyPayload = ( params: MultiSendTransactionSimulationParams, ): Pick => { const data = encodeMultiSendData(params.transactions) - const instance = getMultiSendCallOnlyContractInstance(params.safe.chainId, params.safe?.version) + const instance = getMultiSendCallOnlyContractInstance(params.safe.chainId, params.safe.version) return { to: instance.getAddress(), diff --git a/src/components/tx/modals/BatchExecuteModal/ReviewBatchExecute.tsx b/src/components/tx/modals/BatchExecuteModal/ReviewBatchExecute.tsx index e4405421f1..74a7593327 100644 --- a/src/components/tx/modals/BatchExecuteModal/ReviewBatchExecute.tsx +++ b/src/components/tx/modals/BatchExecuteModal/ReviewBatchExecute.tsx @@ -32,14 +32,14 @@ const ReviewBatchExecute = ({ data, onSubmit }: { data: BatchExecuteData; onSubm }, [data.txs, chain?.chainId]) const multiSendContract = useMemo(() => { - if (!chain?.chainId || !safe?.version) return + if (!chain?.chainId || !safe.version) return return getMultiSendCallOnlyContractInstance(chain.chainId, safe.version) - }, [chain?.chainId, safe?.version]) + }, [chain?.chainId, safe.version]) const multiSendTxs = useMemo(() => { - if (!txsWithDetails || !chain || !safe?.version) return + if (!txsWithDetails || !chain || !safe.version) return return getMultiSendTxs(txsWithDetails, chain, safe.address.value, safe.version) - }, [chain, safe.address.value, safe?.version, txsWithDetails]) + }, [chain, safe.address.value, safe.version, txsWithDetails]) const multiSendTxData = useMemo(() => { if (!txsWithDetails || !multiSendTxs) return diff --git a/src/hooks/coreSDK/useInitSafeCoreSDK.ts b/src/hooks/coreSDK/useInitSafeCoreSDK.ts index f68f3f6328..98955e1cf3 100644 --- a/src/hooks/coreSDK/useInitSafeCoreSDK.ts +++ b/src/hooks/coreSDK/useInitSafeCoreSDK.ts @@ -13,7 +13,7 @@ export const useInitSafeCoreSDK = () => { const dispatch = useAppDispatch() useEffect(() => { - if (!safeLoaded || !wallet?.provider || safe.chainId !== wallet.chainId || !safe?.version) { + if (!safeLoaded || !wallet?.provider || safe.chainId !== wallet.chainId || !safe.version) { // If we don't reset the SDK, a previous Safe could remain in the store setSafeSDK(undefined) return @@ -31,5 +31,5 @@ export const useInitSafeCoreSDK = () => { ) trackError(ErrorCodes._105, (e as Error).message) }) - }, [wallet?.provider, wallet?.chainId, safe.chainId, safe.address.value, safe?.version, safeLoaded, dispatch]) + }, [wallet?.provider, wallet?.chainId, safe.chainId, safe.address.value, safe.version, safeLoaded, dispatch]) } diff --git a/src/services/contracts/safeContracts.ts b/src/services/contracts/safeContracts.ts index e577c0c615..84d178bae5 100644 --- a/src/services/contracts/safeContracts.ts +++ b/src/services/contracts/safeContracts.ts @@ -48,7 +48,7 @@ export const getSpecificGnosisSafeContractInstance = (safe: SafeInfo) => { return ethAdapter.getSafeContract({ customContractAddress: safe.address.value, - ..._getValidatedGetContractProps(safe.chainId, safe?.version), + ..._getValidatedGetContractProps(safe.chainId, safe.version), }) } From e7be67aae81e9c5e7a78d7f0437b7a127bb5858b Mon Sep 17 00:00:00 2001 From: iamacook Date: Fri, 9 Dec 2022 15:42:17 +0100 Subject: [PATCH 4/5] fix: add fallback version text --- src/components/settings/ContractVersion/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/settings/ContractVersion/index.tsx b/src/components/settings/ContractVersion/index.tsx index 76b428a1b4..7dc2884516 100644 --- a/src/components/settings/ContractVersion/index.tsx +++ b/src/components/settings/ContractVersion/index.tsx @@ -33,7 +33,7 @@ export const ContractVersion = ({ isGranted }: { isGranted: boolean }) => { Contract version - {safe.version} + {safe.version || 'Unsupported contract'} {getSafeVersionUpdate()} From 0cfc6e34ce8f1d217ece97ebea42bcdca3358aea Mon Sep 17 00:00:00 2001 From: iamacook Date: Tue, 13 Dec 2022 10:56:49 +0100 Subject: [PATCH 5/5] fix: don't show link for unsupported contract --- src/components/settings/ContractVersion/index.tsx | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/components/settings/ContractVersion/index.tsx b/src/components/settings/ContractVersion/index.tsx index 7dc2884516..f866459b92 100644 --- a/src/components/settings/ContractVersion/index.tsx +++ b/src/components/settings/ContractVersion/index.tsx @@ -32,10 +32,16 @@ export const ContractVersion = ({ isGranted }: { isGranted: boolean }) => { Contract version - - {safe.version || 'Unsupported contract'} - {getSafeVersionUpdate()} - + {safe.version ? ( + + {safe.version} + {getSafeVersionUpdate()} + + ) : ( + + Unsupported contract + + )} {showUpdateDialog && isGranted && }