Skip to content

Commit

Permalink
fix(qr-scanner): Allow data with network prefixes (valora-inc#5262)
Browse files Browse the repository at this point in the history
### Description

just ignores the network prefix and reads the address

I considered trying to read and validate the network, but some things
stopped me:
1. lack of any kind of plumbing for a 'default network' in the send flow
to action upon the prefix. If we want to use it, should probably be
another ticket and have specific UX guidelines
2. Lack of consistency for how other wallets mark their data.

| Wallet              | Celo | Ethereum |
| :---------------- | :------: | ----: |
| Metamask        |   ethereum:0x   | ethereum:0x |
| Coinbase           |  celo:0x   | ethereum:0x |
| SafeWallet    |  celo:0x   | ethereum:0x |

Sometimes the wallets mark L2 chains as their own (celo, optimism,
arbitrum) and sometimes just label it 'ethereum'. This inconsistency
makes it difficult to action upon.

### Test plan

Unit tested with examples pulled from metamask and coinbase

### Related issues

- Fixes
https://linear.app/valora/issue/ACT-1155/chain-prefix-support-for-qr-scanner

---------

Co-authored-by: Satish Ravi <satish.ravi@valoraapp.com>
  • Loading branch information
2 people authored and shottah committed May 15, 2024
1 parent f334178 commit c742d3b
Show file tree
Hide file tree
Showing 2 changed files with 33 additions and 20 deletions.
45 changes: 28 additions & 17 deletions src/qrcode/utils.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -111,27 +111,38 @@ describe('handleQRCodeDefault', () => {
})
expect(ValoraAnalytics.track).toHaveBeenCalledWith(QrScreenEvents.qr_scanned, qrCode)
})
it('navigates to the send amount screen with a qr code with address as the data', async () => {
const qrCode: QrCode = { type: QRCodeTypes.QR_CODE, data: mockAccount }
it.each([mockAccount, `ethereum:${mockAccount}`, `celo:${mockAccount}`])(
'navigates to the send amount screen with a qr code with address as the data',
async (data) => {
const qrCode: QrCode = { type: QRCodeTypes.QR_CODE, data }

await expectSaga(handleQRCodeDefault, handleQRCodeDetected(qrCode))
.withState(createMockStore({}).getState())
.provide([[select(recipientInfoSelector), mockRecipientInfo]])
.run()
expect(navigate).toHaveBeenCalledWith(Screens.SendEnterAmount, {
origin: SendOrigin.AppSendFlow,
isFromScan: true,
recipient: {
address: mockAccount.toLowerCase(),
name: mockName,
contactId: 'contactId',
displayNumber: '14155550000',
thumbnailPath: undefined,
recipientType: RecipientType.Address,
},
forceTokenId: false,
})
expect(ValoraAnalytics.track).toHaveBeenCalledWith(QrScreenEvents.qr_scanned, qrCode)
}
)
it('throws an error when the QR code data is invalid', async () => {
const qrCode: QrCode = { type: QRCodeTypes.QR_CODE, data: mockAccount.replace('0x', '') }

await expectSaga(handleQRCodeDefault, handleQRCodeDetected(qrCode))
.withState(createMockStore({}).getState())
.provide([[select(recipientInfoSelector), mockRecipientInfo]])
.put(showError(ErrorMessages.QR_FAILED_INVALID_ADDRESS))
.run()
expect(navigate).toHaveBeenCalledWith(Screens.SendEnterAmount, {
origin: SendOrigin.AppSendFlow,
isFromScan: true,
recipient: {
address: mockAccount.toLowerCase(),
name: mockName,
contactId: 'contactId',
displayNumber: '14155550000',
thumbnailPath: undefined,
recipientType: RecipientType.Address,
},
forceTokenId: false,
})
expect(ValoraAnalytics.track).toHaveBeenCalledWith(QrScreenEvents.qr_scanned, qrCode)
})
it('navigates to the send amount screen with a qr code with an empty display name', async () => {
const qrCode: QrCode = {
Expand Down
8 changes: 5 additions & 3 deletions src/qrcode/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ import Logger from 'src/utils/Logger'
import { initialiseWalletConnect, isWalletConnectEnabled } from 'src/walletConnect/saga'
import { handleLoadingWithTimeout } from 'src/walletConnect/walletConnect'
import { call, fork, put, select } from 'typed-redux-saga'
import { isAddress } from 'viem'

export enum QRCodeTypes {
QR_CODE = 'QR_CODE',
Expand Down Expand Up @@ -126,9 +127,10 @@ export function* handleSecureSend(
}

function* extractQRAddressData(qrCode: QrCode) {
// Regex matches any 40 hexadecimal characters prefixed with "0x" (case insensitive)
if (/^0x[a-f0-9]{40}$/gi.test(qrCode.data)) {
qrCode.data = `celo://wallet/pay?address=${qrCode.data}`
// strip network prefix if present
const qrAddress = qrCode.data.split(':').at(-1) || qrCode.data
if (isAddress(qrAddress, { strict: false })) {
qrCode.data = `celo://wallet/pay?address=${qrAddress}`
}
let qrData: UriData | null
try {
Expand Down

0 comments on commit c742d3b

Please sign in to comment.