Skip to content

Commit 576448e

Browse files
BitcoinQnAclaude
andcommitted
Present both apps as Tether Wallet and document what a clone needs
The Prime app shares no code with WDK — it re-implements transaction parsing in Rust — so naming it after the SDK overstated the relationship. Both apps now present as Tether Wallet. User-visible strings only. Identifiers stay as they are, and the README now explains why: the Prime appId seeds GetAppSeed, so renaming it derives a different wallet and orphans any funded account, and changing the Android package or storage keys discards a user's pairing and history. Documentation gaps that would have blocked a fresh clone: - mobile/.env.example listed four variables that nothing in the codebase reads, and omitted all three that it does. It would have sent someone to obtain a WDK API key and Tron credentials they do not need. - The arm-none-eabi toolchain was never listed as a prerequisite even though the Xous build has always required it, and the firmware script now refuses to run without arm-none-eabi-ar. - Chain-derived history, its Blockscout dependency, and the override were undocumented, as was the fact that a refresh discloses wallet addresses to those indexers. That disclosure is now recorded in docs/SECURITY.md, along with why indexer responses cannot influence signing. - The Bitcoin endpoint silently selects testnet3 or testnet4, since tb1 addresses and the derivation path are identical on both. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
1 parent d0dc1fb commit 576448e

19 files changed

Lines changed: 113 additions & 49 deletions

File tree

README.md

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,20 @@ authoritative review, and signs only after an approval gesture.
1616
> not send mainnet assets, real USD₮, real BTC, or production secrets to this
1717
> software.
1818
19+
## A note on naming
20+
21+
Both applications present to the user as **Tether Wallet**. "WDK" survives
22+
throughout the code as identifiers — the `gui-app-tether-wdk` crate, the
23+
Android package `com.foundationdevices.tetherwdkpassport`, the `Wdk*`
24+
QuantumLink message types, and the stored-preference keys. Those are
25+
deliberately frozen: the Prime `appId` seeds `GetAppSeed`, so renaming it
26+
derives a different wallet and orphans any funded account, and changing the
27+
Android package or storage keys discards a user's pairing and history.
28+
29+
Note also that WDK itself runs only on the phone. The Prime app shares no code
30+
with it and re-implements transaction parsing independently, which is what
31+
makes its review screen a second opinion rather than an echo.
32+
1933
## Compatibility
2034

2135
The current integration is pinned, built, and tested against:
@@ -82,6 +96,12 @@ and local environment files are intentionally ignored.
8296
- Android SDK 36, NDK 27, and an arm64 Android device for BLE testing
8397
- Rust plus `aarch64-linux-android`
8498
- `cargo-ndk` and the UniFFI tooling used by the native module
99+
- The `arm-none-eabi` GNU toolchain on `PATH`, for Prime firmware builds.
100+
KeyOS pins `CC` for the Xous target but not `AR`, so
101+
`scripts/build_prime_firmware.sh` pins `AR_armv7a_unknown_xous_elf` itself
102+
and refuses to run without `arm-none-eabi-ar`. Without that pin the host
103+
archiver produces an empty micro-ecc archive and the recovery image fails to
104+
link on undefined `uECC_*` symbols.
85105

86106
KeyOS carries its own pinned Rust toolchain, which Rustup selects when commands
87107
run from inside its checkout.
@@ -175,7 +195,20 @@ Never commit that file.
175195

176196
The companion uses `https://mempool.space/testnet/api` for Bitcoin Testnet by
177197
default. Override it with `EXPO_PUBLIC_BITCOIN_TESTNET_API` when using another
178-
Esplora-compatible testnet service.
198+
Esplora-compatible testnet service. That endpoint selects the chain: `tb1`
199+
addresses and the `m/84'/1'/0'/0/0` path are identical on testnet3 and
200+
testnet4, so pointing it at a testnet4 host switches chains silently and
201+
testnet3 coins stop appearing.
202+
203+
Transaction history is read from public indexers rather than accumulated
204+
locally, so receives appear at all. Sepolia history comes from
205+
`https://eth-sepolia.blockscout.com`, overridable with
206+
`EXPO_PUBLIC_SEPOLIA_EXPLORER_API`, and Bitcoin history from the same Esplora
207+
endpoint as balances. A refresh discloses the wallet addresses to those
208+
services. Token history is restricted to the allowlisted test USD₮ contract so
209+
a look-alike token cannot write entries into the list.
210+
211+
All overrides live in `mobile/.env.example`.
179212

180213
## Build the Passport Prime app
181214

@@ -237,7 +270,7 @@ a distributable or hardware-test image.
237270
## Run the POC
238271

239272
1. Install the Android APK on an arm64 handset.
240-
2. Install the developer-signed Tether WDK app or complete developer firmware
273+
2. Install the developer-signed Tether Wallet app or complete developer firmware
241274
on a Passport Prime running KeyOS 1.3.0.
242275
3. Open Tether Wallet on Passport Prime and complete its wallet onboarding.
243276
4. In the Android app, choose Passport, grant camera/Bluetooth permissions, and

docs/ARCHITECTURE.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ flowchart LR
1212
BP --> RN
1313
RN --> BLE["BLE Nordic UART"]
1414
BLE --> QS["KeyOS QuantumLink service"]
15-
QS --> PA["Tether WDK Prime app"]
15+
QS --> PA["Tether Wallet Prime app"]
1616
PA --> KD["KeyOS per-app seed + BIP32"]
1717
PA --> RV["Policy parser + device review"]
1818
EVM --> RPC["Sepolia JSON-RPC"]

docs/DEMO_RUNBOOK.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,8 @@ adb install -r artifacts/tether-wdk-passport-arm64-poc.apk
4141

4242
## Pair
4343

44-
1. Open **Tether WDK** on Passport Prime and unlock the device if asked.
45-
2. Open **Tether WDK for Passport** on Android.
44+
1. Open **Tether Wallet** on Passport Prime and unlock the device if asked.
45+
2. Open **Tether Wallet** on Android.
4646
3. Tap **Pair Passport Prime** once. Accept the camera permission first, then
4747
the nearby-device permission. The scanner opens automatically after both
4848
dialogs settle.

docs/SECURITY.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,22 @@ zeroizing storage; derived BIP32 private fields and the signing key are erased
5959
after use. The phone identity is encrypted with an AES-GCM key held by Android
6060
Keystore. Public pairing metadata is stored separately.
6161

62+
## Network disclosure
63+
64+
The phone reaches three public testnet services: a Sepolia JSON-RPC, an Esplora
65+
Bitcoin Testnet API, and a Blockscout instance for Sepolia history. Balance and
66+
history refreshes disclose the wallet's EVM and Bitcoin addresses to whichever
67+
hosts are configured, which links those addresses to the device's IP. That is
68+
acceptable for a testnet POC and is not acceptable for production; a real
69+
deployment needs self-hosted or privacy-preserving indexers.
70+
71+
Indexer responses are untrusted display data. They never influence signing:
72+
Passport Prime derives its own addresses and re-parses every transaction it is
73+
asked to sign, so a hostile or compromised indexer can mislead the history
74+
screen but cannot cause a transfer. Token history is filtered to the
75+
allowlisted test USD₮ contract, because any address can deploy a token that
76+
claims to be USD₮ and airdrop it to produce convincing entries.
77+
6278
## Dependency posture
6379

6480
Package versions are locked. As validated on 24 July 2026,

mobile/.env.example

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,16 @@
1-
EXPO_PUBLIC_WDK_INDEXER_BASE_URL=https://wdk-api.tether.io
2-
EXPO_PUBLIC_WDK_INDEXER_API_KEY=PUT_WDK_API_KEY_HERE
3-
EXPO_PUBLIC_TRON_API_KEY=PUT_HERE
4-
EXPO_PUBLIC_TRON_API_SECRET=PUT_HERE
1+
# Every value below is optional and testnet-only. The app runs without this
2+
# file; copy it to .env only to point at different public endpoints.
3+
# Never commit .env.
4+
5+
# Ethereum Sepolia JSON-RPC used for balances, gas, and broadcast.
6+
EXPO_PUBLIC_SEPOLIA_RPC_URL=https://ethereum-sepolia-rpc.publicnode.com
7+
8+
# Esplora-compatible Bitcoin Testnet API. Must be testnet3: the app derives
9+
# m/84'/1'/0'/0/0 and tb1 addresses encode identically on testnet3 and
10+
# testnet4, so pointing this at a testnet4 host silently switches chains.
11+
EXPO_PUBLIC_BITCOIN_TESTNET_API=https://mempool.space/testnet/api
12+
13+
# Blockscout instance used to read Sepolia transaction history. Receives never
14+
# reach the phone, so history is read back from an indexer; a refresh discloses
15+
# the wallet address to this host.
16+
EXPO_PUBLIC_SEPOLIA_EXPLORER_API=https://eth-sepolia.blockscout.com

mobile/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Tether WDK for Passport Android app
1+
# Tether Wallet Android companion
22

33
This Expo/React Native application is a focused fork of Tether's WDK starter. It
44
demonstrates WDK through a Tether-branded Ethereum Sepolia and Bitcoin Testnet

mobile/app.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"expo": {
3-
"name": "Tether WDK for Passport",
3+
"name": "Tether Wallet",
44
"slug": "tether-wdk-passport",
55
"version": "1.0.0",
66
"orientation": "portrait",

mobile/modules/quantumlink/android/src/main/java/com/foundationdevices/passport/wdk/quantumlink/PassportQuantumLinkModule.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -420,14 +420,14 @@ class PassportQuantumLinkModule : Module() {
420420
"pair_stale_peer",
421421
"Passport Prime did not answer. This phone was paired to a different " +
422422
"Passport identity, and Passport may still hold that connection. " +
423-
"Remove the Tether WDK connection in Passport's settings, then scan again.",
423+
"Remove the Tether Wallet connection in Passport's settings, then scan again.",
424424
null
425425
)
426426
} else {
427427
promise.reject(
428428
"pair_timeout",
429429
"Passport Prime did not answer the pairing request. If Passport already " +
430-
"lists a Tether WDK connection, remove it in Passport's settings and " +
430+
"lists a Tether Wallet connection, remove it in Passport's settings and " +
431431
"scan again.",
432432
null
433433
)

mobile/src/app/onboarding/index.tsx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,7 @@ export default function PassportOnboardingScreen() {
207207
: 'Scan secure identity';
208208
const instruction =
209209
phase === 'static'
210-
? 'Scan the first QR code shown by the Tether WDK app on Passport.'
210+
? 'Scan the first QR code shown by the Tether Wallet app on Passport.'
211211
: phase === 'connecting'
212212
? 'Keep both devices nearby while Bluetooth connects.'
213213
: 'Passport now shows an animated identity code. Hold steady while all frames are read.';
@@ -232,11 +232,11 @@ export default function PassportOnboardingScreen() {
232232
style={styles.heroMark}
233233
/>
234234
</View>
235-
<Text style={styles.eyebrow}>WDK HARDWARE WALLET</Text>
235+
<Text style={styles.eyebrow}>PASSPORT HARDWARE WALLET</Text>
236236
<Text style={styles.heading}>Your keys stay on Passport Prime.</Text>
237237
<Text style={styles.body}>
238-
Pair over encrypted Bluetooth with QuantumLink, then use Tether WDK to view and transfer
239-
Sepolia ETH and USD₮. Every transaction is reviewed and approved on Passport.
238+
Pair over encrypted Bluetooth with QuantumLink, then use Tether Wallet to view and
239+
transfer Sepolia ETH and USD₮. Every transaction is reviewed and approved on Passport.
240240
</Text>
241241
<View style={styles.featureCard}>
242242
<Feature
@@ -338,7 +338,7 @@ export default function PassportOnboardingScreen() {
338338
</Text>
339339
<Text style={styles.body}>
340340
{phase === 'complete'
341-
? `${deviceName} is ready to authorize Tether WDK transactions.`
341+
? `${deviceName} is ready to authorize Tether Wallet transactions.`
342342
: 'Exchanging encrypted QuantumLink identities with Passport Prime.'}
343343
</Text>
344344
</View>

mobile/src/app/send/details.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -285,7 +285,7 @@ export default function SendScreen() {
285285
<Text style={styles.modalSubtitle}>
286286
{stage === 'signing'
287287
? 'Compare the details below with Passport Prime, then slide to approve on the device.'
288-
: `WDK prepared this ${quote?.asset === 'BTC' ? 'Bitcoin testnet' : 'Sepolia'} transfer. Passport will independently reconstruct and review it.`}
288+
: `Your phone prepared this ${quote?.asset === 'BTC' ? 'Bitcoin testnet' : 'Sepolia'} transfer. Passport will independently reconstruct and review it.`}
289289
</Text>
290290
<View style={styles.reviewCard}>
291291
<ReviewRow label="Asset" value={quote?.asset ?? ''} />
@@ -357,7 +357,7 @@ function ResultView({ result, onDone }: { result: PassportActivity; onDone(): vo
357357
</Text>
358358
<Text style={styles.modalTitle}>Transfer submitted</Text>
359359
<Text style={styles.modalSubtitle}>
360-
Passport Prime approved the transaction and Tether WDK broadcast the signed payload.
360+
Passport Prime approved the transaction and Tether Wallet broadcast the signed payload.
361361
</Text>
362362
<View style={styles.hashCard}>
363363
<Text style={styles.reviewLabel}>TRANSACTION HASH</Text>

0 commit comments

Comments
 (0)