Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: address book undefined entry address #1338

Merged
merged 6 commits into from
Dec 19, 2022
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 20 additions & 1 deletion src/store/__tests__/addressBookSlice.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
import { addressBookSlice, setAddressBook, upsertAddressBookEntry, removeAddressBookEntry } from '../addressBookSlice'
import {
addressBookSlice,
setAddressBook,
upsertAddressBookEntry,
removeAddressBookEntry,
selectAddressBookByChain,
} from '../addressBookSlice'

const initialState = {
'1': { '0x0': 'Alice', '0x1': 'Bob' },
Expand Down Expand Up @@ -54,4 +60,17 @@ describe('addressBookSlice', () => {
'4': { '0x0': 'Charlie', '0x1': 'Dave' },
})
})

it('should not return entries with invalid address format', () => {
const initialState = {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Kudos for adding a test :)

'1': { '0x0': 'Alice', '0x1': 'Bob', '0x2': 'Fred' },
'5': { '0x744aaf04ad770895ce469300771d2ca38463cfa0': 'legit', undefined: 'bug' },
}

const expectedOutput = {
'0x744aaf04ad770895ce469300771d2ca38463cfa0': 'legit',
}

expect(selectAddressBookByChain.resultFunc(initialState, '5')).toEqual(expectedOutput)
})
})
6 changes: 5 additions & 1 deletion src/store/addressBookSlice.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { createSelector, createSlice, type PayloadAction } from '@reduxjs/toolkit'
import { ADDRESS_RE } from '@/utils/validation'
import { pickBy } from 'lodash'
import type { RootState } from '.'

export type AddressBook = { [address: string]: string }
Expand Down Expand Up @@ -45,6 +47,8 @@ export const selectAllAddressBooks = (state: RootState): AddressBookState => {
export const selectAddressBookByChain = createSelector(
[selectAllAddressBooks, (_, chainId: string) => chainId],
(allAddressBooks, chainId): AddressBook => {
return chainId ? allAddressBooks[chainId] || {} : {}
const chainAddresses = allAddressBooks[chainId]
const validAddresses = pickBy(chainAddresses, (_, key) => ADDRESS_RE.test(key))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have a function to validate addresses. This additionally validates that addresses are checksummed, which we also expect in our addressbook.

Suggested change
const validAddresses = pickBy(chainAddresses, (_, key) => ADDRESS_RE.test(key))
const validAddresses = pickBy(chainAddresses, (_, key) => validateAddress(key) === undefined)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It will not validate the address if it isn't checksummed. I didn't use it because it was filtering out the non-checksummed addresses

return chainId ? validAddresses || {} : {}
},
)
3 changes: 1 addition & 2 deletions src/utils/validation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,8 @@ import chains from '@/config/chains'
import { parsePrefixedAddress, sameAddress, isChecksummedAddress } from './addresses'
import { safeFormatUnits, safeParseUnits } from './formatters'

export const ADDRESS_RE = /^0x[0-9a-f]{40}$/i
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd move it back into validateAddress because that is the function which always should be used IMO.

If we need a helper that returns a boolean we should add something like

export const isValidAddress = (address: string) => {
   return validateAddress(address) === undefined
}

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Then we should probably split validateAddress function because it is doing two things at the moment:

  • validating the address format
  • validating if checksum

Initially I was using that function but it would filter out any address not checksummed and I think we don't want to go that strict in the AB, right?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we need a helper that returns a boolean

I think we can use the validation as it is -> returning undefined if passed all checks

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We checksum every address which gets added to the address book. And if its incorrectly checksummed it will not let you add that address. So I think we should go quite strict.
Some APIs only work with checksummed addresses including our tx service for instance.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we validate them when added to the AB, and in address inputs, validating also in the selector seems like an overkill. And it's computationally not the fastest.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've included the checksum validation. Reason being an unchecksummed address might be introduced during a development stage, the same way the corrupted AB with an undefined entry was.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And it's computationally not the fastest.

I didn't experience any degradation of performance but to be fair my AB is not extensively populated.

katspaugh marked this conversation as resolved.
Show resolved Hide resolved
export const validateAddress = (address: string) => {
const ADDRESS_RE = /^0x[0-9a-f]{40}$/i

if (!ADDRESS_RE.test(address)) {
return 'Invalid address format'
}
Expand Down