Bytecode analysis showed MutablePoint.x/y/z, AffinePoint.x/y, and all
PointScratch properties compile to invokevirtual getter calls instead
of direct field reads (getfield). Per verify:
- MutablePoint getX/Y/Z: ~7,450 invokevirtual → getfield
- PointScratch getT/getW/etc: ~2,000+ invokevirtual → getfield
Each invokevirtual has ~3-5ns overhead on ART vs ~1ns for getfield.
@JvmField eliminates the getter method entirely, compiling property
access to a direct field read. On non-JVM targets (iOS), @JvmField
is silently ignored.
https://claude.ai/code/session_01EMY5RnXb9rnsyU2KbXrSaY
Bytecode analysis revealed that every `a.toULong() < b.toULong()`
comparison generates 2 invokestatic calls to ULong.constructor-impl
(NOOPs that return the input unchanged) plus Long.compareUnsigned.
Across all secp256k1 hot paths, this produced 554 NOOP invokestatic
calls in the bytecode, translating to ~18,000 wasted calls per verify.
Replace all toULong() comparisons with an inline uLt() helper that
uses the XOR-with-MIN_VALUE trick directly:
(a xor Long.MIN_VALUE) < (b xor Long.MIN_VALUE)
This produces pure arithmetic bytecode (lxor, lcmp, ifge) with ZERO
method calls, eliminating all ULong.constructor-impl overhead.
Before: lload, invokestatic ULong.constructor-impl, lload,
invokestatic ULong.constructor-impl, invokestatic
Long.compareUnsigned, ifge (6 bytecodes, 3 method calls)
After: lload, ldc MIN_VALUE, lxor, lload, ldc MIN_VALUE, lxor,
lcmp, ifge (8 bytecodes, 0 method calls)
https://claude.ai/code/session_01EMY5RnXb9rnsyU2KbXrSaY
The previous approach inlined all 3 API-level branches into a single
fieldMulReduce method (~600 DEX instructions). ART's register allocator
produces suboptimal code for such large methods, causing stack spills
that negate the benefit of eliminating wrapper calls.
Split each API-level path into its own private function (~200 DEX
instructions each). ART JIT-compiles only the hot one (fieldMulApi35
on API 35+) with full optimization, while the tiny dispatch function
(fieldMulReduce) is easily devirtualized and inlined.
https://claude.ai/code/session_01EMY5RnXb9rnsyU2KbXrSaY
On Android (ART), the unsignedMultiplyHigh wrapper function has per-call
branching for API level detection that prevents ART's JIT from inlining
the intrinsic into the hot loop. This creates ~10,000 extra function
calls per signature verify (20 wrapper calls × 500 field muls).
This commit introduces fieldMulReduce/fieldSqrReduce as expect/actual
functions that fuse U256.mulWide + FieldP.reduceWide into a single
compilation unit. The multiply-high intrinsic is passed as a crossinline
lambda and inlined at each call site, producing platform-specific code
with zero wrapper overhead:
- Android API 35+: Math.unsignedMultiplyHigh inlined directly (UMULH)
- Android API 31-34: Math.multiplyHigh + correction inlined (SMULH)
- Android API <31: pure-Kotlin fallback inlined
- JVM: Math.unsignedMultiplyHigh inlined directly
- Native: pure-Kotlin fallback inlined
The API level check happens ONCE per fieldMulReduce call (outermost
branch) rather than per multiply-high call (innermost loop), so ART
profiles and JIT-compiles only the hot path.
https://claude.ai/code/session_01EMY5RnXb9rnsyU2KbXrSaY
Port the NWC connection buttons from the ZapSetup screen (UpdateZapAmountDialog)
to the AddWalletScreen used by the multi-wallet flow. Users can now connect a
local wallet app via deep link, paste an NWC URI from clipboard, or scan a QR
code — matching the same options already available in the zap settings.
https://claude.ai/code/session_01QFunqjudjjrrn6pnsUSPCS
- AddWalletScreen: dedicated screen with name input + NWC URI paste,
replacing the generic NIP47Setup flow for adding new wallets
- Rename: inline dialog on wallet cards to rename wallets
- Reorder: up/down arrow buttons on wallet cards to reorder the list
- AccountSettings: add renameNwcWallet() and moveNwcWallet() methods
- WalletViewModel: add addWallet(), renameWallet(), moveWallet()
- Fix client.send -> client.publish after main rename
- New Route.WalletAdd for the dedicated add wallet screen
https://claude.ai/code/session_013sWHKeE1uNfAZJWD4FU9mT
Migrate from a single NWC wallet connection to supporting multiple wallets.
Users can now add, remove, and manage multiple NWC wallets, view balance
and transactions per wallet, and select a default wallet for zaps.
Key changes:
- New NwcWalletEntry/NwcWalletEntryNorm data models for wallet storage
- AccountSettings: replace single zapPaymentRequest with nwcWallets list
and defaultNwcWalletId, with backward-compatible changeZapPaymentRequest
- LocalPreferences: new persistence keys with automatic migration from
legacy single-wallet format
- NwcSignerState: derives default wallet URI from multi-wallet settings,
adds sendNwcRequestToWallet for targeting specific wallets
- Account: adds sendNwcRequestToWallet method
- WalletViewModel: supports wallet list, per-wallet balance/info fetching,
wallet selection, set default, and remove operations
- WalletScreen: shows wallet cards with balance, default indicator, and
management actions (set default, remove with confirmation)
- WalletDetailScreen: per-wallet detail view with balance, send/receive
- New Route.WalletDetail for per-wallet navigation
https://claude.ai/code/session_013sWHKeE1uNfAZJWD4FU9mT
In chess note cards (NoteCompose), player hex keys were displayed as
raw strings. Now uses LoadUser + ClickableUserPicture + UsernameDisplay
to show proper profile pictures and display names for:
- Challenge cards (incoming/outgoing)
- Game end cards (both players)
- PGN metadata in game viewers (white/black players)
Added playerContent composable slot to PGNMetadata and ChessGameViewer
so callers can inject platform-specific user rendering.
https://claude.ai/code/session_0171mKrVEfQnNRabmT7Kv4gf
Previously, selecting a community from the home screen top nav filter
would navigate to the community page. Now it filters the home feed
in-place, applying the community filter to all tabs (New Threads,
Conversations, Live) and adjusting subscriptions accordingly.
The in-place filtering infrastructure already existed (SingleCommunityTopNavFilter,
FilterByListParams, HomeOutboxEventsEoseManager) but was bypassed because
community FeedDefinitions had a route that triggered navigation.
https://claude.ai/code/session_0139NCoHmqvp3aRD3FUjuues
All three saves (zap settings, lightning address, payment targets) now
run sequentially inside one accountViewModel.launchSigner block,
so the signer is only invoked once per save instead of three times.
- Extract sendPostSuspend() from UpdateZapAmountViewModel.sendPost()
- Extract saveLnAddressSuspend() from WalletViewModel.saveLnAddress()
- Extract savePaymentTargetsSuspend() from PaymentTargetsViewModel.savePaymentTargets()
- NIP47SetupScreen.onPost calls all three suspend fns in one launchSigner
https://claude.ai/code/session_017NPhwCnSzCuMn9sTX3ca37
- Remove separate Save/Manage buttons; both are now saved via the
screen's existing SavingTopBar save action
- Lightning address: plain OutlinedTextField, saved on confirm
- Payment targets: inline editable list (Column, not LazyColumn) with
add/delete directly in the scrollable content area
- Wire walletViewModel.saveLnAddress() and
paymentTargetsViewModel.savePaymentTargets() into onPost
- Wire paymentTargetsViewModel.refresh() into onCancel
https://claude.ai/code/session_017NPhwCnSzCuMn9sTX3ca37
- Add trailingContent lambda to UpdateZapAmountContent so callers can
inject sections inside its scrollable Column
- Inject lightning address field and payment targets row into
NIP47SetupScreen via trailingContent
- Add WalletViewModel to NIP47SetupScreen to load/save the lightning address
- Add settings icon button to WalletScreen TopAppBar when wallet is configured,
navigating back to NIP47SetupScreen
- Restore WalletScreen to its original layout (no ln/payment sections)
https://claude.ai/code/session_017NPhwCnSzCuMn9sTX3ca37
Same approach as relay filtering: selecting a hashtag from the top nav
filter now filters the home feed in-place across all tabs, with
subscriptions scoped to that hashtag.
https://claude.ai/code/session_01PZLLjE7MWh7PPz1woVqmxM
- Add lightning address field with save button to WalletScreen
- Add payment targets navigation row to WalletScreen
- Remove lightning address field from profile editor (NewUserMetadataScreen)
- Remove payment targets entry from AllSettings
- Extend WalletViewModel to load/save the lightning address via userMetadata
https://claude.ai/code/session_017NPhwCnSzCuMn9sTX3ca37
Instead of computing on-tap, all 9 tones are precomputed in parallel
after a 1-second debounce when the user stops typing. Tone chips only
appear once results are ready. Tapping a chip instantly shows the
precomputed result. Text changes cancel and restart the computation.
https://claude.ai/code/session_01RbCYGrbbapRMike8WQy41F
When selecting a relay from the top nav filter, the home feed now
filters in-place (New Threads, Conversations, Live) showing only
posts from that relay, with subscriptions scoped to it.
https://claude.ai/code/session_01PZLLjE7MWh7PPz1woVqmxM
Replace the sparkle toggle button in the compose toolbar with a
persistent setting in UI/Application Preferences. The new "Propose AI
text improvements" setting (Always/Never) controls whether the tone
chip row appears in the compose screen.
When enabled and the device supports on-device AI, the tone chips show
automatically below the text field. Removes the per-session toggle
button from BottomRowActions.
https://claude.ai/code/session_01RbCYGrbbapRMike8WQy41F
These options are better suited as settings entries rather than top-level
drawer items. They are now in the Account Settings section, placed after
Relay Setup for logical grouping.
https://claude.ai/code/session_017bgasTSsMRR9aepmAsKEzR