Skip to content

Release: develop -> main#3771

Merged
davidleomay merged 6 commits into
mainfrom
develop
May 27, 2026
Merged

Release: develop -> main#3771
davidleomay merged 6 commits into
mainfrom
develop

Conversation

@github-actions
Copy link
Copy Markdown

Automatic Release PR

This PR was automatically created after changes were pushed to develop.

Commits: 1 new commit(s)

Checklist

  • Review all changes
  • Verify CI passes
  • Approve and merge when ready for production

All hummingbot Azure container instances have been stopped (dev: 4, prd: 3).
Updated README to reflect current state.
Blume1977 and others added 5 commits May 26, 2026 23:27
…aracter set (#3766)

* fix(user-data): preserve UTF-8 umlauts in name and address fields

Names and addresses entered via KYC personal-data, name-change, and
address-change flows were ASCII-transliterated before persistence
(Rüttimann -> Ruttimann, Münchwilen -> Munchwilen), causing PDFs and
mails to render without umlauts.

The user_data columns are NVARCHAR/character-varying since 2022 and
support UTF-8 natively. The transliteration in UserDataService was
introduced in #1910 and copied into #3373 and #3376 without a documented
external constraint requiring ASCII storage. Comparison call sites
(Util.isSameName / Util.removeSpecialChars) already normalise diacritics
at compare time, and the realunit BitBox path uses its own
toBitboxAscii helper on-the-fly. Storing the originals preserves
information for downstream consumers (PDF, mail, support) at no cost to
the matching pipelines.

Realunit isPersonalDataMatching now transliterates both sides so legacy
rows (still ASCII in the DB) continue to match alongside new UTF-8 rows.

* feat(user-data): restrict personal fields to Swiss payment character set

Add @IsSwissPaymentText() validator on every user-supplied name and
address field that lands in user_data. Inputs containing characters
outside the SIX SIG IG QR-Bill v2.3 permitted character set (printable
ASCII plus the Latin diacritics for the four Swiss national languages)
are rejected with 400 BadRequestException.

Pairs with the umlaut-preservation fix: now that diacritics survive,
we also prevent CJK/Cyrillic/Arabic/emoji input from reaching the DB,
where Helvetica/WinAnsi PDF rendering would produce empty .notdef
boxes. Validation is restricted to the user-facing input layer
(KycPersonalData, KycChangeNameData, KycAddress, UserNameDto); KYC
ident-result imports from SumSub/IDnow are unchanged because those
providers already deliver Latin names.

Validator follows the existing IsDfxPhone pattern (regex held in
Config.formats.swissPaymentText, accessed at validate-time) to avoid
the let-Config initialisation order issue at decorator evaluation.
Existing rows are not re-validated.
…#3772)

* feat(user): add createSupportTicket capability with prerequisite hint

Narrow follow-up after #3767 was closed. That PR proposed a structured
ActionCapability tree (170 LOC, 4 new DTOs, HttpMethod enum); @davidleomay
correctly pointed out the endpoint paths are static and belong in Swagger
docs, not on every /v2/user response. Pure-error pattern (his alternative
suggestion) doesn't fit the actual UX requirement: the realunit-app needs
to gate the support tile's tap *before* opening the ticket form, so the
user is routed to the email capture step first — a post-submit 400 means
the user fills the form for nothing.

The middle ground here:

- `UserCapabilitiesDto.createSupportTicket: { available, missingPrerequisite? }`
  — per-user runtime info only. The available flag and the missing-
  prerequisite discriminator are derived server-side from `userData.mail`.
  No endpoint paths in the response — those stay in Swagger.
- `MissingPrerequisite` is a closed enum (`Email`, `Phone`). The app maps
  the value to the matching capture flow; the backend owns *which*
  prerequisite blocks the action, the app owns *how* the prerequisite is
  rendered. That's where API-as-Decision-Authority draws the line.
- Mapper returns a discriminated union (`{ available: true } | { available:
  false; missingPrerequisite }`) so the compiler pins `!available implies
  missingPrerequisite defined`. The DTO class stays for Swagger schema
  generation (NestJS decorators don't model TS unions cleanly).
- `@ApiBadRequestResponse` decorator added on `POST /v1/support/issue`
  per @davidleomay's review — Swagger docs now name the email-prerequisite
  remediation path. Description deliberately not narrowed to the email
  case so other 400 causes (DTO validation, etc.) remain covered.

Backwards-compatible: `createSupportTicket?` is optional on the schema
(`@ApiPropertyOptional`) so client SDKs pinned to older API versions
ignore the new field. Other UserCapabilities flags untouched.

## Tests

`user-dto.mapper.spec.ts` gains 4 fixtures: mail present, mail null, mail
empty string, mail undefined. The empty-string and undefined cases pin
the JS-truthy semantics so a future drift in `support-issue.service`'s
gate is caught.

- type-check: clean
- lint: clean
- prettier: clean
- jest: 944 / 944 passing (940 baseline + 4 new tests)

* refactor: drop unused MissingPrerequisite.PHONE

YAGNI: the mapper only emits 'Email'. Add 'Phone' (or any other
prerequisite) when a backend gate actually requires it — the enum
extension would be additive and backwards-compatible.

Addresses @davidleomay's review comment on PR #3772.
Synthesised from the #3733#3761#3767#3772 review sequence with
@davidleomay. New section under "API Design" makes the eight rules
binding for every future capability flag:

1. Heterogeneous capabilities — bool for hide-able, struct for discoverable
2. Static info belongs in Swagger, not in /user response
3. YAGNI for enum members and optional fields
4. Discriminated union for compiler-enforced invariants
5. Pre-tap signal for discoverable actions
6. Pair-PR with a documented trade-off
7. Backend owns business rules; client only maps types to UI
8. Reduction before extension — test the UX requirement first

Cross-references the consumer-side mirror in DFXswiss/realunit-app's
CONTRIBUTING.md and the api-authority-plan.md roll-out document.

Pure documentation — no code changes.
Call `store` on the Monero wallet RPC after each `createAddress` call
to flush subaddresses to disk. Without this, subaddresses created via
RPC can be lost on wallet container restart, silently dropping deposits.
… PSQL migration (#3776)

The PSQL migration prefixed bare column references with the query builder
alias but did not add an explicit select alias. PostgreSQL getRawMany
returns keys as alias_column (e.g. buyCrypto_usedRef) without one,
so the ref array was always [undefined] and no user ref volume was updated.
@davidleomay davidleomay merged commit 8ebc5e3 into main May 27, 2026
11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants