Skip to content

Release: develop -> main#3730

Merged
TaprootFreak merged 2 commits into
mainfrom
develop
May 21, 2026
Merged

Release: develop -> main#3730
TaprootFreak merged 2 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

)

* fix(realunit): make register/wallet idempotent on signature match

`completeRegistrationForWalletAddress` (and the same-wallet branch of
`completeRegistration`) threw `BadRequestException("RealUnit registration
already exists for this wallet")` whenever a KycStep already existed for
the current wallet. That breaks legitimate client retries after a lost
response: the first call succeeds server-side, the network drops the
reply, the client re-sends with the same EIP-712 signature and gets a
hard 400 — surfaced in the realunit-app KYC merge flow (RealUnitCH/app#466)
as a user-stuck failure with no recovery path.

Replace the hard throw with a signature-aware idempotency check:
- Same wallet + same signature → return the existing registration's
  status (`COMPLETED` if the KycStep is completed, otherwise
  `FORWARDING_FAILED`). No new KycStep, no re-forward to Aktionariat.
- Same wallet + different signature → keep the 400, but with the more
  precise message "RealUnit registration already exists for this wallet
  with a different signature". A fresh sign over the same wallet means
  the client produced a new payload — that is a real conflict, not a
  retry.

Tests cover all three branches and assert that `createCustomKycStep` is
never called on the idempotent paths.

* refactor(realunit): destructure findRegistrationStep result and log idempotent retries

- Use destructuring at both `findRegistrationStep` call sites so the
  register/complete and register/wallet paths read identically.
- Pass `userData` explicitly into `idempotentRegistrationResult` rather
  than relying on TypeORM's back-population of `step.userData`, which is
  not guaranteed for steps loaded via the `userData.kycSteps` relation.
- Emit an info log when the idempotent branch fires so production
  retry frequency is observable and stuck-user reports can be correlated
  to a kycStep id.
- Document the three KycStep statuses REALUNIT_REGISTRATION actually
  reaches (INTERNAL_REVIEW / MANUAL_REVIEW / COMPLETED) so the binary
  `isCompleted` mapping does not silently break if the lifecycle is
  ever extended.

* docs(realunit): broaden idempotent-status comment to cover admin overrides

The previous comment claimed the step is in one of three states
(INTERNAL_REVIEW / MANUAL_REVIEW / COMPLETED). That is true under the
normal service flow, but the generic `kyc-admin.updateKycStep` endpoint
can push REALUNIT_REGISTRATION into any non-failed/non-canceled status
(ON_HOLD, OUTDATED, etc.) — and `findRegistrationStep` only filters
FAILED and CANCELED. Runtime behavior is unchanged (anything that is not
`isCompleted` maps to FORWARDING_FAILED, which is the safe default), but
the comment now accurately reflects the reachable state set.

* fix(realunit): match idempotency signatures case-insensitively

EIP-712 signatures are 0x-prefixed hex strings. Lower-case and
upper-case representations of the same signature are semantically
identical, but the previous strict `!==` would have treated them as
different and rejected legitimate retries that happen to flip hex case
(can occur when a wallet library updates its hex-encoding default).

Use `Util.equalsIgnoreCase` for the comparison, matching the convention
already used for wallet-address comparisons in `findRegistrationStep`.
Added a unit test that stores an upper-case signature and retries with
a lower-case one — it now returns COMPLETED instead of throwing 400.
@TaprootFreak TaprootFreak merged commit 381c17f into main May 21, 2026
10 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.

2 participants