-
Notifications
You must be signed in to change notification settings - Fork 363
Editable Prefixed Address Input EIP-3770 #2954
Editable Prefixed Address Input EIP-3770 #2954
Conversation
CLA Assistant Lite All Contributors have signed the CLA. |
ESLint Summary View Full Report
Report generated by eslint-plus-action |
|
E2E Tests Failed Failed tests:
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking good. Just a few points
name={name} | ||
inputProps={{ | ||
value: showNetworkPrefix ? prefixedAddress : value, | ||
onChange: (e) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You are using two onChange
events in this component. Can they be combined in one?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The onChange of the line 71 is related with the input, which contains the prefixed address value (rin:0x123...
format).
The other onChange is related with the value that is stored in the react-final-form state (address without prefix 0x123...
format).
It can not be combined because if we change the address formats in the state of all forms, we will need to update all the flows to adapt them to the new prefixed address format.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As per our call, you could theoretically remove the input onChange
and also reliance on the state by prepending the shortName
here:
inputProps={{
value: `${showPrefix ? getCurrentShortChainName() : ''}${values[name]}`
}}
@@ -142,6 +142,7 @@ function OwnersAndConfirmationsNewSafeStep(): ReactElement { | |||
createSafeForm.change(nameFieldName, addressName) | |||
} | |||
}} | |||
value={createSafeFormValues[addressFieldName]} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why are you passing a value here? Cannot all the logic of prepending the shortName
occur inside the component?
@@ -154,6 +155,7 @@ function LoadSafeAddressStep(): ReactElement { | |||
), | |||
} | |||
} | |||
value={loadSafeFormValues[FIELD_LOAD_SAFE_ADDRESS]} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here is another instance of passing the value manually.
@@ -105,6 +105,7 @@ export const CreateEditEntryModal = ({ | |||
fieldMutator={mutators.setOwnerAddress} | |||
name="address" | |||
placeholder="Address*" | |||
value={formState.values.address} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here is another instance of passing the value manually.
src/utils/addNetworkPrefix.ts
Outdated
|
||
// if no prefix is present in the provided address we return the prefixed address | ||
if (!prefix && address) { | ||
const prefixedAddress = `${netWorkPrefix}:${address}` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can use getPrefixedSafeAddressSlug()
for this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are you sure about this? it is specific for the Safe Addresses that are present in the Url. In that function Prefix argument is a enum SHORT_NAME
, but in the input users can type another values for the prefix.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do you want to allow invalid (currently unsupported in the Safe) shortNames
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is needed to trigger the network prefix validation in the form.
If the user types an address like: vt:0x57CB13cbef735FbDD65f5f2866638c546464E45F
in Rinkeby
we need to set this invalid value in the form state to make sure that the form is aware that a network prefix error is present in that address field.
return address | ||
} | ||
|
||
const prefix = getNetworkPrefix(address) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can use getCurrentShortChainName()
for this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
getCurrentShortChainName
always returns the shortName of the current selected network, but the address that the user types in the input could be could be incorrect.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also an oversight. Maybe it's good to use isValidShortChainName
to parse it before returning it.
@@ -127,6 +128,7 @@ export const OwnerForm = ({ onClose, onSubmit, initialValues }: OwnerFormProps): | |||
placeholder="Owner address*" | |||
testId={ADD_OWNER_ADDRESS_INPUT_TEST_ID} | |||
text="Owner address*" | |||
value={formState.values.ownerAddress} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here is another instance of passing the value manually.
@@ -155,6 +156,7 @@ export const OwnerForm = ({ onClose, onSubmit, owner, initialValues }: OwnerForm | |||
placeholder="Owner address*" | |||
testId={REPLACE_OWNER_ADDRESS_INPUT_TEST_ID} | |||
text="Owner address*" | |||
value={formState.values.ownerAddress} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here is another instance of passing the value manually.
inputAdornment={inputAdornment} | ||
name={name} | ||
inputProps={{ | ||
value: showNetworkPrefix ? prefixedAddress : value, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you not use an effect, with the user preference as a dependency to adjust the value on render/the preference changes? I'm not sure if it's the best idea to have two sources of truth. This would also remove the requirement of passing the value
prop.
setPrefixedAddress(addNetworkPrefix(checkedAddress, showNetworkPrefix, prefix)) | ||
} else { | ||
// this is needed to avoid remove the : char and no prefix is present | ||
const containsTwoDots = prefixedAddress.includes(':') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
containsTwoDots
-> isPrefixed
if (prefix) { | ||
setPrefixedAddress(addNetworkPrefix(checkedAddress, showNetworkPrefix, prefix)) | ||
} else { | ||
// this is needed to avoid remove the : char and no prefix is present |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you rephrase this? Not clear what you mean.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes! sorry for that!
I refactored it to be more clear:
// checksum the address in the input
checkedAddress = checksumAddress(address)
const prefix = getNetworkPrefix(prefixedAddress)
const isPrefixed = prefix || prefixedAddress.includes(':')
// we set the correct network prefix in the state
if (isPrefixed) {
setPrefixedAddress(addNetworkPrefix(checkedAddress, showNetworkPrefix, prefix))
} else {
setPrefixedAddress(addNetworkPrefix(checkedAddress, showNetworkPrefix, getCurrentShortChainName()))
}
src/components/forms/validator.ts
Outdated
|
||
const prefix = getNetworkPrefix(value) | ||
|
||
const isInValidPrefix = prefix && prefix !== extractShortChainName() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
isInvalidPrefix
(invalid is one word)
src/components/forms/validator.ts
Outdated
@@ -91,6 +93,20 @@ export const mustBeEthereumAddress = memoize((address: string): ValidatorReturnT | |||
return result | |||
}) | |||
|
|||
export const checkNetworkPrefix = (value: string): ValidatorReturnType => { | |||
const errorMessage = "The current network doesn't match the given address" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we have a separate error message for prefixes that aren't valid shortNames?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done!
I added the Unsupported network prefix
error label in the validator:
const errorMessage = isValidShortChainName(prefix)
? "The current network doesn't match the given address"
: 'Unsupported network prefix'
And also added a unit test to this new case:
it('Validates unsupported shortNames', () => {
const unsupportedPrefixedAddress = 'xxxx:0x2D42232C03C12f1dC1448f89dcE33d2d5A47Aa33'
renderAddressInputWithinForm()
// invalid address network prefix
fireEvent.change(screen.getByTestId(fieldTestId), { target: { value: unsupportedPrefixedAddress } })
// input value with the network prefix
expect((screen.getByTestId(fieldTestId) as HTMLInputElement).value).toBe(unsupportedPrefixedAddress)
// show prefix error
expect(screen.queryByText('Unsupported network prefix')).toBeInTheDocument()
})
src/utils/getNetworkPrefix.ts
Outdated
@@ -0,0 +1,8 @@ | |||
function getNetworkPrefix(address = ''): string { | |||
const splittedAddress = address.split(':') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
const splittedAddress = address.split(':') | |
const splitAddress = address.split(':') |
("splitted" isn't a word)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done!
Awesome PR! Left some minor comments. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you 👍
In every input it was tested that the address with our without the index was a valid one. Was also tested the QR code and ENS names. Sections checked: Looks Good to me Question: |
There's one behavior that I noticed and think is confusing:
I think we shouldn't remove prefixes if the user inputted them intentionally. Edit: definitely not a blocker though! We can iterate on that later. |
…address-input-editable
ESLint Summary View Full Report
Report generated by eslint-plus-action |
What it solves
Resolves #2756
How this PR fixes it
Added network prefix validations in
AddressInput
andAddressBookInput
components.Added unit tests to
AddressInput
andAddressBookInput
components, andgetAddressWithoutNetworkPrefix
andgetNetworkPrefix
util functionsHow to test it
Places where the Address input appears:
Use cases
Screenshots