Skip to content

Commit

Permalink
feat: add feature toggle for zodiac roles integration (#3830)
Browse files Browse the repository at this point in the history
* feat: add feature toggle for zodiac roles integration

* refactor: rewrite mock
  • Loading branch information
schmanu authored Jun 12, 2024
1 parent 64e6e48 commit 02c99b6
Show file tree
Hide file tree
Showing 4 changed files with 55 additions and 21 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ import { type OnboardAPI } from '@web3-onboard/core'
import { AbiCoder, ZeroAddress, encodeBytes32String } from 'ethers'
import PermissionsCheck from '..'
import * as hooksModule from '../hooks'
import { FEATURES } from '@/utils/chains'
import { chainBuilder } from '@/tests/builders/chains'
import { useHasFeature } from '@/hooks/useChains'

// We assume that CheckWallet always returns true
jest.mock('@/components/common/CheckWallet', () => ({
Expand All @@ -23,16 +26,25 @@ jest.mock('@/components/common/CheckWallet', () => ({
},
}))

// mock useCurrentChain & useHasFeature
const mockChain = chainBuilder()
// @ts-expect-error - we are using a local FEATURES enum
.with({ features: [FEATURES.ZODIAC_ROLES, FEATURES.EIP1559] })
.with({ chainId: '1' })
.with({ shortName: 'eth' })
.with({ chainName: 'Ethereum' })
.with({ transactionService: 'https://tx.service.mock' })
.build()

// mock useCurrentChain
jest.mock('@/hooks/useChains', () => ({
useCurrentChain: jest.fn(() => ({
shortName: 'eth',
chainId: '1',
chainName: 'Ethereum',
features: [],
transactionService: 'https://tx.service.mock',
})),
useHasFeature: jest.fn(() => true), // used to check for EIP1559 support
__esModule: true,
...jest.requireActual('@/hooks/useChains'),
useCurrentChain: jest.fn(() => mockChain),
useHasFeature: jest.fn(),
}))

jest.mock('@/hooks/useChainId', () => ({
useChainId: jest.fn().mockReturnValue(() => '1'),
}))

// mock getModuleTransactionId
Expand Down Expand Up @@ -72,15 +84,15 @@ describe('PermissionsCheck', () => {

beforeEach(() => {
jest.clearAllMocks()

// Safe info
jest.spyOn(useSafeInfoHook, 'default').mockImplementation(() => ({
safe: SAFE_INFO,
safeAddress: SAFE_INFO.address.value,
safeError: undefined,
safeLoading: false,
safeLoaded: true,
}))
;(useHasFeature as jest.Mock).mockImplementation((feature) => mockChain.features.includes(feature)),
// Safe info
jest.spyOn(useSafeInfoHook, 'default').mockImplementation(() => ({
safe: SAFE_INFO,
safeAddress: SAFE_INFO.address.value,
safeError: undefined,
safeLoading: false,
safeLoaded: true,
}))

// Roles mod fetching

Expand All @@ -100,6 +112,25 @@ describe('PermissionsCheck', () => {
jest.spyOn(hooksModule, 'pollModuleTransactionId').mockReturnValue(Promise.resolve('i1234567890'))
})

it('only fetch roles and show the card if the feature is enabled', async () => {
;(useHasFeature as jest.Mock).mockImplementation((feature) => feature !== FEATURES.ZODIAC_ROLES)
mockConnectedWalletAddress(SAFE_INFO.owners[0].value) // connect as safe owner (not a role member)

const safeTx = createMockSafeTransaction({
to: ZeroAddress,
data: '0xd0e30db0', // deposit()
value: AbiCoder.defaultAbiCoder().encode(['uint256'], [123]),
operation: OperationType.Call,
})

const { queryByText } = render(<PermissionsCheck safeTx={safeTx} />)

// the card is not shown
expect(queryByText('Execute without confirmations')).not.toBeInTheDocument()

expect(fetchRolesModMock).not.toHaveBeenCalled()
})

it('only shows the card when the user is a member of any role', async () => {
mockConnectedWalletAddress(SAFE_INFO.owners[0].value) // connect as safe owner (not a role member)

Expand Down
7 changes: 5 additions & 2 deletions src/components/tx/SignOrExecuteForm/PermissionsCheck/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ import { OperationType, type Transaction, type MetaTransactionData } from '@safe
import { type JsonRpcProvider } from 'ethers'
import { KnownContracts, getModuleInstance } from '@gnosis.pm/zodiac'
import useWallet from '@/hooks/wallets/useWallet'
import { useHasFeature } from '@/hooks/useChains'
import { FEATURES } from '@/utils/chains'

const ROLES_V2_SUPPORTED_CHAINS = Object.keys(chains)

Expand All @@ -26,9 +28,10 @@ const ROLES_V2_SUPPORTED_CHAINS = Object.keys(chains)
*/
export const useRolesMods = () => {
const { safe } = useSafeInfo()
const isFeatureEnabled = useHasFeature(FEATURES.ZODIAC_ROLES)

const [data] = useAsync(async () => {
if (!ROLES_V2_SUPPORTED_CHAINS.includes(safe.chainId)) return []
if (!ROLES_V2_SUPPORTED_CHAINS.includes(safe.chainId) || !isFeatureEnabled) return []

const safeModules = safe.modules || []
const rolesMods = await Promise.all(
Expand All @@ -44,7 +47,7 @@ export const useRolesMods = () => {
mod.avatar === safe.address.value.toLowerCase() &&
mod.roles.length > 0,
)
}, [safe])
}, [safe, isFeatureEnabled])

return data
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ jest.mock('@/hooks/useSafeInfo')
jest.mock('@/hooks/wallets/web3')
jest.mock('@/hooks/useChains')
jest.mock('@/hooks/useTxHistory')
jest.mock('@/hooks/useChains')

const mockUseSafeInfo = useSafeInfo as jest.MockedFunction<typeof useSafeInfo>
const mockUseWeb3ReadOnly = useWeb3ReadOnly as jest.MockedFunction<typeof useWeb3ReadOnly>
Expand Down
1 change: 1 addition & 0 deletions src/utils/chains.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export enum FEATURES {
SAP_BANNER = 'SAP_BANNER',
NATIVE_SWAPS = 'NATIVE_SWAPS',
RELAY_NATIVE_SWAPS = 'RELAY_NATIVE_SWAPS',
ZODIAC_ROLES = 'ZODIAC_ROLES',
}

export const FeatureRoutes = {
Expand Down

0 comments on commit 02c99b6

Please sign in to comment.