Skip to content

Conversation

@ben-kaufman
Copy link
Contributor

@ben-kaufman ben-kaufman commented Nov 20, 2025

Added gift codes support.

In the demo:

  1. Redeem valid code, no open channels
  2. Try redeem the same code and fail
  3. Redeem a new code when already having a channel
  4. Try redeeming a code which doesn't exist
Screen.Recording.2025-11-19.at.9.42.46.PM.mov

Commands to test with:
npx uri-scheme open "bitkit://gift-ben19112025-3000" --android
npx uri-scheme open "bitkit://gift-notben19112025-3000" --android

Even when the app is closed, running one of the commands should make the app open and claim.
Can try a non existent code with:
npx uri-scheme open "bitkit://gift-x-3000" --android

Can try a code which has already been out of gifts with:
npx uri-scheme open "bitkit://gift-phil30102025-3000" --android

Copy link
Member

@jvsena42 jvsena42 left a comment

Choose a reason for hiding this comment

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

Just missing some open comments

OBS: On the android repo, the business logic is centered in the repositories.
The methods are exposed to the ViewModels with the return wrapped in a Result class

Copy link
Member

@jvsena42 jvsena42 left a comment

Choose a reason for hiding this comment

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

Worked on tests ✅
Found one last issue

@jvsena42 jvsena42 enabled auto-merge November 21, 2025 17:22
jvsena42
jvsena42 previously approved these changes Nov 21, 2025
@synonymdev synonymdev deleted a comment from claude bot Nov 24, 2025
@synonymdev synonymdev deleted a comment from claude bot Nov 24, 2025
@synonymdev synonymdev deleted a comment from claude bot Nov 24, 2025
@synonymdev synonymdev deleted a comment from claude bot Nov 24, 2025
@synonymdev synonymdev deleted a comment from claude bot Nov 24, 2025
@synonymdev synonymdev deleted a comment from claude bot Nov 24, 2025
@synonymdev synonymdev deleted a comment from claude bot Nov 24, 2025
@synonymdev synonymdev deleted a comment from claude bot Nov 24, 2025
@synonymdev synonymdev deleted a comment from claude bot Nov 24, 2025
@synonymdev synonymdev deleted a comment from claude bot Nov 24, 2025
@synonymdev synonymdev deleted a comment from claude bot Nov 24, 2025
@synonymdev synonymdev deleted a comment from claude bot Nov 24, 2025
ovitrif
ovitrif previously approved these changes Nov 24, 2025
Copy link
Collaborator

@ovitrif ovitrif left a comment

Choose a reason for hiding this comment

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

ACK & tested with all gift codes combinations.

pushed a few small edits.

Only having one uncertainty about the use of:

            lightningRepo.executeWhenNodeRunning(
                operationName = "claimGiftCode",
                waitTimeout = waitTimeout,
            ) {
                Result.success(Unit)
            }.getOrThrow()

If I understand correctly, we could change the body of BlocktankRepo.claimGiftCode so that everything that comes after this block could actually be passed inside the block.

Does it make sense to you?!

My understanding is that the purpose of the empty call with a simple Result success is because we want to wait for the node to init if not ready.

@claude
Copy link

claude bot commented Nov 24, 2025

Pull Request Review: Gift Codes Support

Thank you for this comprehensive implementation of gift codes functionality! Overall, the code is well-structured and follows good practices. Below are my findings and suggestions:

✅ Strengths

  1. Clean Architecture: Good separation of concerns between UI (GiftSheet, GiftViewModel), repository (BlocktankRepo), and business logic layers
  2. Error Handling: Proper use of Result types and comprehensive error classification (Used, UsedUp, Error)
  3. UI/UX: Well-designed loading states and error screens with clear user feedback
  4. Code Reusability: Good refactoring of calculateRemoteBalance into a reusable extension function
  5. Type Safety: Good use of sealed classes for GiftRoute and GiftClaimResult

🐛 Potential Bugs & Issues

Critical

  1. Race Condition in GiftViewModel.initialize() (GiftViewModel.kt:47-58)

    • The isClaiming flag is checked before emitting Loading, but the flag is set inside claimGift()
    • If initialize() is called twice rapidly, both calls could pass the check and launch claimGift() concurrently
    • Recommendation: Set isClaiming = true before launching the coroutine, or use a mutex/atomic operation
  2. Missing Null Safety (BlocktankRepo.kt:465)

    • openedOrder.channel?.fundingTx?.id ?: orderId falls back to orderId, but the activity record expects a valid payment hash or transaction ID
    • If the channel opening fails but doesn't throw an exception, this could create invalid activity records
    • Recommendation: Add validation or throw an exception if fundingTx.id is null
  3. No Result Handling for giftPay() (BlocktankRepo.kt:446-448)

    • The giftPay() call doesn't await or check the result, so payment failures go undetected
    • Users might see a success message even if the payment failed
    • Recommendation: Await the result and handle potential failures appropriately

Moderate

  1. Fixed Delay May Be Insufficient (BlocktankRepo.kt:423)

    • The 2-second PEER_CONNECTION_DELAY_MS is a fixed delay that may not be reliable across all network conditions
    • Recommendation: Consider implementing a retry mechanism or checking peer connection status instead of a fixed delay
  2. Double LaunchedEffect Collection (GiftSheet.kt:39-43, 45-57)

    • Two separate LaunchedEffect(Unit) blocks collecting from different flows could lead to ordering issues
    • Recommendation: Combine into a single LaunchedEffect or use remember { } to ensure proper initialization order
  3. Missing Validation (BlocktankRepo.kt:416-417)

    • Gift code format isn't validated (e.g., does it need to match a pattern?)
    • Amount isn't checked against minimum/maximum thresholds
    • Recommendation: Add format validation and reasonable bounds checking

🔒 Security Concerns

  1. Gift Code in Activity Message (GiftViewModel.kt:108)

    • The gift code is stored in plaintext in the activity message field
    • If gift codes are meant to be secret or single-use, this could be a privacy concern
    • Recommendation: Consider whether gift codes need to be stored, or if they should be hashed/obfuscated
  2. No Rate Limiting

    • The implementation doesn't appear to have rate limiting on gift code attempts
    • An attacker could potentially brute-force gift codes
    • Recommendation: Consider implementing client-side rate limiting or exponential backoff on failures
  3. Error Message Information Disclosure (GiftViewModel.kt:133)

    • The error checking uses case-insensitive matching on full error messages, which could match unintended patterns
    • Recommendation: Use more specific error codes or structured error types instead of string matching

⚡ Performance Considerations

  1. Blocking UI Thread (GiftSheet.kt:30-31)

    • LaunchedEffect(sheet.code, sheet.amount) will re-initialize if these values change
    • Could cause multiple simultaneous claim attempts
    • Recommendation: Add a check to prevent re-initialization if already claiming
  2. StateFlow Buffer Size (GiftViewModel.kt:34, 37)

    • extraBufferCapacity = 1 means events can be dropped if not collected immediately
    • With sheet transitions, this could cause missed navigation events
    • Recommendation: Consider using replay = 1 or ensuring collection starts before events are emitted

🧪 Test Coverage

  1. No Tests for New Functionality

    • BlocktankRepoTest.kt only has 2 lines added (dependency injection)
    • No tests for:
      • claimGiftCode()
      • claimGiftCodeWithLiquidity()
      • claimGiftCodeWithoutLiquidity()
      • GiftViewModel logic
      • UI navigation flows
    • Recommendation: Add comprehensive unit tests for the new repository methods and ViewModel logic
  2. Integration Test Coverage

    • The deep link handling (bitkit://gift-...) isn't covered by tests
    • Recommendation: Add integration tests for the gift code URI scheme handling

📝 Code Quality Suggestions

  1. Magic Numbers (GiftViewModel.kt:144)

    • NODE_STARTUP_TIMEOUT_MS = 30_000L is defined but differs from TIMEOUT_GIFT_CODE = 30.seconds in BlocktankRepo
    • Recommendation: Consolidate timeout constants or document why they differ
  2. Inconsistent Error Handling Pattern (GiftViewModel.kt:115)

    • insertGiftActivity() uses .getOrThrow() which will crash if it fails, but claimGiftCode() returns a Result
    • Recommendation: Handle the Result properly or document why crashing is acceptable here
  3. Missing Documentation

    • The new public API methods lack KDoc comments explaining parameters, return values, and potential exceptions
    • Recommendation: Add documentation for claimGiftCode() and GiftClaimResult
  4. Suppress Annotation Justification (BlocktankRepo.kt:53)

    • The @Suppress("LongParameterList") annotation was added but this is a known issue with DI
    • Recommendation: This is acceptable for constructor injection, but consider if any parameters could be grouped into a configuration object

🎯 Minor Improvements

  1. String Concatenation (GiftViewModel.kt:133)

    • currentError.toString() + (currentError.message ?: "") could include the message twice
    • Recommendation: Use currentError.message ?: currentError.toString() instead
  2. Unused Import (BlocktankRepo.kt:489-490)

    • Consider converting SuccessWithLiquidity to a data object for Kotlin 1.9+ consistency
    • Currently mixing object and data class in the same sealed class hierarchy
  3. Constant Naming (GiftRoute.kt:4)

    • IMAGE_WIDTH_FRACTION is defined at file level but only used in UI files
    • Recommendation: Move to a shared UI constants file or make it private to the UI components

✨ Overall Assessment

This is a solid implementation with good architectural decisions. The main concerns are:

  • Race conditions in initialization (high priority)
  • Missing error handling for payment execution (high priority)
  • Lack of test coverage (high priority)
  • Some security considerations around rate limiting and data storage (medium priority)

The code follows Kotlin best practices and integrates well with the existing codebase. Once the critical issues are addressed, this will be a robust feature.


Review completed by Claude Code

@synonymdev synonymdev deleted a comment from claude bot Nov 24, 2025
@synonymdev synonymdev deleted a comment from claude bot Nov 24, 2025
@synonymdev synonymdev deleted a comment from claude bot Nov 24, 2025
@jvsena42 jvsena42 merged commit b68efb6 into master Nov 24, 2025
18 checks passed
@jvsena42 jvsena42 deleted the feat/gift-codes branch November 24, 2025 19:34
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.

4 participants