Skip to content

fix: standardize SelfAppDisclosureConfig to all snake_case - #1910

Open
Tranquil-Flow wants to merge 7 commits into
stagingfrom
self-1194
Open

fix: standardize SelfAppDisclosureConfig to all snake_case#1910
Tranquil-Flow wants to merge 7 commits into
stagingfrom
self-1194

Conversation

@Tranquil-Flow

@Tranquil-Flow Tranquil-Flow commented Apr 2, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Renames excludedCountriesexcluded_countries and minimumAgeminimum_age in SelfAppDisclosureConfig across all four type definition files (common, new-common, sdk-common, qrcode-angular)
  • Updates all TypeScript consumers: registerInputs.ts, disclosureUtils.ts, selfAppContext.ts, ProofHistoryDetailScreen.tsx
  • Adds normalizeDisclosureConfig() in common/src/utils/appType.ts for backward compatibility with old QR/deep-link payloads and stored proof history that still use camelCase keys
  • Updates SDK READMEs to document the new field names

Notes

The native shell wire format (minimumAge: prefix in the disclosures URL param, and ?excludedCountries= URL param) is intentionally unchanged — this is an internal transport layer. Only the TypeScript type fields and their consumers are renamed.

Closes SELF-1194

Summary by CodeRabbit

  • Bug Fixes

    • Improved cryptographic randomness generation in KYC utilities for enhanced security
    • Fixed inconsistent error handling in identity verification to always return defined values on invalid inputs
  • Chores

    • Standardized internal disclosure configuration format for consistency across the codebase
    • Added automatic conversion layer to maintain backward compatibility with legacy configuration formats
    • Updated repository ignore patterns to exclude build artifacts

@vercel

vercel Bot commented Apr 2, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
self-webview-app Ignored Ignored Preview Apr 16, 2026 7:08am

Request Review

@coderabbitai

coderabbitai Bot commented Apr 2, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Review was skipped due to path filters

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock

CodeRabbit blocks several paths by default. You can override this behavior by explicitly including those paths in the path filters. For example, including **/dist/** will override the default block on the dist directory, by removing the pattern from both the lists.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 22b6810d-69d8-48d4-a510-cad78bc98c00

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

This pull request standardizes disclosure configuration key names from camelCase to snake_case across the codebase (excludedCountriesexcluded_countries, minimumAgeminimum_age). Normalization utilities are introduced to maintain backwards compatibility by converting legacy camelCase keys to the new format. Additional minor updates include improving random number generation in KYC utilities and adjusting error-handling behavior in tree utilities.

Changes

Cohort / File(s) Summary
Disclosure Key Migration
common/src/utils/appType.ts, new-common/src/foundation/types/app.ts, sdk/qrcode-angular/src/lib/common.ts
Updated SelfAppDisclosureConfig interface to use snake_case field names and added normalizeDisclosureConfig function for backwards-compatible conversion of legacy camelCase keys.
Disclosure Normalization Implementation
sdk/sdk-common/index.ts, new-common/src/app/builder.ts
Implemented normalizeDisclosures helper functions in SelfAppBuilder constructors to convert camelCase disclosure keys to snake_case at object initialization time.
Disclosure Key Consumption Updates
app/src/utils/disclosureUtils.ts, common/src/utils/circuits/registerInputs.ts, packages/webview-app/src/utils/selfAppContext.ts
Updated references to disclosure fields throughout utilities and circuit input generation to use snake_case key names.
Disclosure Handling in UI Components
app/src/navigation/deeplinks.ts, app/src/screens/home/ProofHistoryDetailScreen.tsx, app/src/screens/home/ProofHistoryList.tsx, app/src/screens/home/ProofHistoryScreen.tsx
Added normalizeDisclosureConfig calls when parsing disclosure configurations in navigation and UI rendering logic.
Repository Configuration
.gitignore
Added .worktrees/ to ignore patterns.
Utility Functions
common/src/utils/kyc/ecdsa/utils.ts, common/src/utils/trees.ts
Updated generateRandomsg to use cryptographic random source; modified getPassportNumberAndNationalityLeaf to return BigInt(0) on error paths instead of undefined.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 8.89% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely summarizes the main change: standardizing SelfAppDisclosureConfig fields to snake_case, which is the primary objective of this PR.
Description check ✅ Passed The description provides a clear summary of changes, explains the backward-compatibility approach with normalizeDisclosureConfig(), notes the intentional wire-format invariance, and references the closed issue.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch self-1194

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 13

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
packages/native-shell-ios/Sources/SelfNativeShell/Handlers/LifecycleHandler.swift (1)

33-38: ⚠️ Potential issue | 🟠 Major

Verify the setResult payload contract before shipping this.

Line 34 changes onResult from receiving params["result"] to receiving the entire params map. If the bridge still sends { result: ... }, iOS callbacks will now receive a differently shaped payload and existing consumers will parse the wrong object.

Run this read-only check to confirm the web bridge and both native handlers agree on the same payload shape:

#!/bin/bash
set -euo pipefail

printf '\n# JS/TS call sites for lifecycle.setResult\n'
rg -n -C3 '\bsetResult\s*\(' --type=ts --type=tsx --type=js --type=jsx

printf '\n# Places where "setResult" payloads are constructed\n'
rg -n -C4 '"setResult"|'\''setResult'\''' packages --type=ts --type=tsx --type=js --type=jsx

printf '\n# Native handlers on both platforms\n'
rg -n -C4 'setResult' packages/native-shell-ios packages/native-shell-android

Expected result: all producers and consumers use the same contract—either the raw result object or a { result: ... } envelope—but not a mix of both.

common/src/utils/trees.ts (1)

856-858: ⚠️ Potential issue | 🟠 Major

poseidon4 will throw if year is not exactly 4 characters.

processYearKyc passes year directly to poseidon4, which requires exactly 4 inputs. Called from getNameYobLeafKyc (line 772) with no length validation, this will crash if the caller passes a 2-digit year or any non-4-char string.

Proposed fix
 const processYearKyc = (year: string, i: number): bigint => {
+  if (year.length !== 4) {
+    console.error('Invalid year length for processYearKyc:', i, year);
+    return BigInt(0);
+  }
   const yearArr = stringToAsciiBigIntArray(year);
   return BigInt(poseidon4(yearArr));
 };

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: a7f9be31-c0ef-4551-9f6d-65da613b19d6

📥 Commits

Reviewing files that changed from the base of the PR and between 75ac0df and e487b69.

⛔ Files ignored due to path filters (50)
  • packages/native-shell-android/src/main/assets/self-wallet/assets/booklet-moire-left-Bh6MkDzP.png is excluded by !**/*.png
  • packages/native-shell-android/src/main/assets/self-wallet/assets/booklet-moire-right-CY2PQJOz.png is excluded by !**/*.png
  • packages/native-shell-android/src/main/assets/self-wallet/assets/index-YX6AnLbA.js.map is excluded by !**/*.map
  • packages/native-shell-android/src/main/assets/self-wallet/assets/index.es-CVNUZmbC.js.map is excluded by !**/*.map
  • packages/native-shell-android/src/main/assets/self-wallet/backgrounds/dialogue-background-proof-result.jpg is excluded by !**/*.jpg
  • packages/native-shell-android/src/main/assets/self-wallet/backgrounds/dialogue-background-simple.jpg is excluded by !**/*.jpg
  • packages/native-shell-android/src/main/assets/self-wallet/backgrounds/dialogue-background.jpg is excluded by !**/*.jpg
  • packages/native-shell-android/src/main/assets/self-wallet/backgrounds/dialogue-background.png is excluded by !**/*.png
  • packages/native-shell-android/src/main/assets/self-wallet/backgrounds/restore.png is excluded by !**/*.png
  • packages/native-shell-android/src/main/assets/self-wallet/logos/self.svg is excluded by !**/*.svg
  • packages/native-shell-ios/Resources/self-sdk-web/assets/aadhaar-registration-background-CyX4r5V8.png is excluded by !**/*.png
  • packages/native-shell-ios/Resources/self-sdk-web/assets/affirmative-BixXd3iG.wav is excluded by !**/*.wav
  • packages/native-shell-ios/Resources/self-sdk-web/assets/booklet-moire-left-Bh6MkDzP.png is excluded by !**/*.png
  • packages/native-shell-ios/Resources/self-sdk-web/assets/booklet-moire-right-CY2PQJOz.png is excluded by !**/*.png
  • packages/native-shell-ios/Resources/self-sdk-web/assets/dev-mode-B7OFUXG_.png is excluded by !**/*.png
  • packages/native-shell-ios/Resources/self-sdk-web/assets/didit-sdk.esm-D5Sol1U1.js.map is excluded by !**/*.map
  • packages/native-shell-ios/Resources/self-sdk-web/assets/eu-id-guilloche-DpaaokAE.png is excluded by !**/*.png
  • packages/native-shell-ios/Resources/self-sdk-web/assets/eu-id-portrait-v2-Dk8VGCuw.png is excluded by !**/*.png
  • packages/native-shell-ios/Resources/self-sdk-web/assets/eu-id-selfie-rqiwWB-T.png is excluded by !**/*.png
  • packages/native-shell-ios/Resources/self-sdk-web/assets/human-bg-1-BqIVu7Uh.png is excluded by !**/*.png
  • packages/native-shell-ios/Resources/self-sdk-web/assets/human-bg-2-xfWeRop8.png is excluded by !**/*.png
  • packages/native-shell-ios/Resources/self-sdk-web/assets/human-bg-3-BYL8eCsL.png is excluded by !**/*.png
  • packages/native-shell-ios/Resources/self-sdk-web/assets/human-bg-4-MuEVVNxA.png is excluded by !**/*.png
  • packages/native-shell-ios/Resources/self-sdk-web/assets/human-bg-6-DvIg7_wA.png is excluded by !**/*.png
  • packages/native-shell-ios/Resources/self-sdk-web/assets/human-bg-7-DCXaflzB.png is excluded by !**/*.png
  • packages/native-shell-ios/Resources/self-sdk-web/assets/human-bg-8-CAet-elZ.png is excluded by !**/*.png
  • packages/native-shell-ios/Resources/self-sdk-web/assets/human-bg-9-rwM-BoKb.png is excluded by !**/*.png
  • packages/native-shell-ios/Resources/self-sdk-web/assets/index-YX6AnLbA.js.map is excluded by !**/*.map
  • packages/native-shell-ios/Resources/self-sdk-web/assets/index.es-CVNUZmbC.js.map is excluded by !**/*.map
  • packages/native-shell-ios/Resources/self-sdk-web/assets/moire-light-OZs3YXh_.svg is excluded by !**/*.svg
  • packages/native-shell-ios/Resources/self-sdk-web/assets/negative-B7ZRPokk.wav is excluded by !**/*.wav
  • packages/native-shell-ios/Resources/self-sdk-web/assets/nfc-tap-phone-1-CKYQoX9l.png is excluded by !**/*.png
  • packages/native-shell-ios/Resources/self-sdk-web/assets/passport-front-cover-f0fc8EoB.png is excluded by !**/*.png
  • packages/native-shell-ios/Resources/self-sdk-web/assets/passport-inside-left-CLt089eW.png is excluded by !**/*.png
  • packages/native-shell-ios/Resources/self-sdk-web/assets/passport-inside-right-CR1oMMID.png is excluded by !**/*.png
  • packages/native-shell-ios/Resources/self-sdk-web/assets/passport-open-picture-page-DO1lGFjA.png is excluded by !**/*.png
  • packages/native-shell-ios/Resources/self-sdk-web/assets/progress-DQ3165CA.wav is excluded by !**/*.wav
  • packages/native-shell-ios/Resources/self-sdk-web/assets/proof-fail-BwRY0Xme.png is excluded by !**/*.png
  • packages/native-shell-ios/Resources/self-sdk-web/assets/utilize-CDvw9gnR.wav is excluded by !**/*.wav
  • packages/native-shell-ios/Resources/self-sdk-web/backgrounds/dialogue-background-proof-result.jpg is excluded by !**/*.jpg
  • packages/native-shell-ios/Resources/self-sdk-web/backgrounds/dialogue-background-simple.jpg is excluded by !**/*.jpg
  • packages/native-shell-ios/Resources/self-sdk-web/backgrounds/dialogue-background.jpg is excluded by !**/*.jpg
  • packages/native-shell-ios/Resources/self-sdk-web/backgrounds/dialogue-background.png is excluded by !**/*.png
  • packages/native-shell-ios/Resources/self-sdk-web/backgrounds/restore.png is excluded by !**/*.png
  • packages/native-shell-ios/Resources/self-sdk-web/fonts/Advercase-Regular.otf is excluded by !**/*.otf
  • packages/native-shell-ios/Resources/self-sdk-web/fonts/DINOT-Bold.otf is excluded by !**/*.otf
  • packages/native-shell-ios/Resources/self-sdk-web/fonts/DINOT-Medium.otf is excluded by !**/*.otf
  • packages/native-shell-ios/Resources/self-sdk-web/fonts/IBMPlexMono-Regular.otf is excluded by !**/*.otf
  • packages/native-shell-ios/Resources/self-sdk-web/logos/self.svg is excluded by !**/*.svg
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (135)
  • .claude/skills/self-skills
  • .github/workflows/contracts.yml
  • .github/workflows/mobile-e2e.yml
  • .github/workflows/rn-sdk-test-app-ci.yml
  • .gitignore
  • app/.eslintrc.cjs
  • app/src/navigation/deeplinks.ts
  • app/src/screens/home/ProofHistoryDetailScreen.tsx
  • app/src/utils/disclosureUtils.ts
  • common/index.ts
  • common/src/constants/constants.ts
  • common/src/constants/index.ts
  • common/src/utils/appType.ts
  • common/src/utils/circuits/registerInputs.ts
  • common/src/utils/kyc/constants.ts
  • common/src/utils/kyc/ecdsa/ecdsa.ts
  • common/src/utils/kyc/ecdsa/utils.ts
  • common/src/utils/kyc/generateInputs.ts
  • common/src/utils/kyc/types.ts
  • common/src/utils/trees.ts
  • contracts/ignition/deployments/chain-42220/deployed_addresses.json
  • contracts/ignition/modules/upgrade/upgradeKycRegistry.ts
  • docs/reviews/PR-1901-review-findings.md
  • new-common/src/circuits/inputs/disclose-kyc.ts
  • new-common/src/foundation/types/app.ts
  • packages/mobile-sdk-alpha/.eslintrc.cjs
  • packages/mobile-sdk-alpha/src/browser.ts
  • packages/mobile-sdk-alpha/src/documents/utils.ts
  • packages/mobile-sdk-alpha/src/index.ts
  • packages/mobile-sdk-alpha/src/proving/internal/statusHandlers.ts
  • packages/mobile-sdk-alpha/src/proving/provingMachine.ts
  • packages/mobile-sdk-alpha/src/proving/recoveryValidation.ts
  • packages/mobile-sdk-alpha/tests/proving/internal/statusHandlers.test.ts
  • packages/mobile-sdk-alpha/tests/proving/recoveryValidation.test.ts
  • packages/mobile-sdk-alpha/tsconfig.eslint.json
  • packages/native-shell-android/build.gradle.kts
  • packages/native-shell-android/src/main/assets/self-wallet/animations/app-tour-generate.json
  • packages/native-shell-android/src/main/assets/self-wallet/animations/app-tour-get-started.json
  • packages/native-shell-android/src/main/assets/self-wallet/animations/app-tour-proof.json
  • packages/native-shell-android/src/main/assets/self-wallet/animations/app-tour-welcome.json
  • packages/native-shell-android/src/main/assets/self-wallet/animations/cloud-backup.json
  • packages/native-shell-android/src/main/assets/self-wallet/animations/proof-progress.json
  • packages/native-shell-android/src/main/assets/self-wallet/animations/proof-success-check.json
  • packages/native-shell-android/src/main/assets/self-wallet/animations/proof-success.json
  • packages/native-shell-android/src/main/assets/self-wallet/animations/push-notification-prompt.json
  • packages/native-shell-android/src/main/assets/self-wallet/animations/scan-bar.json
  • packages/native-shell-android/src/main/assets/self-wallet/assets/index-LqDWjDzu.css
  • packages/native-shell-android/src/main/assets/self-wallet/assets/index-YX6AnLbA.js
  • packages/native-shell-android/src/main/assets/self-wallet/assets/index.es-CVNUZmbC.js
  • packages/native-shell-android/src/main/kotlin/xyz/self/sdk/api/SecureStorageProvider.kt
  • packages/native-shell-android/src/main/kotlin/xyz/self/sdk/api/SelfSdk.kt
  • packages/native-shell-android/src/main/kotlin/xyz/self/sdk/api/SelfSdkConfig.kt
  • packages/native-shell-android/src/main/kotlin/xyz/self/sdk/api/SelfSdkLaunchConfig.kt
  • packages/native-shell-android/src/main/kotlin/xyz/self/sdk/handlers/LifecycleHandler.kt
  • packages/native-shell-android/src/main/kotlin/xyz/self/sdk/handlers/SecureStorageHandler.kt
  • packages/native-shell-android/src/main/kotlin/xyz/self/sdk/webview/AndroidWebViewHost.kt
  • packages/native-shell-android/src/main/kotlin/xyz/self/sdk/webview/SelfVerificationActivity.kt
  • packages/native-shell-ios/Resources/self-sdk-web/animations/app-tour-generate.json
  • packages/native-shell-ios/Resources/self-sdk-web/animations/app-tour-get-started.json
  • packages/native-shell-ios/Resources/self-sdk-web/animations/app-tour-proof.json
  • packages/native-shell-ios/Resources/self-sdk-web/animations/app-tour-welcome.json
  • packages/native-shell-ios/Resources/self-sdk-web/animations/cloud-backup.json
  • packages/native-shell-ios/Resources/self-sdk-web/animations/proof-progress.json
  • packages/native-shell-ios/Resources/self-sdk-web/animations/proof-success-check.json
  • packages/native-shell-ios/Resources/self-sdk-web/animations/proof-success.json
  • packages/native-shell-ios/Resources/self-sdk-web/animations/push-notification-prompt.json
  • packages/native-shell-ios/Resources/self-sdk-web/animations/scan-bar.json
  • packages/native-shell-ios/Resources/self-sdk-web/assets/didit-sdk.esm-D5Sol1U1.js
  • packages/native-shell-ios/Resources/self-sdk-web/assets/index-LqDWjDzu.css
  • packages/native-shell-ios/Resources/self-sdk-web/assets/index-YX6AnLbA.js
  • packages/native-shell-ios/Resources/self-sdk-web/assets/index.es-CVNUZmbC.js
  • packages/native-shell-ios/Resources/self-sdk-web/index.html
  • packages/native-shell-ios/Sources/SelfNativeShell/API/SecureStorageProvider.swift
  • packages/native-shell-ios/Sources/SelfNativeShell/API/SelfSdk.swift
  • packages/native-shell-ios/Sources/SelfNativeShell/API/SelfSdkConfig.swift
  • packages/native-shell-ios/Sources/SelfNativeShell/Handlers/LifecycleHandler.swift
  • packages/native-shell-ios/Sources/SelfNativeShell/Handlers/SecureStorageHandler.swift
  • packages/native-shell-ios/Sources/SelfNativeShell/WebView/SelfWebViewHost.swift
  • packages/rn-sdk-test-app/android/app/debug.keystore
  • packages/rn-sdk-test-app/scripts/postinstall.cjs
  • packages/sdk-test-app/android/app/build.gradle.kts
  • packages/sdk-test-app/android/app/src/main/AndroidManifest.xml
  • packages/sdk-test-app/android/app/src/main/kotlin/xyz/self/testapp/EncryptedPrefsStorageProvider.kt
  • packages/sdk-test-app/android/app/src/main/kotlin/xyz/self/testapp/MainActivity.kt
  • packages/sdk-test-app/android/app/src/main/res/xml/network_security_config.xml
  • packages/sdk-test-app/ios/SelfTestApp/ContentView.swift
  • packages/sdk-test-app/ios/SelfTestApp/KeychainStorageProvider.swift
  • packages/webview-app/package.json
  • packages/webview-app/src/App.tsx
  • packages/webview-app/src/providers/SelfClientProvider.tsx
  • packages/webview-app/src/screens/debug/KeychainDebugScreen.tsx
  • packages/webview-app/src/screens/onboarding/ConfirmIdentificationScreen.tsx
  • packages/webview-app/src/screens/onboarding/ProviderLaunchScreen.tsx
  • packages/webview-app/src/screens/proving/DiscloseResultScreen.tsx
  • packages/webview-app/src/screens/proving/ProofGenerationRouteScreen.tsx
  • packages/webview-app/src/screens/proving/ProvingScreen.tsx
  • packages/webview-app/src/screens/proving/VerificationResultScreen.tsx
  • packages/webview-app/src/screens/recovery/RecoveryFailureScreen.tsx
  • packages/webview-app/src/screens/recovery/RecoverySuccessScreen.tsx
  • packages/webview-app/src/screens/recovery/SecretPhraseInputScreen.tsx
  • packages/webview-app/src/screens/tunnel/KycMockScreen.tsx
  • packages/webview-app/src/screens/tunnel/TourScreen.tsx
  • packages/webview-app/src/screens/tunnel/TunnelDiscloseScreen.tsx
  • packages/webview-app/src/screens/tunnel/TunnelKycSuccessScreen.tsx
  • packages/webview-app/src/screens/tunnel/TunnelKycWrapper.tsx
  • packages/webview-app/src/screens/tunnel/TunnelProofReceiptScreen.tsx
  • packages/webview-app/src/screens/tunnel/TunnelProvingScreen.tsx
  • packages/webview-app/src/screens/tunnel/TunnelRecoveryRequiredScreen.tsx
  • packages/webview-app/src/screens/tunnel/TunnelResultScreen.tsx
  • packages/webview-app/src/utils/buildKycDocument.ts
  • packages/webview-app/src/utils/diditProvider.ts
  • packages/webview-app/src/utils/insets.ts
  • packages/webview-app/src/utils/provingUtils.test.ts
  • packages/webview-app/src/utils/provingUtils.ts
  • packages/webview-app/src/utils/secretManager.ts
  • packages/webview-app/src/utils/selfAppContext.ts
  • packages/webview-app/src/utils/verificationRequest.test.ts
  • packages/webview-app/src/utils/verificationRequest.ts
  • packages/webview-app/tests/screens/recovery/recoverySupportScreens.test.tsx
  • packages/webview-app/tests/screens/tunnel/tunnelFlowScreens.test.tsx
  • packages/webview-app/tests/utils/secretManager.test.ts
  • sdk/core/README.md
  • sdk/qrcode-angular/README.md
  • sdk/qrcode-angular/src/lib/common.ts
  • sdk/qrcode/README.md
  • sdk/sdk-common/index.ts
  • specs/projects/sdk/OVERVIEW.md
  • specs/projects/sdk/workstreams/kmp-revival/SPEC.md
  • specs/projects/sdk/workstreams/kmp-revival/plans/KR-01-android-parity.md
  • specs/projects/sdk/workstreams/kmp-revival/plans/KR-02-ios-parity.md
  • specs/projects/sdk/workstreams/kmp-revival/plans/KR-03-validate-and-publish.md
  • specs/projects/sdk/workstreams/native-shells-lite/SPEC.md
  • specs/projects/sdk/workstreams/native-shells-lite/plans/NSL-04-delegate-keychain.md
  • specs/projects/sdk/workstreams/webview/SPEC.md
  • specs/projects/sdk/workstreams/webview/plans/WV-17-recovery-phrase-restore-flow.md
💤 Files with no reviewable changes (2)
  • packages/native-shell-android/build.gradle.kts
  • packages/webview-app/src/screens/tunnel/KycMockScreen.tsx

Comment thread common/src/utils/kyc/ecdsa/utils.ts
Comment thread common/src/utils/trees.ts
Comment on lines +611 to +620
export const getNameDobLeafKyc = (name: string, dob: string) => {
const namePaddingLength = 64;
const paddedName = name
.padEnd(namePaddingLength, '\0')
.split('')
.map((char) => char.charCodeAt(0));
const nameHash = BigInt(packBytesAndPoseidon(paddedName));
const dobHash = BigInt(poseidon8(stringToAsciiBigIntArray(dob)));
return generateSmallKey(poseidon2([dobHash, nameHash]));
};

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

poseidon8 will throw if dob is not exactly 8 characters.

Line 618 passes dob through stringToAsciiBigIntArray to poseidon8, which requires exactly 8 inputs. If a caller provides a date in any format other than 8 characters (e.g., "2000-12-31" or "12/31/2000"), this will crash at runtime.

Proposed fix
 export const getNameDobLeafKyc = (name: string, dob: string) => {
   const namePaddingLength = 64;
   const paddedName = name
     .padEnd(namePaddingLength, '\0')
     .split('')
     .map((char) => char.charCodeAt(0));
   const nameHash = BigInt(packBytesAndPoseidon(paddedName));
+  const dobArr = stringToAsciiBigIntArray(dob);
+  if (dobArr.length !== 8) {
+    throw new Error(`dob must be exactly 8 characters (YYYYMMDD), got ${dob.length}`);
+  }
-  const dobHash = BigInt(poseidon8(stringToAsciiBigIntArray(dob)));
+  const dobHash = BigInt(poseidon8(dobArr));
   return generateSmallKey(poseidon2([dobHash, nameHash]));
 };

Comment thread common/src/utils/trees.ts
Comment on lines +40 to +55
// Status 5 with REGISTERED_COMMITMENT → route to recovery flow
if (data.status === 5 && data.error_code === 'REGISTERED_COMMITMENT') {
return {
shouldDisconnect: true,
actorEvent: { type: 'PROVE_ALREADY_REGISTERED' },
analytics: [
{
event: 'SOCKETIO_PROOF_FAILURE',
data: {
error_code: data.error_code,
reason: data.reason,
},
},
],
};
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Scope REGISTERED_COMMITMENT remap to register flow only

At Line 41, this branch triggers PROVE_ALREADY_REGISTERED for any circuit. That can misroute non-register flows into account recovery instead of normal failure handling.

Proposed fix
-  if (data.status === 5 && data.error_code === 'REGISTERED_COMMITMENT') {
+  if (circuitType === 'register' && data.status === 5 && data.error_code === 'REGISTERED_COMMITMENT') {
     return {
       shouldDisconnect: true,
       actorEvent: { type: 'PROVE_ALREADY_REGISTERED' },
       analytics: [
         {
           event: 'SOCKETIO_PROOF_FAILURE',
           data: {
             error_code: data.error_code,
             reason: data.reason,
           },
         },
       ],
     };
   }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// Status 5 with REGISTERED_COMMITMENT → route to recovery flow
if (data.status === 5 && data.error_code === 'REGISTERED_COMMITMENT') {
return {
shouldDisconnect: true,
actorEvent: { type: 'PROVE_ALREADY_REGISTERED' },
analytics: [
{
event: 'SOCKETIO_PROOF_FAILURE',
data: {
error_code: data.error_code,
reason: data.reason,
},
},
],
};
}
// Status 5 with REGISTERED_COMMITMENT → route to recovery flow
if (circuitType === 'register' && data.status === 5 && data.error_code === 'REGISTERED_COMMITMENT') {
return {
shouldDisconnect: true,
actorEvent: { type: 'PROVE_ALREADY_REGISTERED' },
analytics: [
{
event: 'SOCKETIO_PROOF_FAILURE',
data: {
error_code: data.error_code,
reason: data.reason,
},
},
],
};
}

Comment on lines +31 to +34
if (!version || major < 3 || (major === 3 && minor < 2)) {
console.log(`[rn-sdk-test-app] Skipping iOS pod install: Ruby ${version || 'not found'} < 3.2 required. Install Ruby 3.2+ to enable.`);
process.exit(0);
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Early exit bypasses .xcode.env.local cleanup and can leave stale build state.

On this return path, the script exits before the cleanup at Line 46. If .xcode.env.local already exists, it remains and can poison later iOS builds with stale env values. Run that cleanup before this process.exit(0) (or in a shared finally-style path) so all exits are safe.

Proposed fix
 const rubyVersion = spawnSync('ruby', ['-e', 'puts RUBY_VERSION'], { encoding: 'utf8' });
 const version = (rubyVersion.stdout || '').trim();
 const [major, minor] = version.split('.').map(Number);

 if (!version || major < 3 || (major === 3 && minor < 2)) {
+  fs.rmSync(path.join(iosDir, '.xcode.env.local'), { force: true });
   console.log(`[rn-sdk-test-app] Skipping iOS pod install: Ruby ${version || 'not found'} < 3.2 required. Install Ruby 3.2+ to enable.`);
   process.exit(0);
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (!version || major < 3 || (major === 3 && minor < 2)) {
console.log(`[rn-sdk-test-app] Skipping iOS pod install: Ruby ${version || 'not found'} < 3.2 required. Install Ruby 3.2+ to enable.`);
process.exit(0);
}
if (!version || major < 3 || (major === 3 && minor < 2)) {
fs.rmSync(path.join(iosDir, '.xcode.env.local'), { force: true });
console.log(`[rn-sdk-test-app] Skipping iOS pod install: Ruby ${version || 'not found'} < 3.2 required. Install Ruby 3.2+ to enable.`);
process.exit(0);
}

Comment on lines +21 to +28
const onNext = useCallback(async () => {
if (stepNum < 4) {
navigate(`/tunnel/tour/${stepNum + 1}`);
return;
}

const selectedDoc = await loadSelectedDocument(client);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Catch loadSelectedDocument() failures before the last tour step dead-ends.

If that read rejects, onNext exits with an unhandled promise rejection and the user stays stuck on step 4. This path needs a fallback route instead of letting the error escape.

🔧 Proposed fix
   const onNext = useCallback(async () => {
     if (stepNum < 4) {
       navigate(`/tunnel/tour/${stepNum + 1}`);
       return;
     }
 
-    const selectedDoc = await loadSelectedDocument(client);
+    let selectedDoc = null;
+    try {
+      selectedDoc = await loadSelectedDocument(client);
+    } catch {
+      navigate('/tunnel/kyc');
+      return;
+    }
 
     console.log('selected Doc', selectedDoc);
     const isRegisteredRealDoc = selectedDoc?.metadata?.isRegistered === true;
As per coding guidelines, "Always use try-catch for async operations with graceful degradation when native modules fail and comprehensive error boundaries."
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const onNext = useCallback(async () => {
if (stepNum < 4) {
navigate(`/tunnel/tour/${stepNum + 1}`);
return;
}
const selectedDoc = await loadSelectedDocument(client);
const onNext = useCallback(async () => {
if (stepNum < 4) {
navigate(`/tunnel/tour/${stepNum + 1}`);
return;
}
let selectedDoc = null;
try {
selectedDoc = await loadSelectedDocument(client);
} catch {
navigate('/tunnel/kyc');
return;
}
console.log('selected Doc', selectedDoc);
const isRegisteredRealDoc = selectedDoc?.metadata?.isRegistered === true;


const selectedDoc = await loadSelectedDocument(client);

console.log('selected Doc', selectedDoc);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Remove the raw selectedDoc log.

selectedDoc is loaded from persisted identity state, so dumping the whole object here can leak document metadata or PII into browser/native logs.

🔧 Proposed fix
-    console.log('selected Doc', selectedDoc);
As per coding guidelines, "NEVER log sensitive data including PII (names, DOB, passport numbers, addresses), credentials, tokens, API keys, private keys, or session identifiers."
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
console.log('selected Doc', selectedDoc);

Comment on lines +46 to +50
} catch (err) {
analytics.trackEvent('tunnel_result_failure', {
error: err instanceof Error ? err.message : 'Failed to send result',
});
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Silent failure leaves user stranded on success screen without sending result.

If lifecycle.setResult throws, the catch block logs analytics but doesn't navigate away, show an error, or retry. The user sees the success screen, believes everything worked, and may dismiss—but the verification result was never delivered to the requesting app.

Proposed fix
   } catch (err) {
     analytics.trackEvent('tunnel_result_failure', {
       error: err instanceof Error ? err.message : 'Failed to send result',
     });
+    navigate('/tunnel/proof/result', {
+      replace: true,
+      state: { success: false, error: 'Failed to send verification result. Please try again.' },
+    });
   }

* For browser preview, we use small fixed values for visual padding.
*/
export const WEB_SAFE_AREA = createSafeAreaProps({ top: 16, bottom: 16 });
export const WEB_SAFE_AREA = createSafeAreaProps({ top: 0, bottom: 16 });

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Restore a non-zero top preview inset.

Line 8 removes the top padding from WEB_SAFE_AREA, so every browser-preview screen using this helper will render flush against the top edge. That regresses the safe-area padding this utility is meant to provide.

Suggested fix
-export const WEB_SAFE_AREA = createSafeAreaProps({ top: 0, bottom: 16 });
+export const WEB_SAFE_AREA = createSafeAreaProps({ top: 16, bottom: 16 });
Based on learnings, verify content respects safe area padding in browser dev server (not clipped or flush against edges).
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export const WEB_SAFE_AREA = createSafeAreaProps({ top: 0, bottom: 16 });
export const WEB_SAFE_AREA = createSafeAreaProps({ top: 16, bottom: 16 });

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
app/src/screens/home/ProofHistoryList.tsx (1)

190-204: ⚠️ Potential issue | 🟠 Major

Disclosure count is inflated after normalization

After Line 190 normalization, disclosures includes minimum_age (number) and excluded_countries (array). The current truthy-value count at Line 202 over-counts requested disclosures.

Proposed fix
-        const disclosureCount = Object.values(disclosures).filter(
-          value => value,
-        ).length;
+        const disclosureCount = [
+          disclosures.issuing_state,
+          disclosures.name,
+          disclosures.passport_number,
+          disclosures.nationality,
+          disclosures.date_of_birth,
+          disclosures.gender,
+          disclosures.expiry_date,
+          disclosures.ofac,
+          (disclosures.excluded_countries?.length ?? 0) > 0,
+          disclosures.minimum_age != null,
+        ].filter(Boolean).length;
app/src/screens/home/ProofHistoryScreen.tsx (1)

195-210: ⚠️ Potential issue | 🟠 Major

Disclosure badge count miscalculates normalized configs

With normalized disclosures (Line 195), counting all truthy values (Line 208) includes non-boolean fields and inflates the badge count.

Proposed fix
-        const disclosureCount = Object.values(disclosures).filter(
-          value => value,
-        ).length;
+        const disclosureCount = [
+          disclosures.issuing_state,
+          disclosures.name,
+          disclosures.passport_number,
+          disclosures.nationality,
+          disclosures.date_of_birth,
+          disclosures.gender,
+          disclosures.expiry_date,
+          disclosures.ofac,
+          (disclosures.excluded_countries?.length ?? 0) > 0,
+          disclosures.minimum_age != null,
+        ].filter(Boolean).length;

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: ddea747a-ca16-4862-b2c9-a4b0507f6f0d

📥 Commits

Reviewing files that changed from the base of the PR and between e487b69 and 944f63b.

📒 Files selected for processing (5)
  • app/src/screens/home/ProofHistoryList.tsx
  • app/src/screens/home/ProofHistoryScreen.tsx
  • packages/webview-app/src/utils/selfAppContext.ts
  • sdk/qrcode-angular/src/lib/common.ts
  • sdk/sdk-common/index.ts
🚧 Files skipped from review as they are similar to previous changes (3)
  • sdk/sdk-common/index.ts
  • sdk/qrcode-angular/src/lib/common.ts
  • packages/webview-app/src/utils/selfAppContext.ts

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2


ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: c023eba9-77d4-4b39-bbaf-9c0ffd6e29c3

📥 Commits

Reviewing files that changed from the base of the PR and between 944f63b and 76633ac.

📒 Files selected for processing (4)
  • common/src/utils/appType.ts
  • new-common/src/app/builder.ts
  • sdk/qrcode-angular/src/lib/common.ts
  • sdk/sdk-common/index.ts

Comment on lines +281 to +292
function normalizeDisclosures(raw: Record<string, unknown>): Record<string, unknown> {
const result = { ...raw };
if ('excludedCountries' in result && !('excluded_countries' in result)) {
result.excluded_countries = result.excludedCountries;
}
delete result.excludedCountries;
if ('minimumAge' in result && !('minimum_age' in result)) {
result.minimum_age = result.minimumAge;
}
delete result.minimumAge;
return result;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

The Angular SDK now returns a disclosure object that disagrees with its public type.

The local SelfAppDisclosureConfig still advertises excludedCountries / minimumAge, but normalizeDisclosures() removes those keys and Line 650 saves only excluded_countries / minimum_age. Any consumer that reads builder.build().disclosures.minimumAge will compile and then get undefined at runtime. Keep the returned public object camelCase, or dual-expose both key styles until this SDK surface is migrated.

Also applies to: 650-650

Comment thread sdk/sdk-common/index.ts
Comment on lines +281 to +294
function normalizeDisclosures(
raw: Record<string, unknown>,
): Record<string, unknown> {
const result = { ...raw };
if ('excludedCountries' in result && !('excluded_countries' in result)) {
result.excluded_countries = result.excludedCountries;
}
delete result.excludedCountries;
if ('minimumAge' in result && !('minimum_age' in result)) {
result.minimum_age = result.minimumAge;
}
delete result.minimumAge;
return result;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Returned SDK objects no longer match the public disclosure type.

SelfAppDisclosureConfig in this file still exposes excludedCountries and minimumAge, but this helper deletes those keys and Line 656 stores only excluded_countries / minimum_age in SelfApp.disclosures. That means new SelfAppBuilder(...).build().disclosures.minimumAge still type-checks for integrators and now returns undefined at runtime. This mismatch is already visible in packages/webview-app/src/utils/selfAppContext.ts:1-50, which now populates snake_case keys against this type. Keep the public object camelCase, or expose both key styles on the returned object during the transition.

Also applies to: 656-658

Set WEB_SAFE_AREA top to 0 — native shell handles status bar insets edge-to-edge. Add flex wrappers to ConfirmIdentificationScreen and VerificationResultScreen so StatusState fills viewport. Bump @selfxyz/euclid and euclid-core to 1.4.0 (edge-to-edge backgrounds, responsive animations, centered text, fixed button visibility). Delete orphaned KycMockScreen replaced by TunnelKycWrapper.
excludedCountries and minimumAge were camelCase while all other fields in SelfAppDisclosureConfig used snake_case. Renames them to excluded_countries and minimum_age across all four type definition files (common, new-common, sdk-common, qrcode-angular) and updates all consumers.

Adds normalizeDisclosureConfig() to handle backward compatibility when parsing QR/deep-link payloads and stored proof history that still use the old camelCase keys. The native shell wire format (minimumAge: prefix in the disclosures URL param) is preserved unchanged — only the TypeScript type fields and their consumers are renamed.
Add normalizeDisclosureConfig() to ProofHistoryScreen and ProofHistoryList where stored disclosure JSON is parsed, for consistency with ProofHistoryDetailScreen. Add normalizeDisclosureConfig() to the two standalone SDK copies (sdk-common, qrcode-angular) so their consumers have access to the migration helper without importing from @selfxyz/common. Add comment in selfAppContext.ts explaining the intentional minimumAge: wire-format prefix.
…nally

Reverts the public SDK interface (sdk-common, qrcode-angular) back to camelCase for minimumAge and excludedCountries to avoid a breaking change for integrators. All four SelfAppBuilder copies (common, new-common, sdk-common, qrcode-angular) now normalize camelCase to snake_case in the constructor, ensuring internal code always receives consistent snake_case keys. Removes normalizeDisclosureConfig from public SDK exports. App-side normalize calls remain for stored proof history and deeplink data that may contain old camelCase keys.
Math.random() is not cryptographically secure and must not be used for generating values in ECDSA cryptographic operations. Replace with crypto.getRandomValues() to prevent potential key recovery attacks.
…ionalityLeaf

Function declares bigint return type but had code paths returning undefined via bare return statements and a missing return in the catch block. This could propagate undefined where bigint is expected.
@Tranquil-Flow Tranquil-Flow changed the title fix(sdk): standardize SelfAppDisclosureConfig to all snake_case fix: standardize SelfAppDisclosureConfig to all snake_case Apr 16, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1


ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 5114e724-eb1f-4b46-91a7-82cb7c8b41d2

📥 Commits

Reviewing files that changed from the base of the PR and between 76633ac and 1f44ce3.

📒 Files selected for processing (13)
  • .gitignore
  • app/src/navigation/deeplinks.ts
  • app/src/screens/home/ProofHistoryDetailScreen.tsx
  • app/src/screens/home/ProofHistoryList.tsx
  • app/src/screens/home/ProofHistoryScreen.tsx
  • app/src/utils/disclosureUtils.ts
  • common/src/utils/appType.ts
  • common/src/utils/circuits/registerInputs.ts
  • common/src/utils/kyc/ecdsa/utils.ts
  • common/src/utils/trees.ts
  • new-common/src/app/builder.ts
  • new-common/src/foundation/types/app.ts
  • packages/webview-app/src/utils/selfAppContext.ts
✅ Files skipped from review due to trivial changes (2)
  • .gitignore
  • app/src/screens/home/ProofHistoryScreen.tsx
🚧 Files skipped from review as they are similar to previous changes (5)
  • packages/webview-app/src/utils/selfAppContext.ts
  • new-common/src/foundation/types/app.ts
  • app/src/screens/home/ProofHistoryDetailScreen.tsx
  • common/src/utils/appType.ts
  • app/src/utils/disclosureUtils.ts

Comment thread common/src/utils/trees.ts
Comment on lines 826 to +839
if (passport.length !== 9) {
console.log('parsed passport length is not 9:', i, passport);
return;
return BigInt(0);
}
if (nationality.length !== 3) {
console.log('parsed nationality length is not 3:', i, nationality);
return;
return BigInt(0);
}
try {
const fullHash = poseidon12(passport.concat(nationality));
return generateSmallKey(fullHash);
} catch (err) {
console.log('err : passport', err, i, passport);
return BigInt(0);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Remove sensitive identity fields from error logs in this function.

Line 827/Line 838 log passport, and Line 831 logs nationality. These are sensitive identity-derived values and should not be emitted to logs.

Proposed fix
 export function getPassportNumberAndNationalityLeaf(
   passport: (bigint | number)[],
   nationality: (bigint | number)[],
   i?: number
 ): bigint {
   if (passport.length !== 9) {
-    console.log('parsed passport length is not 9:', i, passport);
+    console.warn('parsed passport length is not 9:', i, { passportLength: passport.length });
     return BigInt(0);
   }
   if (nationality.length !== 3) {
-    console.log('parsed nationality length is not 3:', i, nationality);
+    console.warn('parsed nationality length is not 3:', i, { nationalityLength: nationality.length });
     return BigInt(0);
   }
   try {
     const fullHash = poseidon12(passport.concat(nationality));
     return generateSmallKey(fullHash);
   } catch (err) {
-    console.log('err : passport', err, i, passport);
+    console.error('err : passport hash generation failed', err, i);
     return BigInt(0);
   }
 }

As per coding guidelines: "NEVER log sensitive data including PII (names, DOB, passport numbers, addresses), credentials, tokens, API keys, private keys, or session identifiers."

The lockfile was stale after rebasing, causing CI to fail with Yarn's hardened mode lockfile validation.
@Tranquil-Flow

Copy link
Copy Markdown
Contributor Author

@greptileai review

@greptile-apps

greptile-apps Bot commented Apr 16, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR standardizes SelfAppDisclosureConfig from camelCase (minimumAge, excludedCountries) to snake_case (minimum_age, excluded_countries) across the codebase, and adds a normalizeDisclosureConfig() adapter layer for backward compatibility with stored proof history and old QR/deep-link payloads. It also bundles two unrelated fixes: upgrading generateRandomsg to use crypto.getRandomValues() (CSPRNG), and fixing getPassportNumberAndNationalityLeaf to return BigInt(0) instead of undefined on validation failures.

Key changes:

  • SelfAppDisclosureConfig fields renamed in common and new-common type definition files
  • normalizeDisclosureConfig() exported from common/src/utils/appType.ts and called at all deep-link / proof-history parse points
  • SelfAppBuilder.build() in all four SDK variants now normalizes disclosures after the ...config spread

Issue — SelfAppDisclosureConfig not renamed in sdk/sdk-common and sdk/qrcode-angular: The PR description states the rename is applied to all four type definition files, but sdk/sdk-common/index.ts and sdk/qrcode-angular/src/lib/common.ts still declare excludedCountries and minimumAge as camelCase fields. Their SelfAppBuilder.build() calls normalizeDisclosures() at runtime to produce snake_case, but SelfApp.disclosures is typed as the unchanged SelfAppDisclosureConfig. Any consumer accessing disclosures.minimumAge will compile without error but receive undefined at runtime — a silent type/runtime mismatch.

Confidence Score: 3/5

Not safe to merge as-is — two public SDK files have a type/runtime mismatch that will silently break any consumer accessing the renamed disclosure fields.

The app-internal changes are correct and complete. However, sdk/sdk-common/index.ts and sdk/qrcode-angular/src/lib/common.ts were not updated: their SelfAppDisclosureConfig interfaces still declare minimumAge and excludedCountries (camelCase), while SelfAppBuilder.build() normalizes the runtime object to snake_case. Consumers of these SDKs reading disclosures.minimumAge get undefined at runtime while TypeScript reports no error.

sdk/sdk-common/index.ts and sdk/qrcode-angular/src/lib/common.tsSelfAppDisclosureConfig interface fields need to be renamed to snake_case to match the normalized runtime shape.

Important Files Changed

Filename Overview
common/src/utils/appType.ts Renames fields to snake_case, adds exported normalizeDisclosureConfig() for backward compat, and correctly moves disclosures normalization after the ...config spread in SelfAppBuilder.build().
sdk/sdk-common/index.ts SelfAppDisclosureConfig interface still uses camelCase (excludedCountries, minimumAge) while normalizeDisclosures() produces snake_case at runtime — creates a type/runtime mismatch for all SDK consumers.
sdk/qrcode-angular/src/lib/common.ts Same type/runtime mismatch as sdk/sdk-common/index.ts — interface still camelCase while runtime is normalized to snake_case.
new-common/src/app/builder.ts Adds normalizeDisclosures() locally (duplicated from common) and applies it correctly; types are consistent with new-common/src/foundation/types/app.ts.
packages/webview-app/src/utils/selfAppContext.ts Correctly maps native shell wire-format minimumAge: prefix to minimum_age and excludedCountries URL param to excluded_countries, with an explanatory comment.
common/src/utils/circuits/registerInputs.ts All references to minimumAge/excludedCountries updated to minimum_age/excluded_countries; filter lists in getSelectorDg1Passport and getSelectorDg1IdCard correctly updated.
common/src/utils/kyc/ecdsa/utils.ts Upgrades generateRandomsg from Math.random() (PRNG) to crypto.getRandomValues() (CSPRNG) — correct security improvement.
app/src/navigation/deeplinks.ts Correctly calls normalizeDisclosureConfig on incoming deep-link JSON before passing to setSelfApp, enabling backward compat with old QR payloads.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A["External input\n(QR code / deep-link / stored history)"] -->|camelCase or snake_case disclosures| B["normalizeDisclosureConfig()\ncommon/src/utils/appType.ts"]
    B -->|snake_case only| C["SelfAppDisclosureConfig\nexcluded_countries, minimum_age"]

    D["SelfAppBuilder config\n(any SDK package)"] -->|"config.disclosures\n(camelCase or snake_case)"| E["normalizeDisclosures()\ninline in each builder"]
    E -->|snake_case| F["SelfApp.disclosures\nruntime object"]

    subgraph "Types match runtime"
      G["common — excluded_countries / minimum_age"]
      H["new-common — excluded_countries / minimum_age"]
    end

    subgraph "Types do NOT match runtime"
      I["sdk-common — still camelCase in interface"]
      J["qrcode-angular — still camelCase in interface"]
    end

    F --> G
    F --> H
    F --> I
    F --> J
Loading

Comments Outside Diff (2)

  1. sdk/sdk-common/index.ts, line 277-279 (link)

    P1 Type/runtime mismatch — SelfAppDisclosureConfig not renamed here

    The PR renames excludedCountriesexcluded_countries and minimumAgeminimum_age in common and new-common, and the normalizeDisclosures function (added just below) converts the runtime object to snake_case inside SelfAppBuilder.build(). However, the SelfAppDisclosureConfig interface in this file is left unchanged — it still declares excludedCountries and minimumAge as camelCase fields.

    Because SelfApp.disclosures is typed as SelfAppDisclosureConfig, any consumer that does:

    const app = new SelfAppBuilder({ ..., disclosures: { minimumAge: 18 } }).build();
    app.disclosures.minimumAge // TypeScript: number | undefined ✓  — but runtime value: undefined ✗
    app.disclosures.minimum_age // TypeScript error ✗         — but runtime value: 18 ✓

    TypeScript gives a false green light while the value is silently undefined at runtime.

    The interface fields should be updated to match the normalized runtime shape:

  2. sdk/qrcode-angular/src/lib/common.ts, line 277-279 (link)

    P1 Type/runtime mismatch — SelfAppDisclosureConfig not renamed here

    Same issue as sdk/sdk-common/index.ts (these two files shared the same blob hash in the base branch). SelfAppDisclosureConfig still declares excludedCountries and minimumAge with camelCase names, but after SelfAppBuilder.build() calls normalizeDisclosures(), the runtime disclosures object has excluded_countries and minimum_age. Since SelfApp.disclosures is typed as SelfAppDisclosureConfig, any Angular SDK consumer accessing disclosures.minimumAge will get undefined at runtime despite TypeScript reporting the access as valid.

    The interface fields need to be updated to match the normalized output:

Reviews (1): Last reviewed commit: "fix: regenerate yarn.lock after rebase o..." | Re-trigger Greptile

Comment on lines +8 to +19
function normalizeDisclosures(raw: Record<string, unknown>): Record<string, unknown> {
const result = { ...raw };
if ('excludedCountries' in result && !('excluded_countries' in result)) {
result.excluded_countries = result.excludedCountries;
}
delete result.excludedCountries;
if ('minimumAge' in result && !('minimum_age' in result)) {
result.minimum_age = result.minimumAge;
}
delete result.minimumAge;
return result;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 Duplicated normalize logic

normalizeDisclosures in this file is identical to normalizeDisclosureConfig in common/src/utils/appType.ts (and duplicated again in sdk/sdk-common and sdk/qrcode-angular). If new-common can import from @selfxyz/common, consider importing the shared utility instead of maintaining four copies of the same logic.

If standalone packaging constraints prevent shared imports, this is acceptable, but worth tracking so future changes to the normalization logic don't diverge across copies.

Base automatically changed from dev to staging September 4, 2026 16:14
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.

1 participant