Skip to content

fix: copy and share USDT receiving addresses - #1558

Merged
ovitrif merged 2 commits into
masterfrom
fix/usdt-receive-sharing
Oct 17, 2026
Merged

ovitrif merged 2 commits into
masterfrom
fix/usdt-receive-sharing

Conversation

@ben-kaufman

Copy link
Copy Markdown
Contributor

Fixes #1556

This PR makes USDT receive copying and sharing work with exchanges and plain address fields.

Description

  • Copy below the QR and tapping the QR copy the plain receiving address, including the selected network’s bridge deposit address.
  • Share from the QR or address details includes the address, explicit network, optional requested USDT amount and the payment QR image.
  • Reuse the existing QR generator and receive components so the QR still carries the token, network and exact amount.

Out of Scope

  • Bitcoin/Lightning receive behavior, internal Paykit routing and Earn/Locks authorization remain unchanged.

Design

USDT receive flow. Existing layout and styling are unchanged; copy/share behavior follows #1556 and the agreed receive-sharing requirements.

Preview

Copy: 0x…

Shared text:

Network: Arbitrum One
0x…
3.5 USDT

The attached QR retains the payment URI and uint256=3500000.

QA Notes

Journeys

  • updated usdt-receive-amount-and-units.xml — plain-address copying, exact requested amount, network label and payment QR sharing from both views.
  • updated usdt-wallet-navigation.xml — the receive QR copies its address while retaining the payment URI for scanning.

Manual Tests

  • With an enabled bridge provider, Receive → select Polygon → prepare a deposit → copy and share from both views → all actions use the Polygon deposit address and requested amount, and the QR retains its original URI; switch back to Arbitrum and repeat — a live bridge provider is absent from the standard journey environment.

Automated Checks

  • updated UsdtReceivePrivacyTest.kt — checks QR taps, both Copy buttons, both Share actions and decodes the actual shared image while changing amounts and selected receive addresses.
  • ran UsdtReceivePrivacyTest.kt on an API 36 emulator — both tests passed, including direct/Polygon/direct transitions and exact six-decimal amounts.

@greptile-apps

greptile-apps Bot commented Oct 17, 2026 •

Copy link
Copy Markdown
Contributor

RetriggerView in GreptileConfidence Score: 4/5

[Medium impact] The changes reach the USDT receive and Android sharing flow.

The PR appears safe to merge, with a non-blocking timing issue in the new share test.

Findings

  1. P2 Share test races image creation ▶

Summary

USDT receiving now copies plain addresses while keeping payment details in the QR.

  • USDT receive copying and sharing use the plain address and payment QR.

Diagram

%%{init: {'theme': 'neutral'}}%%
flowchart LR
    A[Selected receive address and amount] --> B[USDT receive view]
    B --> C[Copy plain address]
    B --> D[Share network, address and amount]
    A --> E[Payment URI]
    E --> F[QR image]
    F --> D
Loading

Reviews (1) · Last reviewed commit: "fix: copy and share usdt receiving addre..." · Reviewed by Greptile

Comment thread app/src/androidTest/java/to/bitkit/ui/components/UsdtReceivePrivacyTest.kt Outdated

@talosmachina talosmachina left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

No findings. On the USDT receive screen, Copy and a tap on the QR now copy the plain receiving address (the selected network's deposit address included). Share, from the QR and from Show Details, sends the network, the address, the optional requested amount and a QR image that still encodes the payment URI. Reviewed aba0c43, full tier, reasoned from the code and CI (the branch was not built on this reviewer's host).

What I checked, and 5 candidates I ruled out

Read in full: UsdtReceiveContent.kt, UsdtDepositReceiveScreen.kt, UsdtReceiveAmountContent.kt, the ReceiveQrView and CopyAddressCard parts of ReceiveQrScreen.kt, rememberQrBitmap in QrCodeImage.kt, ShareSheet.kt, UsdtReceivePrivacyTest.kt
Call sites traced: CopyAddressCard (the five ReceiveQrScreen.kt callers pass no shareQrContent, so Share stays enabled and text-only for them), UsdtReceiveContent (one caller)
CI: detect-changes and e2e-branch determine green; build, build-local and lint still running at review time on this head (lint, detekt and build-local were green on the previous head 98bda61)

Ruled out

  • Deposit share amount disagrees with the deposit QR: the text uses usdtFormatAmount(state.address.amount), and bitkit-core's receive rejects a response whose amount differs from the request, so it matches the uint256 in state.address.uri.
  • Trailing-dot amount reaching the text (3. USDT): Continue is gated on PaykitAmount.parse, whose regex refuses a trailing separator, so such an amount never becomes amount.
  • Details Share firing before the QR bitmap exists and sending text only: the button is disabled until shareBitmap is non-null whenever shareQrContent is set.
  • Stale bitmap after a network or amount change: rememberQrBitmap is keyed on content and resets to null on change, which disables Share until the new image is ready.
  • Tap on the QR still copies the URI: ReceiveQrView passes copyContent = copyText to the QR, now address; the test asserts the clipboard after both the QR tap and ReceiveCopyQR.

Merge confidence: 4/5, the build and lint jobs had not finished on this head at review time and the change was reasoned, not run.

@github-actions

Copy link
Copy Markdown
Contributor

Regtest APK

Built from aba0c43 (run).

Download bitkit-dev-debug universal APK (expires in 30 days).

@ovi-reviewer ovi-reviewer Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Verdict: ✅ Approve. I found no defects, and all three cases passed on device at aba0c43.

Findings: none.

QA for aba0c43: an Android 16 emulator (Pixel 7) on a local USDT stack (Arbitrum fork, bundler and a mock bridge provider).

  • I read each copy back from Android's clipboard editor, which opens from the preview the system shows after a new copy.
  • What a QR encodes comes from its content description on screen.
  • I checked each Share from the chooser's text preview, and by decoding the PNG it attached.
Test 1: Arbitrum receive with 3.5 USDT ✅ Copy, a QR tap and Details Copy hold only the address. Both Shares send the network, the address, 3.5 USDT and a QR of the payment URI.
  • The QR encodes ethereum:0xFd08…@42161/transfer?address=0xB862…d1B7&uint256=3500000.
  • Each of these put only 0xB8625FaAC7a8C387286cef42c7534074c4E5d1B7 on the clipboard:
    • Copy.
    • A tap on the QR.
    • Copy in Show Details.
  • Share on the QR and Share in Show Details each sent an image with "Network: Arbitrum One", the address and "3.5 USDT". Both images decode to the payment URI with uint256=3500000.

test1-arbitrum-3.5-usdt.2x.mp4

Test 2: Arbitrum receive without an amount, and Back ✅ Every copy holds the address while the QR keeps the full URI, and both Backs behave as the journey says.
  • Show Details shows 0xB862…d1B7, and its Copy holds only that address.
  • System Back from Show Details returns to the QR.
    • Its Copy and a tap on it hold the same address.
    • The QR encodes the full URI, with the Arbitrum USDT token and chain 42161.
  • Share sends "Network: Arbitrum One" and the address with no amount line. The image decodes to the same URI.
  • Back from the QR closes Receive onto the USDT wallet without switching to Savings.

test2-arbitrum-no-amount.2x.mp4

Test 3: Polygon deposit of 3.5 USDT, then back to Arbitrum ✅ Copies and Shares use the Polygon deposit address. After the switch back, they use the wallet address with no leftover amount.
  • After the deposit was prepared, the QR encodes ethereum:0xc213…8e8f@137/transfer?address=0x3e91…575e.
    • The deposit QR carries no uint256, so the amount appears in the share text only.
  • Copy, a QR tap and Details Copy each hold 0x3e91771f24Bc9B1AbecdD8c128cdD99e912a575e, not the wallet address.
  • Both Shares sent "Network: Polygon", the deposit address and "3.5 USDT". Both images decode to the Polygon URI.
  • Back on Arbitrum One:
    • Every copy holds 0xB862…d1B7.
    • Both Shares send "Network: Arbitrum One" and the address, with no amount.
    • Both images decode to the Arbitrum URI.

test3-polygon-then-arbitrum.2x.mp4

Scope: a full review of 722c824..aba0c43 (9 files). The cases drove every changed app path. The other ReceiveQrView and CopyAddressCard callers (Bitcoin, Lightning, hardware wallet) keep shareQrContent = null; I checked this in the code.

Pair PR synonymdev/bitkit-ios#997 at 1294b7e makes the same change, so it matches:

  • The QR tap and Copy copy the plain address.
  • Share sends the same text.
  • Both Shares attach a QR image.

@jvsena42 jvsena42 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Approving at aba0c437f with one LOW open inline. Fix it here or in a follow-up issue, your choice; reply on the thread saying which, with the issue link if it is a follow-up.

Checked and clean:

  • Copy, a QR tap and Details Copy all copy state.address?.address ?: wallet.address, the address shown in Details, and the QR encodes state.address?.uri ?: uri from the same source. UsdtRepo resets the state with an empty receiveUri on an address change, so the two cannot drift.
  • Changing network clears amount/draftAmount and nulls the address. Edit on a deposit re-selects the network, and Back lands on Arbitrum with no amount.
  • Details Share stays disabled until rememberQrBitmap (keyed on content) has the image.
  • Bitcoin, Lightning and hardware-wallet CopyAddressCard callers pass no shareQrContent, so their Share stays enabled and text-only. ReceiveQrView is unchanged.
  • The journeys and docs/usdt-qa.md match the new behaviour and are byte-identical to synonymdev/bitkit-ios#997.
  • greptile's QR-share wait in UsdtReceivePrivacyTest holds: fetchSemanticsNodes() idles through the 200 ms Crossfade, so the fading image is never matched. 15 consecutive runs on an emulator passed, and a probe that blocked QR encoding never matched early.

CI is green at this head.

Device gate: aba0c43 — Savings receive on the CI APK (bitkit-dev-debug-190-universal.apk), new wallet, with and without a 5,000-sat amount: QR Copy and a QR tap copy the address, or the BIP21 when it carries an amount; QR Share attaches the image with the same text; Details Copy copies the BIP21 without lightning, as master does; Details Share is text-only and enabled. All passed. USDT 3.50 share: see the LOW.
Skipped — already driven this round: Arbitrum receive with and without an amount, Show Details Copy and Share, and the Polygon deposit and return to Arbitrum. These are the changed actions of usdt-receive-amount-and-units.xml and usdt-wallet-navigation.xml (Android 16, by ovi-reviewer at aba0c43, passed: #1558 (review)).
Skipped — covered by CI at aba0c43: unit tests, detekt, lint, local e2e (all green).
perf: not relevant; l10n: no UI text changed (the share text reuses usdt__receive_network).

@ovitrif
ovitrif merged commit eff7e1a into master Oct 17, 2026
22 checks passed
@ovitrif
ovitrif deleted the fix/usdt-receive-sharing branch October 17, 2026 14:06
@ovi-reviewer ovi-reviewer Bot mentioned this pull request Oct 19, 2026
3 of 5 tasks
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.

bug: usdt receive copy puts a payment link on the clipboard instead of the address

4 participants