Skip to content
This repository has been archived by the owner on Nov 10, 2023. It is now read-only.

Editable Prefixed Address Input EIP-3770 #2954

Conversation

DaniSomoza
Copy link
Contributor

@DaniSomoza DaniSomoza commented Nov 3, 2021

What it solves

Resolves #2756

How this PR fixes it

Added network prefix validations in AddressInput and AddressBookInput components.

Added unit tests to AddressInput and AddressBookInput components, and getAddressWithoutNetworkPrefix and getNetworkPrefix util functions

How to test it

Places where the Address input appears:

  1. Load Safe Form (Step two, in the load safe address input).
  2. Create Safe Form (Step three, Owners and Confirmations, in the Owners address inputs).
  3. Settings > Owners section, Add & Replace owners Dialogs, in the Owners address inputs.
  4. AddressBook Section > Create Entry Dialog
  5. New Transaction > Send Founds Dialog, Recipient input.
  6. New Transaction > Send collectible Dialog, Recipient input.
  7. New Transaction > Contract Interaction, Contract Address input.

Use cases

  1. Use a valid network prefix in a address input (rin:0xC245cb45B044d66fbE8Fb33C26c0b28B4fc367B2)
  2. Network prefixes that don't match with the current network show an error ("The current network doesn't match the given address")
  3. Invalid network prefix shows an error ("Unsupported network prefix")
  4. Checksum address with a valid network prefix (rin:0X9913B9180C20C6B0F21B6480C84422F6EBC4B808 -> rin:0x9913B9180C20C6b0F21B6480c84422F6ebc4B808)
  5. Load an address with a QR code should work.
  6. Load an address with ENS name should work.
  7. All the validations like "duplicated owner address" should work with prefixed address

Screenshots

Captura de pantalla 2021-11-03 a las 10 37 22
Captura de pantalla 2021-11-04 a las 12 42 10
Captura de pantalla 2021-11-04 a las 12 42 32
Captura de pantalla 2021-11-04 a las 12 42 51
Captura de pantalla 2021-11-04 a las 12 45 01
Captura de pantalla 2021-11-04 a las 12 46 03
Captura de pantalla 2021-11-04 a las 13 16 49

@github-actions
Copy link

github-actions bot commented Nov 3, 2021

CLA Assistant Lite All Contributors have signed the CLA.

@github-actions
Copy link

github-actions bot commented Nov 3, 2021

ESLint Summary View Full Report

Annotations are provided inline on the Files Changed tab. You can also see all annotations that were generated on the annotations page.

Type Occurrences Fixable
Errors 0 0
Warnings 0 0
Ignored 8 N/A
  • Result: ✅ success
  • Annotations: 0 total

Report generated by eslint-plus-action

@github-actions
Copy link

github-actions bot commented Nov 3, 2021

@coveralls
Copy link

coveralls commented Nov 3, 2021

Pull Request Test Coverage Report for Build 1438938297

  • 47 of 50 (94.0%) changed or added relevant lines in 9 files are covered.
  • 12 unchanged lines in 5 files lost coverage.
  • Overall coverage increased (+1.06%) to 32.621%

Changes Missing Coverage Covered Lines Changed/Added Lines %
src/routes/safe/components/Balances/SendModal/screens/AddressBookInput/index.tsx 10 11 90.91%
src/routes/safe/components/Settings/ManageOwners/AddOwnerModal/screens/OwnerForm/index.tsx 0 1 0.0%
src/routes/safe/components/Settings/ManageOwners/ReplaceOwnerModal/screens/OwnerForm/index.tsx 0 1 0.0%
Files with Coverage Reduction New Missed Lines %
src/routes/safe/components/Balances/SendModal/screens/AddressBookInput/index.tsx 1 66.67%
src/routes/safe/components/Settings/ManageOwners/AddOwnerModal/screens/OwnerForm/index.tsx 1 0%
src/routes/safe/components/Settings/ManageOwners/ReplaceOwnerModal/screens/OwnerForm/index.tsx 1 0%
src/routes/safe/components/Apps/components/ConfirmTxModal/index.tsx 2 80.56%
src/routes/safe/components/Apps/components/ConfirmTxModal/ReviewConfirm.tsx 7 64.84%
Totals Coverage Status
Change from base Build 1435451809: 1.06%
Covered Lines: 3072
Relevant Lines: 8447

💛 - Coveralls

@github-actions
Copy link

github-actions bot commented Nov 3, 2021

E2E Tests Failed
Check the results here: https://github.com/gnosis/safe-react-e2e-tests/actions/runs/1438971088

Failed tests:

  • ❌ Add an existing safe Add an existing safe
  • ❌ Address book Address book
  • ❌ Safe Apps List Safe Apps List
  • ❌ Safe Balances Safe Balances

@DaniSomoza DaniSomoza marked this pull request as ready for review November 4, 2021 11:59
Copy link
Member

@iamacook iamacook left a 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) => {
Copy link
Member

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?

Copy link
Contributor Author

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.

Copy link
Member

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]}
Copy link
Member

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]}
Copy link
Member

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}
Copy link
Member

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.


// if no prefix is present in the provided address we return the prefixed address
if (!prefix && address) {
const prefixedAddress = `${netWorkPrefix}:${address}`
Copy link
Member

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.

Copy link
Contributor Author

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.

Copy link
Member

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?

Copy link
Contributor Author

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.
Captura de pantalla 2021-11-08 a las 15 46 17

src/utils/getAddressWithoutNetworkPrefix.ts Show resolved Hide resolved
return address
}

const prefix = getNetworkPrefix(address)
Copy link
Member

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.

Copy link
Contributor Author

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.

Copy link
Member

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}
Copy link
Member

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}
Copy link
Member

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,
Copy link
Member

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(':')
Copy link
Member

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
Copy link
Member

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.

Copy link
Contributor Author

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()))
                }


const prefix = getNetworkPrefix(value)

const isInValidPrefix = prefix && prefix !== extractShortChainName()
Copy link
Member

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)

@@ -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"
Copy link
Member

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?

Copy link
Contributor Author

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()
    })

@@ -0,0 +1,8 @@
function getNetworkPrefix(address = ''): string {
const splittedAddress = address.split(':')
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
const splittedAddress = address.split(':')
const splitAddress = address.split(':')

("splitted" isn't a word)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done!

@katspaugh
Copy link
Member

Awesome PR! Left some minor comments.

Copy link
Member

@katspaugh katspaugh left a comment

Choose a reason for hiding this comment

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

Thank you 👍

@francovenica
Copy link
Contributor

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.
Lastly it was checked that only the prefix of the currently selected network works (so xdai: cannot work on rinkeby and viceversa)

Sections checked:
Safe creation > Owners
Add existing safe
Send funds
Send collectibles
Contract interaction
Address book add entry
Owner settings Add owner
Owner settings Replace owner
Spending Limit Add Allowance

Looks Good to me

Question:
In the Contract Interaction > Receiver input the address set doesn't show the prefix once you enter a valid address. I've seen this work fine in other inputs like the one in "Add existing safe" and Address book Add entry. So the issue is only how the address shows, but it works fine regarding if the input has the prefix or not. So, it should there the prefix for a valid address or not?
Contract interaction snapshot:
image
Add existing safe snapshot:
image

@katspaugh
Copy link
Member

katspaugh commented Nov 15, 2021

There's one behavior that I noticed and think is confusing:

  • Disable prefixes in Settings -> Appearance
  • Go to Address Book -> Create Entry
  • Enter rin:0x4f9BD57BCC68Bf7770429F137922B3afD23d83E7
  • It will remove the rin: part

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.

@DiogoSoaress DiogoSoaress changed the base branch from dev to feature/eip-3770-prefixes November 16, 2021 08:43
@github-actions
Copy link

ESLint Summary View Full Report

Annotations are provided inline on the Files Changed tab. You can also see all annotations that were generated on the annotations page.

Type Occurrences Fixable
Errors 0 0
Warnings 0 0
Ignored 8 N/A
  • Result: ✅ success
  • Annotations: 0 total

Report generated by eslint-plus-action

@DiogoSoaress DiogoSoaress merged commit 2b92443 into feature/eip-3770-prefixes Nov 16, 2021
@DiogoSoaress DiogoSoaress deleted the feature/chain-specific-address-input-editable branch November 16, 2021 22:17
@github-actions github-actions bot locked and limited conversation to collaborators Nov 16, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Allow EIP-3770 chain-specific address input
6 participants