Skip to content

Commit

Permalink
Merge branch 'release/1.19.0' of github.com:safe-global/safe-wallet-w…
Browse files Browse the repository at this point in the history
…eb into dev
  • Loading branch information
katspaugh committed Sep 27, 2023
2 parents dccfef7 + b420813 commit 3e026a4
Show file tree
Hide file tree
Showing 8 changed files with 170 additions and 43 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,11 @@ export const PushNotificationsBanner = ({ children }: { children: ReactElement }
const allPreferences = getAllPreferences()
const safesToRegister = getSafesToRegister(addedSafes, allPreferences)

await assertWalletChain(onboard, safe.chainId)
try {
await assertWalletChain(onboard, safe.chainId)
} catch {
return
}

await registerNotifications(safesToRegister)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,11 @@ import * as sdk from '@safe-global/safe-gateway-typescript-sdk'
import { renderHook } from '@/tests/test-utils'
import { useNotificationRegistrations } from '../useNotificationRegistrations'
import * as web3 from '@/hooks/wallets/web3'
import * as wallet from '@/hooks/wallets/useWallet'
import * as logic from '../../logic'
import * as preferences from '../useNotificationPreferences'
import * as notificationsSlice from '@/store/notificationsSlice'
import type { ConnectedWallet } from '@/services/onboard'

jest.mock('@safe-global/safe-gateway-typescript-sdk')

Expand All @@ -28,7 +30,13 @@ describe('useNotificationRegistrations', () => {
describe('registerNotifications', () => {
beforeEach(() => {
const mockProvider = new Web3Provider(jest.fn())
jest.spyOn(web3, 'useWeb3').mockImplementation(() => mockProvider)
jest.spyOn(web3, 'createWeb3').mockImplementation(() => mockProvider)
jest.spyOn(wallet, 'default').mockImplementation(
() =>
({
label: 'MetaMask',
} as ConnectedWallet),
)
})

const registerDeviceSpy = jest.spyOn(sdk, 'registerDevice')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ import {
} from '@/services/push-notifications/preferences'
import { logError } from '@/services/exceptions'
import ErrorCodes from '@/services/exceptions/ErrorCodes'
import useIsSafeOwner from '@/hooks/useIsSafeOwner'
import type { PushNotificationPreferences, PushNotificationPrefsKey } from '@/services/push-notifications/preferences'
import type { NotifiableSafes } from '../logic'

Expand All @@ -26,7 +25,7 @@ export const DEFAULT_NOTIFICATION_PREFERENCES: PushNotificationPreferences[PushN
[WebhookType.INCOMING_ETHER]: true,
[WebhookType.INCOMING_TOKEN]: true,
[WebhookType.MODULE_TRANSACTION]: true,
[WebhookType.CONFIRMATION_REQUEST]: false, // Requires signature
[WebhookType.CONFIRMATION_REQUEST]: true, // Requires signature
[WebhookType.SAFE_CREATED]: false, // We do not preemptively subscribe to Safes before they are created
// Disabled on the Transaction Service but kept here for completeness
[WebhookType._PENDING_MULTISIG_TRANSACTION]: true,
Expand Down Expand Up @@ -59,7 +58,6 @@ export const useNotificationPreferences = (): {
// State
const uuid = useUuid()
const preferences = usePreferences()
const isOwner = useIsSafeOwner()

// Getters
const getPreferences = (chainId: string, safeAddress: string) => {
Expand Down Expand Up @@ -152,10 +150,7 @@ export const useNotificationPreferences = (): {
const defaultPreferences: PushNotificationPreferences[PushNotificationPrefsKey] = {
chainId,
safeAddress,
preferences: {
...DEFAULT_NOTIFICATION_PREFERENCES,
[WebhookType.CONFIRMATION_REQUEST]: isOwner,
},
preferences: DEFAULT_NOTIFICATION_PREFERENCES,
}

return [key, defaultPreferences]
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { registerDevice, unregisterDevice, unregisterSafe } from '@safe-global/safe-gateway-typescript-sdk'

import { useWeb3 } from '@/hooks/wallets/web3'
import { useAppDispatch } from '@/store'
import { showNotification } from '@/store/notificationsSlice'
import { useNotificationPreferences } from './useNotificationPreferences'
Expand All @@ -9,6 +8,7 @@ import { PUSH_NOTIFICATION_EVENTS } from '@/services/analytics/events/push-notif
import { getRegisterDevicePayload } from '../logic'
import { logError } from '@/services/exceptions'
import ErrorCodes from '@/services/exceptions/ErrorCodes'
import useWallet from '@/hooks/wallets/useWallet'
import type { NotifiableSafes } from '../logic'

const registrationFlow = async (registrationFn: Promise<unknown>, callback: () => void): Promise<boolean> => {
Expand Down Expand Up @@ -37,20 +37,20 @@ export const useNotificationRegistrations = (): {
unregisterDeviceNotifications: (chainId: string) => Promise<boolean | undefined>
} => {
const dispatch = useAppDispatch()
const web3 = useWeb3()
const wallet = useWallet()

const { uuid, _createPreferences, _deletePreferences, _deleteAllPreferences } = useNotificationPreferences()

const registerNotifications = async (safesToRegister: NotifiableSafes) => {
if (!uuid || !web3) {
if (!uuid || !wallet) {
return
}

const register = async () => {
const payload = await getRegisterDevicePayload({
uuid,
safesToRegister,
web3,
wallet,
})

return registerDevice(payload)
Expand Down
6 changes: 5 additions & 1 deletion src/components/settings/PushNotifications/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,11 @@ export const PushNotifications = (): ReactElement => {

setIsRegistering(true)

await assertWalletChain(onboard, safe.chainId)
try {
await assertWalletChain(onboard, safe.chainId)
} catch {
return
}

if (!preferences) {
await registerNotifications({ [safe.chainId]: [safe.address.value] })
Expand Down
91 changes: 86 additions & 5 deletions src/components/settings/PushNotifications/logic.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ import { Web3Provider } from '@ethersproject/providers'
import type { JsonRpcSigner } from '@ethersproject/providers'

import * as logic from './logic'
import * as web3 from '@/hooks/wallets/web3'
import packageJson from '../../../../package.json'
import type { ConnectedWallet } from '@/services/onboard'

jest.mock('firebase/messaging')

Expand All @@ -29,6 +31,13 @@ Object.defineProperty(globalThis, 'location', {
},
})

const MM_SIGNATURE =
'0x844ba559793a122c5742e9d922ed1f4650d4efd8ea35191105ddaee6a604000165c14f56278bda8d52c9400cdaeaf5cdc38d3596264cc5ccd8f03e5619d5d9d41b'
const LEDGER_SIGNATURE =
'0xb1274687aea0d8b8578a3eb6da57979eee0a64225e04445a0858e6f8d0d1b5870cdff961513992d849e47e9b0a8d432019829f1e4958837fd86e034656766a4e00'
const ADJUSTED_LEDGER_SIGNATURE =
'0xb1274687aea0d8b8578a3eb6da57979eee0a64225e04445a0858e6f8d0d1b5870cdff961513992d849e47e9b0a8d432019829f1e4958837fd86e034656766a4e1b'

describe('Notifications', () => {
let alertMock = jest.fn()

Expand Down Expand Up @@ -88,20 +97,90 @@ describe('Notifications', () => {
})
})

describe('adjustLegerSignature', () => {
it('should return the same signature if not that of a Ledger', () => {
const adjustedSignature = logic._adjustLedgerSignatureV(MM_SIGNATURE)

expect(adjustedSignature).toBe(MM_SIGNATURE)
})

it('should return an adjusted signature if is that of a Ledger and v is 0 or 1', () => {
const adjustedSignature = logic._adjustLedgerSignatureV(LEDGER_SIGNATURE)

expect(adjustedSignature).toBe(ADJUSTED_LEDGER_SIGNATURE)
})

it('should return the same signature if v is 27 or 28', () => {
const adjustedSignature = logic._adjustLedgerSignatureV(MM_SIGNATURE)

expect(adjustedSignature).toBe(MM_SIGNATURE)
})
})

describe('getRegisterDevicePayload', () => {
it('should return the payload with signature', async () => {
const token = crypto.randomUUID()
jest.spyOn(firebase, 'getToken').mockImplementation(() => Promise.resolve(token))

const mockProvider = new Web3Provider(jest.fn())
const signature = hexZeroPad('0x69420', 65)

jest.spyOn(mockProvider, 'getSigner').mockImplementation(
() =>
({
signMessage: jest.fn().mockResolvedValueOnce(signature),
signMessage: jest.fn().mockResolvedValueOnce(MM_SIGNATURE),
} as unknown as JsonRpcSigner),
)
jest.spyOn(web3, 'createWeb3').mockImplementation(() => mockProvider)

const uuid = crypto.randomUUID()

const payload = await logic.getRegisterDevicePayload({
safesToRegister: {
['1']: [hexZeroPad('0x1', 20), hexZeroPad('0x2', 20)],
['2']: [hexZeroPad('0x1', 20)],
},
uuid,
wallet: {
label: 'MetaMask',
} as ConnectedWallet,
})

expect(payload).toStrictEqual({
uuid,
cloudMessagingToken: token,
buildNumber: '0',
bundle: 'safe',
deviceType: DeviceType.WEB,
version: packageJson.version,
timestamp: expect.any(String),
safeRegistrations: [
{
chainId: '1',
safes: [hexZeroPad('0x1', 20), hexZeroPad('0x2', 20)],
signatures: [MM_SIGNATURE],
},
{
chainId: '2',
safes: [hexZeroPad('0x1', 20)],
signatures: [MM_SIGNATURE],
},
],
})
})

it('should return the payload with a Ledger adjusted signature', async () => {
const token = crypto.randomUUID()
jest.spyOn(firebase, 'getToken').mockImplementation(() => Promise.resolve(token))

const mockProvider = new Web3Provider(jest.fn())

jest.spyOn(mockProvider, 'getSigner').mockImplementation(
() =>
({
signMessage: jest.fn().mockResolvedValueOnce(LEDGER_SIGNATURE),
} as unknown as JsonRpcSigner),
)
jest.spyOn(web3, 'createWeb3').mockImplementation(() => mockProvider)

const uuid = crypto.randomUUID()

Expand All @@ -111,7 +190,9 @@ describe('Notifications', () => {
['2']: [hexZeroPad('0x1', 20)],
},
uuid,
web3: mockProvider,
wallet: {
label: 'Ledger',
} as ConnectedWallet,
})

expect(payload).toStrictEqual({
Expand All @@ -126,12 +207,12 @@ describe('Notifications', () => {
{
chainId: '1',
safes: [hexZeroPad('0x1', 20), hexZeroPad('0x2', 20)],
signatures: [signature],
signatures: [ADJUSTED_LEDGER_SIGNATURE],
},
{
chainId: '2',
safes: [hexZeroPad('0x1', 20)],
signatures: [signature],
signatures: [ADJUSTED_LEDGER_SIGNATURE],
},
],
})
Expand Down
Loading

0 comments on commit 3e026a4

Please sign in to comment.