fix(wallets): normalize Gmail dots in signer locator to match backend#1946
Conversation
The backend strips dots from Gmail local parts at wallet creation (noniep.reggie@gmail.com -> noniepreggie@gmail.com), but the SDK constructed signer locators with the raw dotted email. This caused locator mismatches in isRecoverySigner() and signerIsRegistered(), blocking all outbound operations (send/swap/transfer) for Gmail users with dots in their email.
Original prompt from Robin
|
🤖 Devin AI EngineerI'll be helping with this pull request! Here's what you should know: ✅ I will automatically:
Note: I can only respond to comments from users who have write access to this repository. ⚙️ Control Options:
|
🦋 Changeset detectedLatest commit: 986a3e6 The changes in this PR will be included in the next version bump. This PR includes changesets to release 9 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
Prompt To Fix All With AIFix the following 2 code review issues. Work through them one at a time, proposing concise fixes.
---
### Issue 1 of 2
packages/wallets/src/utils/signer-locator.ts:11-26
**Duplicated normalization logic already exists in the same package**
`packages/wallets/src/utils/signer-validation.ts` already contains a private `normalizeEmail()` function that performs the exact same Gmail dot-stripping and `googlemail.com` → `gmail.com` substitution. This PR adds a second, functionally equivalent implementation (`normalizeEmailForLocator`) rather than extracting the shared logic. If the backend's normalization rules change (e.g., Gmail adds plus-addressing treatment or a new alias domain), only one copy will get updated, causing the locator comparison and the config-mismatch validator to diverge silently. The fix is to export `normalizeEmail` from `signer-validation.ts` and import it here.
### Issue 2 of 2
packages/wallets/src/utils/signer-locator.test.ts:42-45
**Lowercasing of non-Gmail domains is not tested**
`normalizeEmailForLocator` always calls `toLowerCase()` and returns `lower` for non-Gmail addresses — a silent behavior change from the original code that returned `signer.email` verbatim. For example, `"User@Company.Com"` now becomes `"email:user@company.com"` rather than `"email:User@Company.Com"`. The existing non-Gmail test only passes a pre-lowercased input, so this case is untested. A case like `{ email: "First.Last@Company.Com" }` → `"email:first.last@company.com"` should be added to confirm the behavior is intentional and matches what the backend stores.
Reviews (1): Last reviewed commit: "chore: add changeset for gmail dot norma..." | Re-trigger Greptile |
There was a problem hiding this comment.
Devin Review found 2 potential issues.
🐛 1 issue in files not directly in the diff
🐛 Email signer locator in buildInternalConfig not normalized, causing approval matching failures for Gmail addresses with dots (packages/wallets/src/signers/descriptors/email.ts:28)
The PR adds Gmail dot normalization to getSignerLocator (packages/wallets/src/utils/signer-locator.ts:22) but does not update the locator construction in emailSignerDescriptor.buildInternalConfig (packages/wallets/src/signers/descriptors/email.ts:28), which still uses the raw email: email:${emailConfig.email}. This creates a mismatch: getSignerLocator produces email:firstlast@gmail.com but the assembled signer's locator() method (via NonCustodialSigner at packages/wallets/src/signers/non-custodial/ncs-signer.ts:34-36) returns email:first.last@gmail.com. When approving transactions/signatures, wallet.ts:1165 and wallet.ts:1216 compare s.locator() against pendingApproval.signer.locator (which uses the backend-normalized form). For Gmail addresses with dots, this comparison fails and throws InvalidSignerError, making Gmail users with dots in their email unable to approve any transactions or signatures.
…InternalConfig - Export normalizeEmail from signer-validation.ts and import in signer-locator.ts instead of duplicating the logic. - Normalize the email locator in emailSignerDescriptor.buildInternalConfig via getSignerLocator so the NCS signer locator matches the backend form. - Add test for non-Gmail lowercasing behavior.
|
Re: Devin Review findings — both addressed in 986a3e6:
|
|
Reviews (2): Last reviewed commit: "fix(wallets): reuse shared normalizeEmai..." | Re-trigger Greptile |
Description
The backend's
normalizeEmail()strips dots from Gmail local parts at wallet creation time (e.g.,test.test@gmail.com→testtest@gmail.com), but the SDK'sgetSignerLocator()was constructing locators with the raw dotted email. This caused string-equality mismatches in three places duringuseSigner():isRecoverySigner()→matchesRecovery()→getSignerLocator(config) === getSignerLocator(recovery)— falsesignerIsRegistered()→ API lookup by locator — not foundisRecoverySigner()fallback — false againSigner "email:test.test@gmail.com" is not registered in this wallet.Impact: All outbound wallet operations (send, swap, transfer) were blocked for any Gmail user whose email contains dots. Inbound (receiving) was unaffected since it does not require signer auth.
Fix: Added
normalizeEmailForLocator()insigner-locator.tsthat mirrors the backend's normalization — strips dots from@gmail.com/@googlemail.comlocal parts and normalizesgooglemail.com→gmail.com. Applied to the email branch ofgetSignerLocator().References
Test plan
signer-locator.test.tscovering:first.last@gmail.com→firstlast@gmail.com)f.i.r.s.t@gmail.com→first@gmail.com)googlemail.com→gmail.comdomain normalizationFirst.Last@Gmail.com)wallet.test.tstests pass (including existing email signer "not registered" tests)Package updates
@crossmint/wallets-sdk: patch — changeset added via.changeset/fix-gmail-dot-normalization.mdCategory: improvements
Product Area: wallets
Link to Devin session: https://crossmint.devinenterprise.com/sessions/fd1d8361dff94f7a946fc9ec9414f19c
Requested by: @jcurbelo